1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
6 using System.Collections.Generic;
9 using System.Threading;
11 namespace Mono.Profiler.Log {
13 public sealed class LogProcessor {
15 public LogReader Reader { get; }
17 public LogEventVisitor ImmediateVisitor { get; }
19 public LogEventVisitor SortedVisitor { get; }
21 public LogStreamHeader StreamHeader { get; private set; }
23 LogBufferHeader _bufferHeader;
29 public LogProcessor (LogReader reader, LogEventVisitor immediateVisitor, LogEventVisitor sortedVisitor)
32 throw new ArgumentNullException (nameof (reader));
35 ImmediateVisitor = immediateVisitor;
36 SortedVisitor = sortedVisitor;
39 public void Process ()
41 Process (CancellationToken.None);
44 static void ProcessEvent (LogEventVisitor visitor, LogEvent ev)
46 if (visitor != null) {
47 visitor.VisitBefore (ev);
49 visitor.VisitAfter (ev);
53 void ProcessEvents (List<LogEvent> events, CancellationToken token)
55 foreach (var ev in events.OrderBy (x => x.Timestamp)) {
56 token.ThrowIfCancellationRequested ();
57 ProcessEvent (SortedVisitor, ev);
63 public void Process (CancellationToken token)
66 throw new InvalidOperationException ("This log processor cannot be reused.");
69 StreamHeader = new LogStreamHeader (Reader);
71 var events = new List<LogEvent> (Environment.ProcessorCount * 1000);
73 while (!Reader.BaseStream.EndOfStream) {
74 token.ThrowIfCancellationRequested ();
76 _bufferHeader = new LogBufferHeader (StreamHeader, Reader);
78 // Use the manual position tracking in LogReader so we're
79 // compatible with non-seekable streams.
80 var goal = Reader.Position + _bufferHeader.Length;
82 while (Reader.Position < goal) {
83 token.ThrowIfCancellationRequested ();
85 var ev = ReadEvent ();
87 ProcessEvent (ImmediateVisitor, ev);
90 if (ev is SynchronizationPointEvent)
91 ProcessEvents (events, token);
95 ProcessEvents (events, token);
100 var type = Reader.ReadByte ();
101 var basicType = (LogEventType) (type & 0xf);
102 var extType = (LogEventType) (type & 0xf0);
108 case LogEventType.Allocation:
110 case LogEventType.AllocationBacktrace:
111 case LogEventType.AllocationNoBacktrace:
112 ev = new AllocationEvent {
113 ClassPointer = ReadPointer (),
114 ObjectPointer = ReadObject (),
115 ObjectSize = (long) Reader.ReadULeb128 (),
116 Backtrace = ReadBacktrace (extType == LogEventType.AllocationBacktrace),
120 throw new LogException ($"Invalid extended event type ({extType}).");
123 case LogEventType.GC:
125 case LogEventType.GCEvent:
127 Type = (LogGCEvent) Reader.ReadByte (),
128 Generation = Reader.ReadByte (),
131 case LogEventType.GCResize:
132 ev = new GCResizeEvent {
133 NewSize = (long) Reader.ReadULeb128 (),
136 case LogEventType.GCMove: {
137 var list = new long [(int) Reader.ReadULeb128 ()];
139 for (var i = 0; i < list.Length; i++)
140 list [i] = ReadObject ();
142 ev = new GCMoveEvent {
143 OldObjectPointers = list.Where ((_, i) => i % 2 == 0).ToArray (),
144 NewObjectPointers = list.Where ((_, i) => i % 2 != 0).ToArray (),
148 case LogEventType.GCHandleCreationNoBacktrace:
149 case LogEventType.GCHandleCreationBacktrace:
150 ev = new GCHandleCreationEvent {
151 Type = (LogGCHandleType) Reader.ReadULeb128 (),
152 Handle = (long) Reader.ReadULeb128 (),
153 ObjectPointer = ReadObject (),
154 Backtrace = ReadBacktrace (extType == LogEventType.GCHandleCreationBacktrace),
157 case LogEventType.GCHandleDeletionNoBacktrace:
158 case LogEventType.GCHandleDeletionBacktrace:
159 ev = new GCHandleDeletionEvent {
160 Type = (LogGCHandleType) Reader.ReadULeb128 (),
161 Handle = (long) Reader.ReadULeb128 (),
162 Backtrace = ReadBacktrace (extType == LogEventType.GCHandleDeletionBacktrace),
165 case LogEventType.GCFinalizeBegin:
166 ev = new GCFinalizeBeginEvent ();
168 case LogEventType.GCFinalizeEnd:
169 ev = new GCFinalizeEndEvent ();
171 case LogEventType.GCFinalizeObjectBegin:
172 ev = new GCFinalizeObjectBeginEvent {
173 ObjectPointer = ReadObject (),
176 case LogEventType.GCFinalizeObjectEnd:
177 ev = new GCFinalizeObjectEndEvent {
178 ObjectPointer = ReadObject (),
182 throw new LogException ($"Invalid extended event type ({extType}).");
185 case LogEventType.Metadata: {
190 case LogEventType.MetadataExtra:
192 case LogEventType.MetadataEndLoad:
195 case LogEventType.MetadataEndUnload:
199 throw new LogException ($"Invalid extended event type ({extType}).");
202 var metadataType = (LogMetadataType) Reader.ReadByte ();
204 switch (metadataType) {
205 case LogMetadataType.Class:
207 ev = new ClassLoadEvent {
208 ClassPointer = ReadPointer (),
209 ImagePointer = ReadPointer (),
210 Name = Reader.ReadCString (),
213 throw new LogException ("Invalid class metadata event.");
215 case LogMetadataType.Image:
217 ev = new ImageLoadEvent {
218 ImagePointer = ReadPointer (),
219 Name = Reader.ReadCString (),
222 ev = new ImageUnloadEvent {
223 ImagePointer = ReadPointer (),
224 Name = Reader.ReadCString (),
227 throw new LogException ("Invalid image metadata event.");
229 case LogMetadataType.Assembly:
231 ev = new AssemblyLoadEvent {
232 AssemblyPointer = ReadPointer (),
233 ImagePointer = StreamHeader.FormatVersion >= 14 ? ReadPointer () : 0,
234 Name = Reader.ReadCString (),
237 ev = new AssemblyUnloadEvent {
238 AssemblyPointer = ReadPointer (),
239 ImagePointer = StreamHeader.FormatVersion >= 14 ? ReadPointer () : 0,
240 Name = Reader.ReadCString (),
243 throw new LogException ("Invalid assembly metadata event.");
245 case LogMetadataType.AppDomain:
247 ev = new AppDomainLoadEvent {
248 AppDomainId = ReadPointer (),
251 ev = new AppDomainUnloadEvent {
252 AppDomainId = ReadPointer (),
255 ev = new AppDomainNameEvent {
256 AppDomainId = ReadPointer (),
257 Name = Reader.ReadCString (),
261 case LogMetadataType.Thread:
263 ev = new ThreadStartEvent {
264 ThreadId = ReadPointer (),
267 ev = new ThreadEndEvent {
268 ThreadId = ReadPointer (),
271 ev = new ThreadNameEvent {
272 ThreadId = ReadPointer (),
273 Name = Reader.ReadCString (),
277 case LogMetadataType.Context:
279 ev = new ContextLoadEvent {
280 ContextId = ReadPointer (),
281 AppDomainId = ReadPointer (),
284 ev = new ContextUnloadEvent {
285 ContextId = ReadPointer (),
286 AppDomainId = ReadPointer (),
289 throw new LogException ("Invalid context metadata event.");
292 throw new LogException ($"Invalid metadata type ({metadataType}).");
296 case LogEventType.Method:
298 case LogEventType.MethodLeave:
299 ev = new LeaveEvent {
300 MethodPointer = ReadMethod (),
303 case LogEventType.MethodEnter:
304 ev = new EnterEvent {
305 MethodPointer = ReadMethod (),
308 case LogEventType.MethodLeaveExceptional:
309 ev = new ExceptionalLeaveEvent {
310 MethodPointer = ReadMethod (),
313 case LogEventType.MethodJit:
315 MethodPointer = ReadMethod (),
316 CodePointer = ReadPointer (),
317 CodeSize = (long) Reader.ReadULeb128 (),
318 Name = Reader.ReadCString (),
322 throw new LogException ($"Invalid extended event type ({extType}).");
325 case LogEventType.Exception:
327 case LogEventType.ExceptionThrowNoBacktrace:
328 case LogEventType.ExceptionThrowBacktrace:
329 ev = new ThrowEvent {
330 ObjectPointer = ReadObject (),
331 Backtrace = ReadBacktrace (extType == LogEventType.ExceptionThrowBacktrace),
334 case LogEventType.ExceptionClause:
335 ev = new ExceptionClauseEvent {
336 Type = (LogExceptionClause) Reader.ReadByte (),
337 Index = (long) Reader.ReadULeb128 (),
338 MethodPointer = ReadMethod (),
339 ObjectPointer = StreamHeader.FormatVersion >= 14 ? ReadObject () : 0,
343 throw new LogException ($"Invalid extended event type ({extType}).");
346 case LogEventType.Monitor:
347 if (StreamHeader.FormatVersion < 14) {
348 if (extType.HasFlag (LogEventType.MonitorBacktrace)) {
349 extType = LogEventType.MonitorBacktrace;
351 extType = LogEventType.MonitorNoBacktrace;
355 case LogEventType.MonitorNoBacktrace:
356 case LogEventType.MonitorBacktrace:
357 ev = new MonitorEvent {
358 Event = StreamHeader.FormatVersion >= 14 ?
359 (LogMonitorEvent) Reader.ReadByte () :
360 (LogMonitorEvent) ((((byte) type & 0xf0) >> 4) & 0x3),
361 ObjectPointer = ReadObject (),
362 Backtrace = ReadBacktrace (extType == LogEventType.MonitorBacktrace),
366 throw new LogException ($"Invalid extended event type ({extType}).");
369 case LogEventType.Heap:
371 case LogEventType.HeapBegin:
372 ev = new HeapBeginEvent ();
374 case LogEventType.HeapEnd:
375 ev = new HeapEndEvent ();
377 case LogEventType.HeapObject: {
378 HeapObjectEvent hoe = new HeapObjectEvent {
379 ObjectPointer = ReadObject (),
380 ClassPointer = ReadPointer (),
381 ObjectSize = (long) Reader.ReadULeb128 (),
384 var list = new HeapObjectEvent.HeapObjectReference [(int) Reader.ReadULeb128 ()];
386 for (var i = 0; i < list.Length; i++) {
387 list [i] = new HeapObjectEvent.HeapObjectReference {
388 Offset = (long) Reader.ReadULeb128 (),
389 ObjectPointer = ReadObject (),
393 hoe.References = list;
399 case LogEventType.HeapRoots: {
400 // TODO: This entire event makes no sense.
401 var hre = new HeapRootsEvent ();
402 var list = new HeapRootsEvent.HeapRoot [(int) Reader.ReadULeb128 ()];
404 hre.MaxGenerationCollectionCount = (long) Reader.ReadULeb128 ();
406 for (var i = 0; i < list.Length; i++) {
407 list [i] = new HeapRootsEvent.HeapRoot {
408 ObjectPointer = ReadObject (),
409 Attributes = StreamHeader.FormatVersion == 13 ? (LogHeapRootAttributes) Reader.ReadByte () : (LogHeapRootAttributes) Reader.ReadULeb128 (),
410 ExtraInfo = (long) Reader.ReadULeb128 (),
420 throw new LogException ($"Invalid extended event type ({extType}).");
423 case LogEventType.Sample:
425 case LogEventType.SampleHit:
426 if (StreamHeader.FormatVersion < 14) {
427 // Read SampleType (always set to .Cycles) for versions < 14
430 ev = new SampleHitEvent {
431 ThreadId = ReadPointer (),
432 UnmanagedBacktrace = ReadBacktrace (true, false), // FIXME: isn't this reversed?
433 ManagedBacktrace = ReadBacktrace (true),
436 case LogEventType.SampleUnmanagedSymbol:
437 ev = new UnmanagedSymbolEvent {
438 CodePointer = ReadPointer (),
439 CodeSize = (long) Reader.ReadULeb128 (),
440 Name = Reader.ReadCString (),
443 case LogEventType.SampleUnmanagedBinary:
444 ev = new UnmanagedBinaryEvent {
445 SegmentPointer = StreamHeader.FormatVersion >= 14 ? ReadPointer () : Reader.ReadSLeb128 (),
446 SegmentOffset = (long) Reader.ReadULeb128 (),
447 SegmentSize = (long) Reader.ReadULeb128 (),
448 FileName = Reader.ReadCString (),
451 case LogEventType.SampleCounterDescriptions: {
452 var cde = new CounterDescriptionsEvent ();
453 var list = new CounterDescriptionsEvent.CounterDescription [(int) Reader.ReadULeb128 ()];
455 for (var i = 0; i < list.Length; i++) {
456 var section = (LogCounterSection) Reader.ReadULeb128 ();
458 list [i] = new CounterDescriptionsEvent.CounterDescription {
460 SectionName = section == LogCounterSection.User ? Reader.ReadCString () : string.Empty,
461 CounterName = Reader.ReadCString (),
462 Type = (LogCounterType) Reader.ReadByte (),
463 Unit = (LogCounterUnit) Reader.ReadByte (),
464 Variance = (LogCounterVariance) Reader.ReadByte (),
465 Index = (long) Reader.ReadULeb128 (),
469 cde.Descriptions = list;
474 case LogEventType.SampleCounters: {
475 var cse = new CounterSamplesEvent ();
476 var list = new List<CounterSamplesEvent.CounterSample> ();
479 var index = (long) Reader.ReadULeb128 ();
484 var counterType = (LogCounterType) Reader.ReadByte ();
488 switch (counterType) {
489 case LogCounterType.String:
490 value = Reader.ReadByte () == 1 ? Reader.ReadCString () : null;
492 case LogCounterType.Int32:
493 case LogCounterType.Word:
494 case LogCounterType.Int64:
495 case LogCounterType.Interval:
496 value = Reader.ReadSLeb128 ();
498 case LogCounterType.UInt32:
499 case LogCounterType.UInt64:
500 value = Reader.ReadULeb128 ();
502 case LogCounterType.Double:
503 value = Reader.ReadDouble ();
506 throw new LogException ($"Invalid counter type ({counterType}).");
509 list.Add (new CounterSamplesEvent.CounterSample {
522 throw new LogException ($"Invalid extended event type ({extType}).");
525 case LogEventType.Runtime:
527 case LogEventType.RuntimeJitHelper: {
528 var helperType = (LogJitHelper) Reader.ReadByte ();
530 ev = new JitHelperEvent {
532 BufferPointer = ReadPointer (),
533 BufferSize = (long) Reader.ReadULeb128 (),
534 Name = helperType == LogJitHelper.SpecificTrampoline ? Reader.ReadCString () : string.Empty,
539 throw new LogException ($"Invalid extended event type ({extType}).");
542 case LogEventType.Meta:
544 case LogEventType.MetaSynchronizationPoint:
545 ev = new SynchronizationPointEvent {
546 Type = (LogSynchronizationPoint) Reader.ReadByte (),
550 throw new LogException ($"Invalid extended event type ({extType}).");
554 throw new LogException ($"Invalid basic event type ({basicType}).");
557 ev.Timestamp = _time;
558 ev.Buffer = _bufferHeader;
565 var ptr = Reader.ReadSLeb128 () + _bufferHeader.PointerBase;
567 return StreamHeader.PointerSize == sizeof (long) ? ptr : ptr & 0xffffffffL;
572 return Reader.ReadSLeb128 () + _bufferHeader.ObjectBase << 3;
577 return _bufferHeader.CurrentMethod += Reader.ReadSLeb128 ();
582 return _bufferHeader.CurrentTime += Reader.ReadULeb128 ();
585 IReadOnlyList<long> ReadBacktrace (bool actuallyRead, bool managed = true)
588 return Array.Empty<long> ();
590 var list = new long [(int) Reader.ReadULeb128 ()];
592 for (var i = 0; i < list.Length; i++)
593 list [i] = managed ? ReadMethod () : ReadPointer ();