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 LogStream Stream { get; }
17 public LogEventVisitor ImmediateVisitor { get; }
19 public LogEventVisitor SortedVisitor { get; }
21 public LogStreamHeader StreamHeader { get; private set; }
25 LogBufferHeader _bufferHeader;
31 public LogProcessor (LogStream stream, LogEventVisitor immediateVisitor, LogEventVisitor sortedVisitor)
34 throw new ArgumentNullException (nameof (stream));
37 ImmediateVisitor = immediateVisitor;
38 SortedVisitor = sortedVisitor;
41 public void Process ()
43 Process (CancellationToken.None);
46 static void ProcessEvent (LogEventVisitor visitor, LogEvent ev)
48 if (visitor != null) {
49 visitor.VisitBefore (ev);
51 visitor.VisitAfter (ev);
55 void ProcessEvents (List<LogEvent> events, CancellationToken token)
57 foreach (var ev in events.OrderBy (x => x.Timestamp)) {
58 token.ThrowIfCancellationRequested ();
59 ProcessEvent (SortedVisitor, ev);
65 public void Process (CancellationToken token)
68 throw new InvalidOperationException ("This log processor cannot be reused.");
71 _reader = new LogReader (Stream, true);
73 StreamHeader = new LogStreamHeader (_reader);
75 var events = new List<LogEvent> (Environment.ProcessorCount * 1000);
77 while (!Stream.EndOfStream) {
78 token.ThrowIfCancellationRequested ();
80 _bufferHeader = new LogBufferHeader (StreamHeader, _reader);
82 // Read the entire buffer into a MemoryStream ahead of time to
83 // reduce the amount of I/O system calls we do. This should be
84 // fine since the profiler tries to keep buffers small and
85 // flushes them every second at minimum. This also has the
86 // advantage that we can use the Position and Length properties
87 // even if the stream we read the buffer from is actually
89 var stream = new MemoryStream (_reader.ReadBytes (_bufferHeader.Length), false);
91 using (var reader = new LogReader (stream, false)) {
92 var oldReader = _reader;
96 while (stream.Position < stream.Length) {
97 token.ThrowIfCancellationRequested ();
99 var ev = ReadEvent ();
101 ProcessEvent (ImmediateVisitor, ev);
104 if (ev is SynchronizationPointEvent)
105 ProcessEvents (events, token);
112 ProcessEvents (events, token);
115 LogEvent ReadEvent ()
117 var type = _reader.ReadByte ();
118 var basicType = (LogEventType) (type & 0xf);
119 var extType = (LogEventType) (type & 0xf0);
125 case LogEventType.Allocation:
127 case LogEventType.AllocationBacktrace:
128 case LogEventType.AllocationNoBacktrace:
129 ev = new AllocationEvent {
130 ClassPointer = ReadPointer (),
131 ObjectPointer = ReadObject (),
132 ObjectSize = (long) _reader.ReadULeb128 (),
133 Backtrace = ReadBacktrace (extType == LogEventType.AllocationBacktrace),
137 throw new LogException ($"Invalid extended event type ({extType}).");
140 case LogEventType.GC:
142 case LogEventType.GCEvent:
144 Type = (LogGCEvent) _reader.ReadByte (),
145 Generation = _reader.ReadByte (),
148 case LogEventType.GCResize:
149 ev = new GCResizeEvent {
150 NewSize = (long) _reader.ReadULeb128 (),
153 case LogEventType.GCMove: {
154 var list = new long [(int) _reader.ReadULeb128 ()];
156 for (var i = 0; i < list.Length; i++)
157 list [i] = ReadObject ();
159 ev = new GCMoveEvent {
160 OldObjectPointers = list.Where ((_, i) => i % 2 == 0).ToArray (),
161 NewObjectPointers = list.Where ((_, i) => i % 2 != 0).ToArray (),
165 case LogEventType.GCHandleCreationNoBacktrace:
166 case LogEventType.GCHandleCreationBacktrace:
167 ev = new GCHandleCreationEvent {
168 Type = (LogGCHandleType) _reader.ReadULeb128 (),
169 Handle = (long) _reader.ReadULeb128 (),
170 ObjectPointer = ReadObject (),
171 Backtrace = ReadBacktrace (extType == LogEventType.GCHandleCreationBacktrace),
174 case LogEventType.GCHandleDeletionNoBacktrace:
175 case LogEventType.GCHandleDeletionBacktrace:
176 ev = new GCHandleDeletionEvent {
177 Type = (LogGCHandleType) _reader.ReadULeb128 (),
178 Handle = (long) _reader.ReadULeb128 (),
179 Backtrace = ReadBacktrace (extType == LogEventType.GCHandleDeletionBacktrace),
182 case LogEventType.GCFinalizeBegin:
183 ev = new GCFinalizeBeginEvent ();
185 case LogEventType.GCFinalizeEnd:
186 ev = new GCFinalizeEndEvent ();
188 case LogEventType.GCFinalizeObjectBegin:
189 ev = new GCFinalizeObjectBeginEvent {
190 ObjectPointer = ReadObject (),
193 case LogEventType.GCFinalizeObjectEnd:
194 ev = new GCFinalizeObjectEndEvent {
195 ObjectPointer = ReadObject (),
199 throw new LogException ($"Invalid extended event type ({extType}).");
202 case LogEventType.Metadata: {
207 case LogEventType.MetadataExtra:
209 case LogEventType.MetadataEndLoad:
212 case LogEventType.MetadataEndUnload:
216 throw new LogException ($"Invalid extended event type ({extType}).");
219 var metadataType = (LogMetadataType) _reader.ReadByte ();
221 switch (metadataType) {
222 case LogMetadataType.Class:
224 ev = new ClassLoadEvent {
225 ClassPointer = ReadPointer (),
226 ImagePointer = ReadPointer (),
227 Name = _reader.ReadCString (),
230 throw new LogException ("Invalid class metadata event.");
232 case LogMetadataType.Image:
234 ev = new ImageLoadEvent {
235 ImagePointer = ReadPointer (),
236 Name = _reader.ReadCString (),
239 ev = new ImageUnloadEvent {
240 ImagePointer = ReadPointer (),
241 Name = _reader.ReadCString (),
244 throw new LogException ("Invalid image metadata event.");
246 case LogMetadataType.Assembly:
248 ev = new AssemblyLoadEvent {
249 AssemblyPointer = ReadPointer (),
250 ImagePointer = StreamHeader.FormatVersion >= 14 ? ReadPointer () : 0,
251 Name = _reader.ReadCString (),
254 ev = new AssemblyUnloadEvent {
255 AssemblyPointer = ReadPointer (),
256 ImagePointer = StreamHeader.FormatVersion >= 14 ? ReadPointer () : 0,
257 Name = _reader.ReadCString (),
260 throw new LogException ("Invalid assembly metadata event.");
262 case LogMetadataType.AppDomain:
264 ev = new AppDomainLoadEvent {
265 AppDomainId = ReadPointer (),
268 ev = new AppDomainUnloadEvent {
269 AppDomainId = ReadPointer (),
272 ev = new AppDomainNameEvent {
273 AppDomainId = ReadPointer (),
274 Name = _reader.ReadCString (),
278 case LogMetadataType.Thread:
280 ev = new ThreadStartEvent {
281 ThreadId = ReadPointer (),
284 ev = new ThreadEndEvent {
285 ThreadId = ReadPointer (),
288 ev = new ThreadNameEvent {
289 ThreadId = ReadPointer (),
290 Name = _reader.ReadCString (),
294 case LogMetadataType.Context:
296 ev = new ContextLoadEvent {
297 ContextId = ReadPointer (),
298 AppDomainId = ReadPointer (),
301 ev = new ContextUnloadEvent {
302 ContextId = ReadPointer (),
303 AppDomainId = ReadPointer (),
306 throw new LogException ("Invalid context metadata event.");
309 throw new LogException ($"Invalid metadata type ({metadataType}).");
313 case LogEventType.Method:
315 case LogEventType.MethodLeave:
316 ev = new LeaveEvent {
317 MethodPointer = ReadMethod (),
320 case LogEventType.MethodEnter:
321 ev = new EnterEvent {
322 MethodPointer = ReadMethod (),
325 case LogEventType.MethodLeaveExceptional:
326 ev = new ExceptionalLeaveEvent {
327 MethodPointer = ReadMethod (),
330 case LogEventType.MethodJit:
332 MethodPointer = ReadMethod (),
333 CodePointer = ReadPointer (),
334 CodeSize = (long) _reader.ReadULeb128 (),
335 Name = _reader.ReadCString (),
339 throw new LogException ($"Invalid extended event type ({extType}).");
342 case LogEventType.Exception:
344 case LogEventType.ExceptionThrowNoBacktrace:
345 case LogEventType.ExceptionThrowBacktrace:
346 ev = new ThrowEvent {
347 ObjectPointer = ReadObject (),
348 Backtrace = ReadBacktrace (extType == LogEventType.ExceptionThrowBacktrace),
351 case LogEventType.ExceptionClause:
352 ev = new ExceptionClauseEvent {
353 Type = (LogExceptionClause) _reader.ReadByte (),
354 Index = (long) _reader.ReadULeb128 (),
355 MethodPointer = ReadMethod (),
356 ObjectPointer = StreamHeader.FormatVersion >= 14 ? ReadObject () : 0,
360 throw new LogException ($"Invalid extended event type ({extType}).");
363 case LogEventType.Monitor:
364 if (StreamHeader.FormatVersion < 14) {
365 if (extType.HasFlag (LogEventType.MonitorBacktrace)) {
366 extType = LogEventType.MonitorBacktrace;
368 extType = LogEventType.MonitorNoBacktrace;
372 case LogEventType.MonitorNoBacktrace:
373 case LogEventType.MonitorBacktrace:
374 ev = new MonitorEvent {
375 Event = StreamHeader.FormatVersion >= 14 ?
376 (LogMonitorEvent) _reader.ReadByte () :
377 (LogMonitorEvent) ((((byte) type & 0xf0) >> 4) & 0x3),
378 ObjectPointer = ReadObject (),
379 Backtrace = ReadBacktrace (extType == LogEventType.MonitorBacktrace),
383 throw new LogException ($"Invalid extended event type ({extType}).");
386 case LogEventType.Heap:
388 case LogEventType.HeapBegin:
389 ev = new HeapBeginEvent ();
391 case LogEventType.HeapEnd:
392 ev = new HeapEndEvent ();
394 case LogEventType.HeapObject: {
395 HeapObjectEvent hoe = new HeapObjectEvent {
396 ObjectPointer = ReadObject (),
397 ClassPointer = ReadPointer (),
398 ObjectSize = (long) _reader.ReadULeb128 (),
401 var list = new HeapObjectEvent.HeapObjectReference [(int) _reader.ReadULeb128 ()];
403 for (var i = 0; i < list.Length; i++) {
404 list [i] = new HeapObjectEvent.HeapObjectReference {
405 Offset = (long) _reader.ReadULeb128 (),
406 ObjectPointer = ReadObject (),
410 hoe.References = list;
416 case LogEventType.HeapRoots: {
417 // TODO: This entire event makes no sense.
418 var hre = new HeapRootsEvent ();
419 var list = new HeapRootsEvent.HeapRoot [(int) _reader.ReadULeb128 ()];
421 hre.MaxGenerationCollectionCount = (long) _reader.ReadULeb128 ();
423 for (var i = 0; i < list.Length; i++) {
424 list [i] = new HeapRootsEvent.HeapRoot {
425 ObjectPointer = ReadObject (),
426 Attributes = StreamHeader.FormatVersion == 13 ? (LogHeapRootAttributes) _reader.ReadByte () : (LogHeapRootAttributes) _reader.ReadULeb128 (),
427 ExtraInfo = (long) _reader.ReadULeb128 (),
437 throw new LogException ($"Invalid extended event type ({extType}).");
440 case LogEventType.Sample:
442 case LogEventType.SampleHit:
443 if (StreamHeader.FormatVersion < 14) {
444 // Read SampleType (always set to .Cycles) for versions < 14
447 ev = new SampleHitEvent {
448 ThreadId = ReadPointer (),
449 UnmanagedBacktrace = ReadBacktrace (true, false),
450 ManagedBacktrace = ReadBacktrace (true).Reverse ().ToArray (),
453 case LogEventType.SampleUnmanagedSymbol:
454 ev = new UnmanagedSymbolEvent {
455 CodePointer = ReadPointer (),
456 CodeSize = (long) _reader.ReadULeb128 (),
457 Name = _reader.ReadCString (),
460 case LogEventType.SampleUnmanagedBinary:
461 ev = new UnmanagedBinaryEvent {
462 SegmentPointer = StreamHeader.FormatVersion >= 14 ? ReadPointer () : _reader.ReadSLeb128 (),
463 SegmentOffset = (long) _reader.ReadULeb128 (),
464 SegmentSize = (long) _reader.ReadULeb128 (),
465 FileName = _reader.ReadCString (),
468 case LogEventType.SampleCounterDescriptions: {
469 var cde = new CounterDescriptionsEvent ();
470 var list = new CounterDescriptionsEvent.CounterDescription [(int) _reader.ReadULeb128 ()];
472 for (var i = 0; i < list.Length; i++) {
473 var section = (LogCounterSection) _reader.ReadULeb128 ();
475 list [i] = new CounterDescriptionsEvent.CounterDescription {
477 SectionName = section == LogCounterSection.User ? _reader.ReadCString () : null,
478 CounterName = _reader.ReadCString (),
479 Type = (LogCounterType) _reader.ReadByte (),
480 Unit = (LogCounterUnit) _reader.ReadByte (),
481 Variance = (LogCounterVariance) _reader.ReadByte (),
482 Index = (long) _reader.ReadULeb128 (),
486 cde.Descriptions = list;
491 case LogEventType.SampleCounters: {
492 var cse = new CounterSamplesEvent ();
493 var list = new List<CounterSamplesEvent.CounterSample> ();
496 var index = (long) _reader.ReadULeb128 ();
501 var counterType = (LogCounterType) _reader.ReadByte ();
505 switch (counterType) {
506 case LogCounterType.String:
507 value = _reader.ReadByte () == 1 ? _reader.ReadCString () : null;
509 case LogCounterType.Int32:
510 case LogCounterType.Word:
511 case LogCounterType.Int64:
512 case LogCounterType.Interval:
513 value = _reader.ReadSLeb128 ();
515 case LogCounterType.UInt32:
516 case LogCounterType.UInt64:
517 value = _reader.ReadULeb128 ();
519 case LogCounterType.Double:
520 value = _reader.ReadDouble ();
523 throw new LogException ($"Invalid counter type ({counterType}).");
526 list.Add (new CounterSamplesEvent.CounterSample {
539 throw new LogException ($"Invalid extended event type ({extType}).");
542 case LogEventType.Runtime:
544 case LogEventType.RuntimeJitHelper: {
545 var helperType = (LogJitHelper) _reader.ReadByte ();
547 ev = new JitHelperEvent {
549 BufferPointer = ReadPointer (),
550 BufferSize = (long) _reader.ReadULeb128 (),
551 Name = helperType == LogJitHelper.SpecificTrampoline ? _reader.ReadCString () : null,
556 throw new LogException ($"Invalid extended event type ({extType}).");
559 case LogEventType.Meta:
561 case LogEventType.MetaSynchronizationPoint:
562 ev = new SynchronizationPointEvent {
563 Type = (LogSynchronizationPoint) _reader.ReadByte (),
567 throw new LogException ($"Invalid extended event type ({extType}).");
571 throw new LogException ($"Invalid basic event type ({basicType}).");
574 ev.Timestamp = _time;
575 ev.Buffer = _bufferHeader;
582 var ptr = _reader.ReadSLeb128 () + _bufferHeader.PointerBase;
584 return StreamHeader.PointerSize == sizeof (long) ? ptr : ptr & 0xffffffffL;
589 return _reader.ReadSLeb128 () + _bufferHeader.ObjectBase << 3;
594 return _bufferHeader.CurrentMethod += _reader.ReadSLeb128 ();
599 return _bufferHeader.CurrentTime += _reader.ReadULeb128 ();
602 IReadOnlyList<long> ReadBacktrace (bool actuallyRead, bool managed = true)
605 return Array.Empty<long> ();
607 var list = new long [(int) _reader.ReadULeb128 ()];
609 for (var i = 0; i < list.Length; i++)
610 list [i] = managed ? ReadMethod () : ReadPointer ();