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);
107 case LogEventType.Allocation:
109 case LogEventType.AllocationBacktrace:
110 case LogEventType.AllocationNoBacktrace:
111 return new AllocationEvent {
112 ClassPointer = ReadPointer (),
113 ObjectPointer = ReadObject (),
114 ObjectSize = (long) Reader.ReadULeb128 (),
115 Backtrace = ReadBacktrace (extType == LogEventType.AllocationBacktrace),
118 throw new LogException ($"Invalid extended event type ({extType}).");
120 case LogEventType.GC:
122 case LogEventType.GCEvent:
124 Type = (LogGCEvent) Reader.ReadByte (),
125 Generation = Reader.ReadByte (),
127 case LogEventType.GCResize:
128 return new GCResizeEvent {
129 NewSize = (long) Reader.ReadULeb128 (),
131 case LogEventType.GCMove: {
132 var list = new long [(int) Reader.ReadULeb128 ()];
134 for (var i = 0; i < list.Length; i++)
135 list [i] = ReadObject ();
137 return new GCMoveEvent {
138 OldObjectPointers = list.Where ((_, i) => i % 2 == 0).ToArray (),
139 NewObjectPointers = list.Where ((_, i) => i % 2 != 0).ToArray (),
142 case LogEventType.GCHandleCreationNoBacktrace:
143 case LogEventType.GCHandleCreationBacktrace:
144 return new GCHandleCreationEvent {
145 Type = (LogGCHandleType) Reader.ReadULeb128 (),
146 Handle = (long) Reader.ReadULeb128 (),
147 ObjectPointer = ReadObject (),
148 Backtrace = ReadBacktrace (extType == LogEventType.GCHandleCreationBacktrace),
150 case LogEventType.GCHandleDeletionNoBacktrace:
151 case LogEventType.GCHandleDeletionBacktrace:
152 return new GCHandleDeletionEvent {
153 Type = (LogGCHandleType) Reader.ReadULeb128 (),
154 Handle = (long) Reader.ReadULeb128 (),
155 Backtrace = ReadBacktrace (extType == LogEventType.GCHandleDeletionBacktrace),
157 case LogEventType.GCFinalizeBegin:
158 return new GCFinalizeBeginEvent ();
159 case LogEventType.GCFinalizeEnd:
160 return new GCFinalizeEndEvent ();
161 case LogEventType.GCFinalizeObjectBegin:
162 return new GCFinalizeObjectBeginEvent {
163 ObjectPointer = ReadObject (),
165 case LogEventType.GCFinalizeObjectEnd:
166 return new GCFinalizeObjectEndEvent {
167 ObjectPointer = ReadObject (),
170 throw new LogException ($"Invalid extended event type ({extType}).");
172 case LogEventType.Metadata: {
177 case LogEventType.MetadataExtra:
179 case LogEventType.MetadataEndLoad:
182 case LogEventType.MetadataEndUnload:
186 throw new LogException ($"Invalid extended event type ({extType}).");
189 var metadataType = (LogMetadataType) Reader.ReadByte ();
191 switch (metadataType) {
192 case LogMetadataType.Class:
194 return new ClassLoadEvent {
195 ClassPointer = ReadPointer (),
196 ImagePointer = ReadPointer (),
197 Name = Reader.ReadCString (),
200 throw new LogException ("Invalid class metadata event.");
201 case LogMetadataType.Image:
203 return new ImageLoadEvent {
204 ImagePointer = ReadPointer (),
205 Name = Reader.ReadCString (),
208 return new ImageUnloadEvent {
209 ImagePointer = ReadPointer (),
210 Name = Reader.ReadCString (),
213 throw new LogException ("Invalid image metadata event.");
214 case LogMetadataType.Assembly:
216 return new AssemblyLoadEvent {
217 AssemblyPointer = ReadPointer (),
218 ImagePointer = ReadPointer (),
219 Name = Reader.ReadCString (),
222 return new AssemblyUnloadEvent {
223 AssemblyPointer = ReadPointer (),
224 ImagePointer = ReadPointer (),
225 Name = Reader.ReadCString (),
228 throw new LogException ("Invalid assembly metadata event.");
229 case LogMetadataType.AppDomain:
231 return new AppDomainLoadEvent {
232 AppDomainId = ReadPointer (),
235 return new AppDomainUnloadEvent {
236 AppDomainId = ReadPointer (),
239 return new AppDomainNameEvent {
240 AppDomainId = ReadPointer (),
241 Name = Reader.ReadCString (),
244 case LogMetadataType.Thread:
246 return new ThreadStartEvent {
247 ThreadId = ReadPointer (),
250 return new ThreadEndEvent {
251 ThreadId = ReadPointer (),
254 return new ThreadNameEvent {
255 ThreadId = ReadPointer (),
256 Name = Reader.ReadCString (),
259 case LogMetadataType.Context:
261 return new ContextLoadEvent {
262 ContextId = ReadPointer (),
263 AppDomainId = ReadPointer (),
266 return new ContextUnloadEvent {
267 ContextId = ReadPointer (),
268 AppDomainId = ReadPointer (),
271 throw new LogException ("Invalid context metadata event.");
273 throw new LogException ($"Invalid metadata type ({metadataType}).");
276 case LogEventType.Method:
278 case LogEventType.MethodLeave:
279 return new LeaveEvent {
280 MethodPointer = ReadMethod (),
282 case LogEventType.MethodEnter:
283 return new EnterEvent {
284 MethodPointer = ReadMethod (),
286 case LogEventType.MethodLeaveExceptional:
287 return new ExceptionalLeaveEvent {
288 MethodPointer = ReadMethod (),
290 case LogEventType.MethodJit:
291 return new JitEvent {
292 MethodPointer = ReadMethod (),
293 CodePointer = ReadPointer (),
294 CodeSize = (long) Reader.ReadULeb128 (),
295 Name = Reader.ReadCString (),
298 throw new LogException ($"Invalid extended event type ({extType}).");
300 case LogEventType.Exception:
302 case LogEventType.ExceptionThrowNoBacktrace:
303 case LogEventType.ExceptionThrowBacktrace:
304 return new ThrowEvent {
305 ObjectPointer = ReadObject (),
306 Backtrace = ReadBacktrace (extType == LogEventType.ExceptionThrowBacktrace),
308 case LogEventType.ExceptionClause:
309 return new ExceptionClauseEvent {
310 Type = (LogExceptionClause) Reader.ReadByte (),
311 Index = (long) Reader.ReadULeb128 (),
312 MethodPointer = ReadMethod (),
313 ObjectPointer = ReadObject (),
316 throw new LogException ($"Invalid extended event type ({extType}).");
318 case LogEventType.Monitor:
320 case LogEventType.MonitorNoBacktrace:
321 case LogEventType.MonitorBacktrace:
322 return new MonitorEvent {
323 Event = (LogMonitorEvent) Reader.ReadByte (),
324 ObjectPointer = ReadObject (),
325 Backtrace = ReadBacktrace (extType == LogEventType.MonitorBacktrace),
328 throw new LogException ($"Invalid extended event type ({extType}).");
330 case LogEventType.Heap:
332 case LogEventType.HeapBegin:
333 return new HeapBeginEvent ();
334 case LogEventType.HeapEnd:
335 return new HeapEndEvent ();
336 case LogEventType.HeapObject: {
337 var ev = new HeapObjectEvent {
338 ObjectPointer = ReadObject (),
339 ClassPointer = ReadPointer (),
340 ObjectSize = (long) Reader.ReadULeb128 (),
343 var list = new HeapObjectEvent.HeapObjectReference [(int) Reader.ReadULeb128 ()];
345 for (var i = 0; i < list.Length; i++) {
346 list [i] = new HeapObjectEvent.HeapObjectReference {
347 Offset = (long) Reader.ReadULeb128 (),
348 ObjectPointer = ReadObject (),
352 ev.References = list;
356 case LogEventType.HeapRoots: {
357 // TODO: This entire event makes no sense.
359 var ev = new HeapRootsEvent ();
360 var list = new HeapRootsEvent.HeapRoot [(int) Reader.ReadULeb128 ()];
362 ev.MaxGenerationCollectionCount = (long) Reader.ReadULeb128 ();
364 for (var i = 0; i < list.Length; i++) {
365 list [i] = new HeapRootsEvent.HeapRoot {
366 ObjectPointer = ReadObject (),
367 Attributes = (LogHeapRootAttributes) Reader.ReadByte (),
368 ExtraInfo = (long) Reader.ReadULeb128 (),
377 throw new LogException ($"Invalid extended event type ({extType}).");
379 case LogEventType.Sample:
381 case LogEventType.SampleHit:
382 return new SampleHitEvent {
383 ThreadId = ReadPointer (),
384 UnmanagedBacktrace = ReadBacktrace (true, false),
385 ManagedBacktrace = ReadBacktrace (true),
387 case LogEventType.SampleUnmanagedSymbol:
388 return new UnmanagedSymbolEvent {
389 CodePointer = ReadPointer (),
390 CodeSize = (long) Reader.ReadULeb128 (),
391 Name = Reader.ReadCString (),
393 case LogEventType.SampleUnmanagedBinary:
394 return new UnmanagedBinaryEvent {
395 SegmentPointer = ReadPointer (),
396 SegmentOffset = (long) Reader.ReadULeb128 (),
397 SegmentSize = (long) Reader.ReadULeb128 (),
398 FileName = Reader.ReadCString (),
400 case LogEventType.SampleCounterDescriptions: {
401 var ev = new CounterDescriptionsEvent ();
402 var list = new CounterDescriptionsEvent.CounterDescription [(int) Reader.ReadULeb128 ()];
404 for (var i = 0; i < list.Length; i++) {
405 var section = (LogCounterSection) Reader.ReadULeb128 ();
407 list [i] = new CounterDescriptionsEvent.CounterDescription {
409 SectionName = section == LogCounterSection.User ? Reader.ReadCString () : string.Empty,
410 CounterName = Reader.ReadCString (),
411 Type = (LogCounterType) Reader.ReadByte (),
412 Unit = (LogCounterUnit) Reader.ReadByte (),
413 Variance = (LogCounterVariance) Reader.ReadByte (),
414 Index = (long) Reader.ReadULeb128 (),
418 ev.Descriptions = list;
422 case LogEventType.SampleCounters: {
423 var ev = new CounterSamplesEvent ();
424 var list = new List<CounterSamplesEvent.CounterSample> ();
427 var index = (long) Reader.ReadULeb128 ();
432 var counterType = (LogCounterType) Reader.ReadByte ();
436 switch (counterType) {
437 case LogCounterType.String:
438 value = Reader.ReadByte () == 1 ? Reader.ReadCString () : null;
440 case LogCounterType.Int32:
441 case LogCounterType.Word:
442 case LogCounterType.Int64:
443 case LogCounterType.Interval:
444 value = Reader.ReadSLeb128 ();
446 case LogCounterType.UInt32:
447 case LogCounterType.UInt64:
448 value = Reader.ReadULeb128 ();
450 case LogCounterType.Double:
451 value = Reader.ReadDouble ();
454 throw new LogException ($"Invalid counter type ({counterType}).");
457 list.Add (new CounterSamplesEvent.CounterSample {
469 throw new LogException ($"Invalid extended event type ({extType}).");
471 case LogEventType.Runtime:
473 case LogEventType.RuntimeJitHelper: {
474 var helperType = (LogJitHelper) Reader.ReadByte ();
476 return new JitHelperEvent {
478 BufferPointer = ReadPointer (),
479 BufferSize = (long) Reader.ReadULeb128 (),
480 Name = helperType == LogJitHelper.SpecificTrampoline ? Reader.ReadCString () : string.Empty,
484 throw new LogException ($"Invalid extended event type ({extType}).");
486 case LogEventType.Coverage:
488 case LogEventType.CoverageAssembly:
489 return new AssemblyCoverageEvent {
490 AssemblyName = Reader.ReadCString (),
491 Guid = Guid.Parse (Reader.ReadCString ()),
492 FileName = Reader.ReadCString (),
493 NumberOfMethods = (long) Reader.ReadULeb128 (),
494 FullyCovered = (long) Reader.ReadULeb128 (),
495 PartiallyCovered = (long) Reader.ReadULeb128 (),
497 case LogEventType.CoverageMethod:
498 return new MethodCoverageEvent {
499 AssemblyName = Reader.ReadCString (),
500 ClassName = Reader.ReadCString (),
501 MethodName = Reader.ReadCString (),
502 MethodSignature = Reader.ReadCString (),
503 FileName = Reader.ReadCString (),
504 MetadataToken = Reader.ReadULeb128 (),
505 MethodId = (long) Reader.ReadULeb128 (),
506 NumberOfStatements = (long) Reader.ReadULeb128 (),
508 case LogEventType.CoverageStatement:
509 return new StatementCoverageEvent {
510 MethodId = (long) Reader.ReadULeb128 (),
511 RelativeILOffset = (long) Reader.ReadULeb128 (),
512 Counter = Reader.ReadULeb128 (),
513 Line = (long) Reader.ReadULeb128 (),
514 Column = (long) Reader.ReadULeb128 (),
516 case LogEventType.CoverageClass:
517 return new ClassCoverageEvent {
518 AssemblyName = Reader.ReadCString (),
519 ClassName = Reader.ReadCString (),
520 NumberOfMethods = (long) Reader.ReadULeb128 (),
521 FullyCovered = (long) Reader.ReadULeb128 (),
522 PartiallyCovered = (long) Reader.ReadULeb128 (),
525 throw new LogException ($"Invalid extended event type ({extType}).");
527 case LogEventType.Meta:
529 case LogEventType.MetaSynchronizationPoint:
530 return new SynchronizationPointEvent {
531 Type = (LogSynchronizationPoint) Reader.ReadByte (),
534 throw new LogException ($"Invalid extended event type ({extType}).");
537 throw new LogException ($"Invalid basic event type ({basicType}).");
543 var ptr = Reader.ReadSLeb128 () + _bufferHeader.PointerBase;
545 return StreamHeader.PointerSize == sizeof (long) ? ptr : ptr & 0xffffffffL;
550 return Reader.ReadSLeb128 () + _bufferHeader.ObjectBase << 3;
555 return _bufferHeader.CurrentMethod += Reader.ReadSLeb128 ();
560 return _bufferHeader.CurrentTime += Reader.ReadULeb128 ();
563 IReadOnlyList<long> ReadBacktrace (bool actuallyRead, bool managed = true)
566 return Array.Empty<long> ();
568 var list = new long [(int) Reader.ReadULeb128 ()];
570 for (var i = 0; i < list.Length; i++)
571 list [i] = managed ? ReadMethod () : ReadPointer ();