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 = ReadPointer (),
234 Name = Reader.ReadCString (),
237 ev = new AssemblyUnloadEvent {
238 AssemblyPointer = ReadPointer (),
239 ImagePointer = ReadPointer (),
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 = ReadObject (),
343 throw new LogException ($"Invalid extended event type ({extType}).");
346 case LogEventType.Monitor:
348 case LogEventType.MonitorNoBacktrace:
349 case LogEventType.MonitorBacktrace:
350 ev = new MonitorEvent {
351 Event = (LogMonitorEvent) Reader.ReadByte (),
352 ObjectPointer = ReadObject (),
353 Backtrace = ReadBacktrace (extType == LogEventType.MonitorBacktrace),
357 throw new LogException ($"Invalid extended event type ({extType}).");
360 case LogEventType.Heap:
362 case LogEventType.HeapBegin:
363 ev = new HeapBeginEvent ();
365 case LogEventType.HeapEnd:
366 ev = new HeapEndEvent ();
368 case LogEventType.HeapObject: {
369 HeapObjectEvent hoe = new HeapObjectEvent {
370 ObjectPointer = ReadObject (),
371 ClassPointer = ReadPointer (),
372 ObjectSize = (long) Reader.ReadULeb128 (),
375 var list = new HeapObjectEvent.HeapObjectReference [(int) Reader.ReadULeb128 ()];
377 for (var i = 0; i < list.Length; i++) {
378 list [i] = new HeapObjectEvent.HeapObjectReference {
379 Offset = (long) Reader.ReadULeb128 (),
380 ObjectPointer = ReadObject (),
384 hoe.References = list;
390 case LogEventType.HeapRoots: {
391 // TODO: This entire event makes no sense.
392 var hre = new HeapRootsEvent ();
393 var list = new HeapRootsEvent.HeapRoot [(int) Reader.ReadULeb128 ()];
395 hre.MaxGenerationCollectionCount = (long) Reader.ReadULeb128 ();
397 for (var i = 0; i < list.Length; i++) {
398 list [i] = new HeapRootsEvent.HeapRoot {
399 ObjectPointer = ReadObject (),
400 Attributes = (LogHeapRootAttributes) Reader.ReadULeb128 (),
401 ExtraInfo = (long) Reader.ReadULeb128 (),
411 throw new LogException ($"Invalid extended event type ({extType}).");
414 case LogEventType.Sample:
416 case LogEventType.SampleHit:
417 ev = new SampleHitEvent {
418 ThreadId = ReadPointer (),
419 UnmanagedBacktrace = ReadBacktrace (true, false),
420 ManagedBacktrace = ReadBacktrace (true),
423 case LogEventType.SampleUnmanagedSymbol:
424 ev = new UnmanagedSymbolEvent {
425 CodePointer = ReadPointer (),
426 CodeSize = (long) Reader.ReadULeb128 (),
427 Name = Reader.ReadCString (),
430 case LogEventType.SampleUnmanagedBinary:
431 ev = new UnmanagedBinaryEvent {
432 SegmentPointer = ReadPointer (),
433 SegmentOffset = (long) Reader.ReadULeb128 (),
434 SegmentSize = (long) Reader.ReadULeb128 (),
435 FileName = Reader.ReadCString (),
438 case LogEventType.SampleCounterDescriptions: {
439 var cde = new CounterDescriptionsEvent ();
440 var list = new CounterDescriptionsEvent.CounterDescription [(int) Reader.ReadULeb128 ()];
442 for (var i = 0; i < list.Length; i++) {
443 var section = (LogCounterSection) Reader.ReadULeb128 ();
445 list [i] = new CounterDescriptionsEvent.CounterDescription {
447 SectionName = section == LogCounterSection.User ? Reader.ReadCString () : string.Empty,
448 CounterName = Reader.ReadCString (),
449 Type = (LogCounterType) Reader.ReadByte (),
450 Unit = (LogCounterUnit) Reader.ReadByte (),
451 Variance = (LogCounterVariance) Reader.ReadByte (),
452 Index = (long) Reader.ReadULeb128 (),
456 cde.Descriptions = list;
461 case LogEventType.SampleCounters: {
462 var cse = new CounterSamplesEvent ();
463 var list = new List<CounterSamplesEvent.CounterSample> ();
466 var index = (long) Reader.ReadULeb128 ();
471 var counterType = (LogCounterType) Reader.ReadByte ();
475 switch (counterType) {
476 case LogCounterType.String:
477 value = Reader.ReadByte () == 1 ? Reader.ReadCString () : null;
479 case LogCounterType.Int32:
480 case LogCounterType.Word:
481 case LogCounterType.Int64:
482 case LogCounterType.Interval:
483 value = Reader.ReadSLeb128 ();
485 case LogCounterType.UInt32:
486 case LogCounterType.UInt64:
487 value = Reader.ReadULeb128 ();
489 case LogCounterType.Double:
490 value = Reader.ReadDouble ();
493 throw new LogException ($"Invalid counter type ({counterType}).");
496 list.Add (new CounterSamplesEvent.CounterSample {
509 throw new LogException ($"Invalid extended event type ({extType}).");
512 case LogEventType.Runtime:
514 case LogEventType.RuntimeJitHelper: {
515 var helperType = (LogJitHelper) Reader.ReadByte ();
517 ev = new JitHelperEvent {
519 BufferPointer = ReadPointer (),
520 BufferSize = (long) Reader.ReadULeb128 (),
521 Name = helperType == LogJitHelper.SpecificTrampoline ? Reader.ReadCString () : string.Empty,
526 throw new LogException ($"Invalid extended event type ({extType}).");
529 case LogEventType.Meta:
531 case LogEventType.MetaSynchronizationPoint:
532 ev = new SynchronizationPointEvent {
533 Type = (LogSynchronizationPoint) Reader.ReadByte (),
537 throw new LogException ($"Invalid extended event type ({extType}).");
541 throw new LogException ($"Invalid basic event type ({basicType}).");
544 ev.Timestamp = _time;
545 ev.Buffer = _bufferHeader;
552 var ptr = Reader.ReadSLeb128 () + _bufferHeader.PointerBase;
554 return StreamHeader.PointerSize == sizeof (long) ? ptr : ptr & 0xffffffffL;
559 return Reader.ReadSLeb128 () + _bufferHeader.ObjectBase << 3;
564 return _bufferHeader.CurrentMethod += Reader.ReadSLeb128 ();
569 return _bufferHeader.CurrentTime += Reader.ReadULeb128 ();
572 IReadOnlyList<long> ReadBacktrace (bool actuallyRead, bool managed = true)
575 return Array.Empty<long> ();
577 var list = new long [(int) Reader.ReadULeb128 ()];
579 for (var i = 0; i < list.Length; i++)
580 list [i] = managed ? ReadMethod () : ReadPointer ();