71bdca3f0dc1defe85366f1b7ba93cc4c373a101
[mono.git] / mcs / class / Mono.Profiler.Log / Mono.Profiler.Log / LogProcessor.cs
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.
4
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Linq;
9 using System.Threading;
10
11 namespace Mono.Profiler.Log {
12
13         public sealed class LogProcessor {
14
15                 public LogReader Reader { get; }
16
17                 public LogEventVisitor ImmediateVisitor { get; }
18
19                 public LogEventVisitor SortedVisitor { get; }
20
21                 public LogStreamHeader StreamHeader { get; private set; }
22
23                 LogBufferHeader _bufferHeader;
24
25                 ulong _time;
26
27                 bool _used;
28
29                 public LogProcessor (LogReader reader, LogEventVisitor immediateVisitor, LogEventVisitor sortedVisitor)
30                 {
31                         if (reader == null)
32                                 throw new ArgumentNullException (nameof (reader));
33
34                         Reader = reader;
35                         ImmediateVisitor = immediateVisitor;
36                         SortedVisitor = sortedVisitor;
37                 }
38
39                 public void Process ()
40                 {
41                         Process (CancellationToken.None);
42                 }
43
44                 static void ProcessEvent (LogEventVisitor visitor, LogEvent ev)
45                 {
46                         if (visitor != null) {
47                                 visitor.VisitBefore (ev);
48                                 ev.Accept (visitor);
49                                 visitor.VisitAfter (ev);
50                         }
51                 }
52
53                 void ProcessEvents (List<LogEvent> events, CancellationToken token)
54                 {
55                         foreach (var ev in events.OrderBy (x => x.Timestamp)) {
56                                 token.ThrowIfCancellationRequested ();
57                                 ProcessEvent (SortedVisitor, ev);
58                         }
59
60                         events.Clear ();
61                 }
62
63                 public void Process (CancellationToken token)
64                 {
65                         if (_used)
66                                 throw new InvalidOperationException ("This log processor cannot be reused.");
67
68                         _used = true;
69                         StreamHeader = new LogStreamHeader (Reader);
70
71                         var events = new List<LogEvent> (Environment.ProcessorCount * 1000);
72
73                         while (!Reader.BaseStream.EndOfStream) {
74                                 token.ThrowIfCancellationRequested ();
75
76                                 _bufferHeader = new LogBufferHeader (StreamHeader, Reader);
77
78                                 // Use the manual position tracking in LogReader so we're
79                                 // compatible with non-seekable streams.
80                                 var goal = Reader.Position + _bufferHeader.Length;
81
82                                 while (Reader.Position < goal) {
83                                         token.ThrowIfCancellationRequested ();
84
85                                         var ev = ReadEvent ();
86
87                                         ProcessEvent (ImmediateVisitor, ev);
88                                         events.Add (ev);
89
90                                         if (ev is SynchronizationPointEvent)
91                                                 ProcessEvents (events, token);
92                                 }
93                         }
94
95                         ProcessEvents (events, token);
96                 }
97
98                 LogEvent ReadEvent ()
99                 {
100                         var type = Reader.ReadByte ();
101                         var basicType = (LogEventType) (type & 0xf);
102                         var extType = (LogEventType) (type & 0xf0);
103
104                         _time = ReadTime ();
105                         LogEvent ev = null;
106
107                         switch (basicType) {
108                         case LogEventType.Allocation:
109                                 switch (extType) {
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),
117                                         };
118                                         break;
119                                 default:
120                                         throw new LogException ($"Invalid extended event type ({extType}).");
121                                 }
122                                 break;
123                         case LogEventType.GC:
124                                 switch (extType) {
125                                 case LogEventType.GCEvent:
126                                         ev = new GCEvent {
127                                                 Type = (LogGCEvent) Reader.ReadByte (),
128                                                 Generation = Reader.ReadByte (),
129                                         };
130                                         break;
131                                 case LogEventType.GCResize:
132                                         ev = new GCResizeEvent {
133                                                 NewSize = (long) Reader.ReadULeb128 (),
134                                         };
135                                         break;
136                                 case LogEventType.GCMove: {
137                                         var list = new long [(int) Reader.ReadULeb128 ()];
138
139                                         for (var i = 0; i < list.Length; i++)
140                                                 list [i] = ReadObject ();
141
142                                         ev = new GCMoveEvent {
143                                                 OldObjectPointers = list.Where ((_, i) => i % 2 == 0).ToArray (),
144                                                 NewObjectPointers = list.Where ((_, i) => i % 2 != 0).ToArray (),
145                                         };
146                                         break;
147                                 }
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),
155                                         };
156                                         break;
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),
163                                         };
164                                         break;
165                                 case LogEventType.GCFinalizeBegin:
166                                         ev = new GCFinalizeBeginEvent ();
167                                         break;
168                                 case LogEventType.GCFinalizeEnd:
169                                         ev = new GCFinalizeEndEvent ();
170                                         break;
171                                 case LogEventType.GCFinalizeObjectBegin:
172                                         ev = new GCFinalizeObjectBeginEvent {
173                                                 ObjectPointer = ReadObject (),
174                                         };
175                                         break;
176                                 case LogEventType.GCFinalizeObjectEnd:
177                                         ev = new GCFinalizeObjectEndEvent {
178                                                 ObjectPointer = ReadObject (),
179                                         };
180                                         break;
181                                 default:
182                                         throw new LogException ($"Invalid extended event type ({extType}).");
183                                 }
184                                 break;
185                         case LogEventType.Metadata: {
186                                 var load = false;
187                                 var unload = false;
188
189                                 switch (extType) {
190                                 case LogEventType.MetadataExtra:
191                                         break;
192                                 case LogEventType.MetadataEndLoad:
193                                         load = true;
194                                         break;
195                                 case LogEventType.MetadataEndUnload:
196                                         unload = true;
197                                         break;
198                                 default:
199                                         throw new LogException ($"Invalid extended event type ({extType}).");
200                                 }
201
202                                 var metadataType = (LogMetadataType) Reader.ReadByte ();
203
204                                 switch (metadataType) {
205                                 case LogMetadataType.Class:
206                                         if (load) {
207                                                 ev = new ClassLoadEvent {
208                                                         ClassPointer = ReadPointer (),
209                                                         ImagePointer = ReadPointer (),
210                                                         Name = Reader.ReadCString (),
211                                                 };
212                                         } else
213                                                 throw new LogException ("Invalid class metadata event.");
214                                         break;
215                                 case LogMetadataType.Image:
216                                         if (load) {
217                                                 ev = new ImageLoadEvent {
218                                                         ImagePointer = ReadPointer (),
219                                                         Name = Reader.ReadCString (),
220                                                 };
221                                         } else if (unload) {
222                                                 ev = new ImageUnloadEvent {
223                                                         ImagePointer = ReadPointer (),
224                                                         Name = Reader.ReadCString (),
225                                                 };
226                                         } else
227                                                 throw new LogException ("Invalid image metadata event.");
228                                         break;
229                                 case LogMetadataType.Assembly:
230                                         if (load) {
231                                                 ev = new AssemblyLoadEvent {
232                                                         AssemblyPointer = ReadPointer (),
233                                                         ImagePointer = StreamHeader.FormatVersion >= 14 ? ReadPointer () : 0,
234                                                         Name = Reader.ReadCString (),
235                                                 };
236                                         } else if (unload) {
237                                                 ev = new AssemblyUnloadEvent {
238                                                         AssemblyPointer = ReadPointer (),
239                                                         ImagePointer = StreamHeader.FormatVersion >= 14 ? ReadPointer () : 0,
240                                                         Name = Reader.ReadCString (),
241                                                 };
242                                         } else
243                                                 throw new LogException ("Invalid assembly metadata event.");
244                                         break;
245                                 case LogMetadataType.AppDomain:
246                                         if (load) {
247                                                 ev = new AppDomainLoadEvent {
248                                                         AppDomainId = ReadPointer (),
249                                                 };
250                                         } else if (unload) {
251                                                 ev = new AppDomainUnloadEvent {
252                                                         AppDomainId = ReadPointer (),
253                                                 };
254                                         } else {
255                                                 ev = new AppDomainNameEvent {
256                                                         AppDomainId = ReadPointer (),
257                                                         Name = Reader.ReadCString (),
258                                                 };
259                                         }
260                                         break;
261                                 case LogMetadataType.Thread:
262                                         if (load) {
263                                                 ev = new ThreadStartEvent {
264                                                         ThreadId = ReadPointer (),
265                                                 };
266                                         } else if (unload) {
267                                                 ev = new ThreadEndEvent {
268                                                         ThreadId = ReadPointer (),
269                                                 };
270                                         } else {
271                                                 ev = new ThreadNameEvent {
272                                                         ThreadId = ReadPointer (),
273                                                         Name = Reader.ReadCString (),
274                                                 };
275                                         }
276                                         break;
277                                 case LogMetadataType.Context:
278                                         if (load) {
279                                                 ev = new ContextLoadEvent {
280                                                         ContextId = ReadPointer (),
281                                                         AppDomainId = ReadPointer (),
282                                                 };
283                                         } else if (unload) {
284                                                 ev = new ContextUnloadEvent {
285                                                         ContextId = ReadPointer (),
286                                                         AppDomainId = ReadPointer (),
287                                                 };
288                                         } else
289                                                 throw new LogException ("Invalid context metadata event.");
290                                         break;
291                                 default:
292                                         throw new LogException ($"Invalid metadata type ({metadataType}).");
293                                 }
294                                 break;
295                         }
296                         case LogEventType.Method:
297                                 switch (extType) {
298                                 case LogEventType.MethodLeave:
299                                         ev = new LeaveEvent {
300                                                 MethodPointer = ReadMethod (),
301                                         };
302                                         break;
303                                 case LogEventType.MethodEnter:
304                                         ev = new EnterEvent {
305                                                 MethodPointer = ReadMethod (),
306                                         };
307                                         break;
308                                 case LogEventType.MethodLeaveExceptional:
309                                         ev = new ExceptionalLeaveEvent {
310                                                 MethodPointer = ReadMethod (),
311                                         };
312                                         break;
313                                 case LogEventType.MethodJit:
314                                         ev = new JitEvent {
315                                                 MethodPointer = ReadMethod (),
316                                                 CodePointer = ReadPointer (),
317                                                 CodeSize = (long) Reader.ReadULeb128 (),
318                                                 Name = Reader.ReadCString (),
319                                         };
320                                         break;
321                                 default:
322                                         throw new LogException ($"Invalid extended event type ({extType}).");
323                                 }
324                                 break;
325                         case LogEventType.Exception:
326                                 switch (extType) {
327                                 case LogEventType.ExceptionThrowNoBacktrace:
328                                 case LogEventType.ExceptionThrowBacktrace:
329                                         ev = new ThrowEvent {
330                                                 ObjectPointer = ReadObject (),
331                                                 Backtrace = ReadBacktrace (extType == LogEventType.ExceptionThrowBacktrace),
332                                         };
333                                         break;
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,
340                                         };
341                                         break;
342                                 default:
343                                         throw new LogException ($"Invalid extended event type ({extType}).");
344                                 }
345                                 break;
346                         case LogEventType.Monitor:
347                                 if (StreamHeader.FormatVersion < 14) {
348                                         if (extType.HasFlag (LogEventType.MonitorBacktrace)) {
349                                                 extType = LogEventType.MonitorBacktrace;
350                                         } else {
351                                                 extType = LogEventType.MonitorNoBacktrace;
352                                         }
353                                 }
354                                 switch (extType) {
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),
363                                         };
364                                         break;
365                                 default:
366                                         throw new LogException ($"Invalid extended event type ({extType}).");
367                                 }
368                                 break;
369                         case LogEventType.Heap:
370                                 switch (extType) {
371                                 case LogEventType.HeapBegin:
372                                         ev = new HeapBeginEvent ();
373                                         break;
374                                 case LogEventType.HeapEnd:
375                                         ev = new HeapEndEvent ();
376                                         break;
377                                 case LogEventType.HeapObject: {
378                                         HeapObjectEvent hoe = new HeapObjectEvent {
379                                                 ObjectPointer = ReadObject (),
380                                                 ClassPointer = ReadPointer (),
381                                                 ObjectSize = (long) Reader.ReadULeb128 (),
382                                         };
383
384                                         var list = new HeapObjectEvent.HeapObjectReference [(int) Reader.ReadULeb128 ()];
385
386                                         for (var i = 0; i < list.Length; i++) {
387                                                 list [i] = new HeapObjectEvent.HeapObjectReference {
388                                                         Offset = (long) Reader.ReadULeb128 (),
389                                                         ObjectPointer = ReadObject (),
390                                                 };
391                                         }
392
393                                         hoe.References = list;
394                                         ev = hoe;
395
396                                         break;
397                                 }
398
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 ()];
403
404                                         hre.MaxGenerationCollectionCount = (long) Reader.ReadULeb128 ();
405
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 (),
411                                                 };
412                                         }
413
414                                         hre.Roots = list;
415                                         ev = hre;
416
417                                         break;
418                                 }
419                                 default:
420                                         throw new LogException ($"Invalid extended event type ({extType}).");
421                                 }
422                                 break;
423                         case LogEventType.Sample:
424                                 switch (extType) {
425                                 case LogEventType.SampleHit:
426                                         if (StreamHeader.FormatVersion < 14) {
427                                                 // Read SampleType (always set to .Cycles) for versions < 14
428                                                 Reader.ReadByte ();
429                                         }
430                                         ev = new SampleHitEvent {
431                                                 ThreadId = ReadPointer (),
432                                                 UnmanagedBacktrace = ReadBacktrace (true, false),
433                                                 ManagedBacktrace = ReadBacktrace (true).Reverse ().ToArray (),
434                                         };
435                                         break;
436                                 case LogEventType.SampleUnmanagedSymbol:
437                                         ev = new UnmanagedSymbolEvent {
438                                                 CodePointer = ReadPointer (),
439                                                 CodeSize = (long) Reader.ReadULeb128 (),
440                                                 Name = Reader.ReadCString (),
441                                         };
442                                         break;
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 (),
449                                         };
450                                         break;
451                                 case LogEventType.SampleCounterDescriptions: {
452                                         var cde = new CounterDescriptionsEvent ();
453                                         var list = new CounterDescriptionsEvent.CounterDescription [(int) Reader.ReadULeb128 ()];
454
455                                         for (var i = 0; i < list.Length; i++) {
456                                                 var section = (LogCounterSection) Reader.ReadULeb128 ();
457
458                                                 list [i] = new CounterDescriptionsEvent.CounterDescription {
459                                                         Section = section,
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 (),
466                                                 };
467                                         }
468
469                                         cde.Descriptions = list;
470                                         ev = cde;
471
472                                         break;
473                                 }
474                                 case LogEventType.SampleCounters: {
475                                         var cse = new CounterSamplesEvent ();
476                                         var list = new List<CounterSamplesEvent.CounterSample> ();
477
478                                         while (true) {
479                                                 var index = (long) Reader.ReadULeb128 ();
480
481                                                 if (index == 0)
482                                                         break;
483
484                                                 var counterType = (LogCounterType) Reader.ReadByte ();
485
486                                                 object value = null;
487
488                                                 switch (counterType) {
489                                                 case LogCounterType.String:
490                                                         value = Reader.ReadByte () == 1 ? Reader.ReadCString () : null;
491                                                         break;
492                                                 case LogCounterType.Int32:
493                                                 case LogCounterType.Word:
494                                                 case LogCounterType.Int64:
495                                                 case LogCounterType.Interval:
496                                                         value = Reader.ReadSLeb128 ();
497                                                         break;
498                                                 case LogCounterType.UInt32:
499                                                 case LogCounterType.UInt64:
500                                                         value = Reader.ReadULeb128 ();
501                                                         break;
502                                                 case LogCounterType.Double:
503                                                         value = Reader.ReadDouble ();
504                                                         break;
505                                                 default:
506                                                         throw new LogException ($"Invalid counter type ({counterType}).");
507                                                 }
508
509                                                 list.Add (new CounterSamplesEvent.CounterSample {
510                                                         Index = index,
511                                                         Type = counterType,
512                                                         Value = value,
513                                                 });
514                                         }
515
516                                         cse.Samples = list;
517                                         ev = cse;
518
519                                         break;
520                                 }
521                                 default:
522                                         throw new LogException ($"Invalid extended event type ({extType}).");
523                                 }
524                                 break;
525                         case LogEventType.Runtime:
526                                 switch (extType) {
527                                 case LogEventType.RuntimeJitHelper: {
528                                         var helperType = (LogJitHelper) Reader.ReadByte ();
529
530                                         ev = new JitHelperEvent {
531                                                 Type = helperType,
532                                                 BufferPointer = ReadPointer (),
533                                                 BufferSize = (long) Reader.ReadULeb128 (),
534                                                 Name = helperType == LogJitHelper.SpecificTrampoline ? Reader.ReadCString () : string.Empty,
535                                         };
536                                         break;
537                                 }
538                                 default:
539                                         throw new LogException ($"Invalid extended event type ({extType}).");
540                                 }
541                                 break;
542                         case LogEventType.Meta:
543                                 switch (extType) {
544                                 case LogEventType.MetaSynchronizationPoint:
545                                         ev = new SynchronizationPointEvent {
546                                                 Type = (LogSynchronizationPoint) Reader.ReadByte (),
547                                         };
548                                         break;
549                                 default:
550                                         throw new LogException ($"Invalid extended event type ({extType}).");
551                                 }
552                                 break;
553                         default:
554                                 throw new LogException ($"Invalid basic event type ({basicType}).");
555                         }
556
557                         ev.Timestamp = _time;
558                         ev.Buffer = _bufferHeader;
559
560                         return ev;
561                 }
562
563                 long ReadPointer ()
564                 {
565                         var ptr = Reader.ReadSLeb128 () + _bufferHeader.PointerBase;
566
567                         return StreamHeader.PointerSize == sizeof (long) ? ptr : ptr & 0xffffffffL;
568                 }
569
570                 long ReadObject ()
571                 {
572                         return Reader.ReadSLeb128 () + _bufferHeader.ObjectBase << 3;
573                 }
574
575                 long ReadMethod ()
576                 {
577                         return _bufferHeader.CurrentMethod += Reader.ReadSLeb128 ();
578                 }
579
580                 ulong ReadTime ()
581                 {
582                         return _bufferHeader.CurrentTime += Reader.ReadULeb128 ();
583                 }
584
585                 IReadOnlyList<long> ReadBacktrace (bool actuallyRead, bool managed = true)
586                 {
587                         if (!actuallyRead)
588                                 return Array.Empty<long> ();
589
590                         var list = new long [(int) Reader.ReadULeb128 ()];
591
592                         for (var i = 0; i < list.Length; i++)
593                                 list [i] = managed ? ReadMethod () : ReadPointer ();
594
595                         return list;
596                 }
597         }
598 }