Merge pull request #5562 from alexrp/master
[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 = ReadPointer (),
234                                                         Name = Reader.ReadCString (),
235                                                 };
236                                         } else if (unload) {
237                                                 ev = new AssemblyUnloadEvent {
238                                                         AssemblyPointer = ReadPointer (),
239                                                         ImagePointer = ReadPointer (),
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 = ReadObject (),
340                                         };
341                                         break;
342                                 default:
343                                         throw new LogException ($"Invalid extended event type ({extType}).");
344                                 }
345                                 break;
346                         case LogEventType.Monitor:
347                                 switch (extType) {
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),
354                                         };
355                                         break;
356                                 default:
357                                         throw new LogException ($"Invalid extended event type ({extType}).");
358                                 }
359                                 break;
360                         case LogEventType.Heap:
361                                 switch (extType) {
362                                 case LogEventType.HeapBegin:
363                                         ev = new HeapBeginEvent ();
364                                         break;
365                                 case LogEventType.HeapEnd:
366                                         ev = new HeapEndEvent ();
367                                         break;
368                                 case LogEventType.HeapObject: {
369                                         HeapObjectEvent hoe = new HeapObjectEvent {
370                                                 ObjectPointer = ReadObject (),
371                                                 ClassPointer = ReadPointer (),
372                                                 ObjectSize = (long) Reader.ReadULeb128 (),
373                                         };
374
375                                         var list = new HeapObjectEvent.HeapObjectReference [(int) Reader.ReadULeb128 ()];
376
377                                         for (var i = 0; i < list.Length; i++) {
378                                                 list [i] = new HeapObjectEvent.HeapObjectReference {
379                                                         Offset = (long) Reader.ReadULeb128 (),
380                                                         ObjectPointer = ReadObject (),
381                                                 };
382                                         }
383
384                                         hoe.References = list;
385                                         ev = hoe;
386
387                                         break;
388                                 }
389
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 ()];
394
395                                         hre.MaxGenerationCollectionCount = (long) Reader.ReadULeb128 ();
396
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 (),
402                                                 };
403                                         }
404
405                                         hre.Roots = list;
406                                         ev = hre;
407
408                                         break;
409                                 }
410                                 default:
411                                         throw new LogException ($"Invalid extended event type ({extType}).");
412                                 }
413                                 break;
414                         case LogEventType.Sample:
415                                 switch (extType) {
416                                 case LogEventType.SampleHit:
417                                         ev = new SampleHitEvent {
418                                                 ThreadId = ReadPointer (),
419                                                 UnmanagedBacktrace = ReadBacktrace (true, false),
420                                                 ManagedBacktrace = ReadBacktrace (true),
421                                         };
422                                         break;
423                                 case LogEventType.SampleUnmanagedSymbol:
424                                         ev = new UnmanagedSymbolEvent {
425                                                 CodePointer = ReadPointer (),
426                                                 CodeSize = (long) Reader.ReadULeb128 (),
427                                                 Name = Reader.ReadCString (),
428                                         };
429                                         break;
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 (),
436                                         };
437                                         break;
438                                 case LogEventType.SampleCounterDescriptions: {
439                                         var cde = new CounterDescriptionsEvent ();
440                                         var list = new CounterDescriptionsEvent.CounterDescription [(int) Reader.ReadULeb128 ()];
441
442                                         for (var i = 0; i < list.Length; i++) {
443                                                 var section = (LogCounterSection) Reader.ReadULeb128 ();
444
445                                                 list [i] = new CounterDescriptionsEvent.CounterDescription {
446                                                         Section = section,
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 (),
453                                                 };
454                                         }
455
456                                         cde.Descriptions = list;
457                                         ev = cde;
458
459                                         break;
460                                 }
461                                 case LogEventType.SampleCounters: {
462                                         var cse = new CounterSamplesEvent ();
463                                         var list = new List<CounterSamplesEvent.CounterSample> ();
464
465                                         while (true) {
466                                                 var index = (long) Reader.ReadULeb128 ();
467
468                                                 if (index == 0)
469                                                         break;
470
471                                                 var counterType = (LogCounterType) Reader.ReadByte ();
472
473                                                 object value = null;
474
475                                                 switch (counterType) {
476                                                 case LogCounterType.String:
477                                                         value = Reader.ReadByte () == 1 ? Reader.ReadCString () : null;
478                                                         break;
479                                                 case LogCounterType.Int32:
480                                                 case LogCounterType.Word:
481                                                 case LogCounterType.Int64:
482                                                 case LogCounterType.Interval:
483                                                         value = Reader.ReadSLeb128 ();
484                                                         break;
485                                                 case LogCounterType.UInt32:
486                                                 case LogCounterType.UInt64:
487                                                         value = Reader.ReadULeb128 ();
488                                                         break;
489                                                 case LogCounterType.Double:
490                                                         value = Reader.ReadDouble ();
491                                                         break;
492                                                 default:
493                                                         throw new LogException ($"Invalid counter type ({counterType}).");
494                                                 }
495
496                                                 list.Add (new CounterSamplesEvent.CounterSample {
497                                                         Index = index,
498                                                         Type = counterType,
499                                                         Value = value,
500                                                 });
501                                         }
502
503                                         cse.Samples = list;
504                                         ev = cse;
505
506                                         break;
507                                 }
508                                 default:
509                                         throw new LogException ($"Invalid extended event type ({extType}).");
510                                 }
511                                 break;
512                         case LogEventType.Runtime:
513                                 switch (extType) {
514                                 case LogEventType.RuntimeJitHelper: {
515                                         var helperType = (LogJitHelper) Reader.ReadByte ();
516
517                                         ev = new JitHelperEvent {
518                                                 Type = helperType,
519                                                 BufferPointer = ReadPointer (),
520                                                 BufferSize = (long) Reader.ReadULeb128 (),
521                                                 Name = helperType == LogJitHelper.SpecificTrampoline ? Reader.ReadCString () : string.Empty,
522                                         };
523                                         break;
524                                 }
525                                 default:
526                                         throw new LogException ($"Invalid extended event type ({extType}).");
527                                 }
528                                 break;
529                         case LogEventType.Meta:
530                                 switch (extType) {
531                                 case LogEventType.MetaSynchronizationPoint:
532                                         ev = new SynchronizationPointEvent {
533                                                 Type = (LogSynchronizationPoint) Reader.ReadByte (),
534                                         };
535                                         break;
536                                 default:
537                                         throw new LogException ($"Invalid extended event type ({extType}).");
538                                 }
539                                 break;
540                         default:
541                                 throw new LogException ($"Invalid basic event type ({basicType}).");
542                         }
543
544                         ev.Timestamp = _time;
545                         ev.Buffer = _bufferHeader;
546
547                         return ev;
548                 }
549
550                 long ReadPointer ()
551                 {
552                         var ptr = Reader.ReadSLeb128 () + _bufferHeader.PointerBase;
553
554                         return StreamHeader.PointerSize == sizeof (long) ? ptr : ptr & 0xffffffffL;
555                 }
556
557                 long ReadObject ()
558                 {
559                         return Reader.ReadSLeb128 () + _bufferHeader.ObjectBase << 3;
560                 }
561
562                 long ReadMethod ()
563                 {
564                         return _bufferHeader.CurrentMethod += Reader.ReadSLeb128 ();
565                 }
566
567                 ulong ReadTime ()
568                 {
569                         return _bufferHeader.CurrentTime += Reader.ReadULeb128 ();
570                 }
571
572                 IReadOnlyList<long> ReadBacktrace (bool actuallyRead, bool managed = true)
573                 {
574                         if (!actuallyRead)
575                                 return Array.Empty<long> ();
576
577                         var list = new long [(int) Reader.ReadULeb128 ()];
578
579                         for (var i = 0; i < list.Length; i++)
580                                 list [i] = managed ? ReadMethod () : ReadPointer ();
581
582                         return list;
583                 }
584         }
585 }