[Mono.Profiler.Log] Read root type field as uleb128.
[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
106                         switch (basicType) {
107                         case LogEventType.Allocation:
108                                 switch (extType) {
109                                 case LogEventType.AllocationBacktrace:
110                                 case LogEventType.AllocationNoBacktrace:
111                                         return new AllocationEvent {
112                                                 ClassPointer = ReadPointer (),
113                                                 ObjectPointer = ReadObject (),
114                                                 ObjectSize = (long) Reader.ReadULeb128 (),
115                                                 Backtrace = ReadBacktrace (extType == LogEventType.AllocationBacktrace),
116                                         };
117                                 default:
118                                         throw new LogException ($"Invalid extended event type ({extType}).");
119                                 }
120                         case LogEventType.GC:
121                                 switch (extType) {
122                                 case LogEventType.GCEvent:
123                                         return new GCEvent {
124                                                 Type = (LogGCEvent) Reader.ReadByte (),
125                                                 Generation = Reader.ReadByte (),
126                                         };
127                                 case LogEventType.GCResize:
128                                         return new GCResizeEvent {
129                                                 NewSize = (long) Reader.ReadULeb128 (),
130                                         };
131                                 case LogEventType.GCMove: {
132                                         var list = new long [(int) Reader.ReadULeb128 ()];
133
134                                         for (var i = 0; i < list.Length; i++)
135                                                 list [i] = ReadObject ();
136
137                                         return new GCMoveEvent {
138                                                 OldObjectPointers = list.Where ((_, i) => i % 2 == 0).ToArray (),
139                                                 NewObjectPointers = list.Where ((_, i) => i % 2 != 0).ToArray (),
140                                         };
141                                 }
142                                 case LogEventType.GCHandleCreationNoBacktrace:
143                                 case LogEventType.GCHandleCreationBacktrace:
144                                         return new GCHandleCreationEvent {
145                                                 Type = (LogGCHandleType) Reader.ReadULeb128 (),
146                                                 Handle = (long) Reader.ReadULeb128 (),
147                                                 ObjectPointer = ReadObject (),
148                                                 Backtrace = ReadBacktrace (extType == LogEventType.GCHandleCreationBacktrace),
149                                         };
150                                 case LogEventType.GCHandleDeletionNoBacktrace:
151                                 case LogEventType.GCHandleDeletionBacktrace:
152                                         return new GCHandleDeletionEvent {
153                                                 Type = (LogGCHandleType) Reader.ReadULeb128 (),
154                                                 Handle = (long) Reader.ReadULeb128 (),
155                                                 Backtrace = ReadBacktrace (extType == LogEventType.GCHandleDeletionBacktrace),
156                                         };
157                                 case LogEventType.GCFinalizeBegin:
158                                         return new GCFinalizeBeginEvent ();
159                                 case LogEventType.GCFinalizeEnd:
160                                         return new GCFinalizeEndEvent ();
161                                 case LogEventType.GCFinalizeObjectBegin:
162                                         return new GCFinalizeObjectBeginEvent {
163                                                 ObjectPointer = ReadObject (),
164                                         };
165                                 case LogEventType.GCFinalizeObjectEnd:
166                                         return new GCFinalizeObjectEndEvent {
167                                                 ObjectPointer = ReadObject (),
168                                         };
169                                 default:
170                                         throw new LogException ($"Invalid extended event type ({extType}).");
171                                 }
172                         case LogEventType.Metadata: {
173                                 var load = false;
174                                 var unload = false;
175
176                                 switch (extType) {
177                                 case LogEventType.MetadataExtra:
178                                         break;
179                                 case LogEventType.MetadataEndLoad:
180                                         load = true;
181                                         break;
182                                 case LogEventType.MetadataEndUnload:
183                                         unload = true;
184                                         break;
185                                 default:
186                                         throw new LogException ($"Invalid extended event type ({extType}).");
187                                 }
188
189                                 var metadataType = (LogMetadataType) Reader.ReadByte ();
190
191                                 switch (metadataType) {
192                                 case LogMetadataType.Class:
193                                         if (load) {
194                                                 return new ClassLoadEvent {
195                                                         ClassPointer = ReadPointer (),
196                                                         ImagePointer = ReadPointer (),
197                                                         Name = Reader.ReadCString (),
198                                                 };
199                                         } else
200                                                 throw new LogException ("Invalid class metadata event.");
201                                 case LogMetadataType.Image:
202                                         if (load) {
203                                                 return new ImageLoadEvent {
204                                                         ImagePointer = ReadPointer (),
205                                                         Name = Reader.ReadCString (),
206                                                 };
207                                         } else if (unload) {
208                                                 return new ImageUnloadEvent {
209                                                         ImagePointer = ReadPointer (),
210                                                         Name = Reader.ReadCString (),
211                                                 };
212                                         } else
213                                                 throw new LogException ("Invalid image metadata event.");
214                                 case LogMetadataType.Assembly:
215                                         if (load) {
216                                                 return new AssemblyLoadEvent {
217                                                         AssemblyPointer = ReadPointer (),
218                                                         ImagePointer = ReadPointer (),
219                                                         Name = Reader.ReadCString (),
220                                                 };
221                                         } else if (unload) {
222                                                 return new AssemblyUnloadEvent {
223                                                         AssemblyPointer = ReadPointer (),
224                                                         ImagePointer = ReadPointer (),
225                                                         Name = Reader.ReadCString (),
226                                                 };
227                                         } else
228                                                 throw new LogException ("Invalid assembly metadata event.");
229                                 case LogMetadataType.AppDomain:
230                                         if (load) {
231                                                 return new AppDomainLoadEvent {
232                                                         AppDomainId = ReadPointer (),
233                                                 };
234                                         } else if (unload) {
235                                                 return new AppDomainUnloadEvent {
236                                                         AppDomainId = ReadPointer (),
237                                                 };
238                                         } else {
239                                                 return new AppDomainNameEvent {
240                                                         AppDomainId = ReadPointer (),
241                                                         Name = Reader.ReadCString (),
242                                                 };
243                                         }
244                                 case LogMetadataType.Thread:
245                                         if (load) {
246                                                 return new ThreadStartEvent {
247                                                         ThreadId = ReadPointer (),
248                                                 };
249                                         } else if (unload) {
250                                                 return new ThreadEndEvent {
251                                                         ThreadId = ReadPointer (),
252                                                 };
253                                         } else {
254                                                 return new ThreadNameEvent {
255                                                         ThreadId = ReadPointer (),
256                                                         Name = Reader.ReadCString (),
257                                                 };
258                                         }
259                                 case LogMetadataType.Context:
260                                         if (load) {
261                                                 return new ContextLoadEvent {
262                                                         ContextId = ReadPointer (),
263                                                         AppDomainId = ReadPointer (),
264                                                 };
265                                         } else if (unload) {
266                                                 return new ContextUnloadEvent {
267                                                         ContextId = ReadPointer (),
268                                                         AppDomainId = ReadPointer (),
269                                                 };
270                                         } else
271                                                 throw new LogException ("Invalid context metadata event.");
272                                 default:
273                                         throw new LogException ($"Invalid metadata type ({metadataType}).");
274                                 }
275                         }
276                         case LogEventType.Method:
277                                 switch (extType) {
278                                 case LogEventType.MethodLeave:
279                                         return new LeaveEvent {
280                                                 MethodPointer = ReadMethod (),
281                                         };
282                                 case LogEventType.MethodEnter:
283                                         return new EnterEvent {
284                                                 MethodPointer = ReadMethod (),
285                                         };
286                                 case LogEventType.MethodLeaveExceptional:
287                                         return new ExceptionalLeaveEvent {
288                                                 MethodPointer = ReadMethod (),
289                                         };
290                                 case LogEventType.MethodJit:
291                                         return new JitEvent {
292                                                 MethodPointer = ReadMethod (),
293                                                 CodePointer = ReadPointer (),
294                                                 CodeSize = (long) Reader.ReadULeb128 (),
295                                                 Name = Reader.ReadCString (),
296                                         };
297                                 default:
298                                         throw new LogException ($"Invalid extended event type ({extType}).");
299                                 }
300                         case LogEventType.Exception:
301                                 switch (extType) {
302                                 case LogEventType.ExceptionThrowNoBacktrace:
303                                 case LogEventType.ExceptionThrowBacktrace:
304                                         return new ThrowEvent {
305                                                 ObjectPointer = ReadObject (),
306                                                 Backtrace = ReadBacktrace (extType == LogEventType.ExceptionThrowBacktrace),
307                                         };
308                                 case LogEventType.ExceptionClause:
309                                         return new ExceptionClauseEvent {
310                                                 Type = (LogExceptionClause) Reader.ReadByte (),
311                                                 Index = (long) Reader.ReadULeb128 (),
312                                                 MethodPointer = ReadMethod (),
313                                                 ObjectPointer = ReadObject (),
314                                         };
315                                 default:
316                                         throw new LogException ($"Invalid extended event type ({extType}).");
317                                 }
318                         case LogEventType.Monitor:
319                                 switch (extType) {
320                                 case LogEventType.MonitorNoBacktrace:
321                                 case LogEventType.MonitorBacktrace:
322                                         return new MonitorEvent {
323                                                 Event = (LogMonitorEvent) Reader.ReadByte (),
324                                                 ObjectPointer = ReadObject (),
325                                                 Backtrace = ReadBacktrace (extType == LogEventType.MonitorBacktrace),
326                                         };
327                                 default:
328                                         throw new LogException ($"Invalid extended event type ({extType}).");
329                                 }
330                         case LogEventType.Heap:
331                                 switch (extType) {
332                                 case LogEventType.HeapBegin:
333                                         return new HeapBeginEvent ();
334                                 case LogEventType.HeapEnd:
335                                         return new HeapEndEvent ();
336                                 case LogEventType.HeapObject: {
337                                         var ev = new HeapObjectEvent {
338                                                 ObjectPointer = ReadObject (),
339                                                 ClassPointer = ReadPointer (),
340                                                 ObjectSize = (long) Reader.ReadULeb128 (),
341                                         };
342
343                                         var list = new HeapObjectEvent.HeapObjectReference [(int) Reader.ReadULeb128 ()];
344
345                                         for (var i = 0; i < list.Length; i++) {
346                                                 list [i] = new HeapObjectEvent.HeapObjectReference {
347                                                         Offset = (long) Reader.ReadULeb128 (),
348                                                         ObjectPointer = ReadObject (),
349                                                 };
350                                         }
351
352                                         ev.References = list;
353
354                                         return ev;
355                                 }
356                                 case LogEventType.HeapRoots: {
357                                         // TODO: This entire event makes no sense.
358
359                                         var ev = new HeapRootsEvent ();
360                                         var list = new HeapRootsEvent.HeapRoot [(int) Reader.ReadULeb128 ()];
361
362                                         ev.MaxGenerationCollectionCount = (long) Reader.ReadULeb128 ();
363
364                                         for (var i = 0; i < list.Length; i++) {
365                                                 list [i] = new HeapRootsEvent.HeapRoot {
366                                                         ObjectPointer = ReadObject (),
367                                                         Attributes = (LogHeapRootAttributes) Reader.ReadULeb128 (),
368                                                         ExtraInfo = (long) Reader.ReadULeb128 (),
369                                                 };
370                                         }
371
372                                         ev.Roots = list;
373
374                                         return ev;
375                                 }
376                                 default:
377                                         throw new LogException ($"Invalid extended event type ({extType}).");
378                                 }
379                         case LogEventType.Sample:
380                                 switch (extType) {
381                                 case LogEventType.SampleHit:
382                                         return new SampleHitEvent {
383                                                 ThreadId = ReadPointer (),
384                                                 UnmanagedBacktrace = ReadBacktrace (true, false),
385                                                 ManagedBacktrace = ReadBacktrace (true),
386                                         };
387                                 case LogEventType.SampleUnmanagedSymbol:
388                                         return new UnmanagedSymbolEvent {
389                                                 CodePointer = ReadPointer (),
390                                                 CodeSize = (long) Reader.ReadULeb128 (),
391                                                 Name = Reader.ReadCString (),
392                                         };
393                                 case LogEventType.SampleUnmanagedBinary:
394                                         return new UnmanagedBinaryEvent {
395                                                 SegmentPointer = ReadPointer (),
396                                                 SegmentOffset = (long) Reader.ReadULeb128 (),
397                                                 SegmentSize = (long) Reader.ReadULeb128 (),
398                                                 FileName = Reader.ReadCString (),
399                                         };
400                                 case LogEventType.SampleCounterDescriptions: {
401                                         var ev = new CounterDescriptionsEvent ();
402                                         var list = new CounterDescriptionsEvent.CounterDescription [(int) Reader.ReadULeb128 ()];
403
404                                         for (var i = 0; i < list.Length; i++) {
405                                                 var section = (LogCounterSection) Reader.ReadULeb128 ();
406
407                                                 list [i] = new CounterDescriptionsEvent.CounterDescription {
408                                                         Section = section,
409                                                         SectionName = section == LogCounterSection.User ? Reader.ReadCString () : string.Empty,
410                                                         CounterName = Reader.ReadCString (),
411                                                         Type = (LogCounterType) Reader.ReadByte (),
412                                                         Unit = (LogCounterUnit) Reader.ReadByte (),
413                                                         Variance = (LogCounterVariance) Reader.ReadByte (),
414                                                         Index = (long) Reader.ReadULeb128 (),
415                                                 };
416                                         }
417
418                                         ev.Descriptions = list;
419
420                                         return ev;
421                                 }
422                                 case LogEventType.SampleCounters: {
423                                         var ev = new CounterSamplesEvent ();
424                                         var list = new List<CounterSamplesEvent.CounterSample> ();
425
426                                         while (true) {
427                                                 var index = (long) Reader.ReadULeb128 ();
428
429                                                 if (index == 0)
430                                                         break;
431
432                                                 var counterType = (LogCounterType) Reader.ReadByte ();
433
434                                                 object value = null;
435
436                                                 switch (counterType) {
437                                                 case LogCounterType.String:
438                                                         value = Reader.ReadByte () == 1 ? Reader.ReadCString () : null;
439                                                         break;
440                                                 case LogCounterType.Int32:
441                                                 case LogCounterType.Word:
442                                                 case LogCounterType.Int64:
443                                                 case LogCounterType.Interval:
444                                                         value = Reader.ReadSLeb128 ();
445                                                         break;
446                                                 case LogCounterType.UInt32:
447                                                 case LogCounterType.UInt64:
448                                                         value = Reader.ReadULeb128 ();
449                                                         break;
450                                                 case LogCounterType.Double:
451                                                         value = Reader.ReadDouble ();
452                                                         break;
453                                                 default:
454                                                         throw new LogException ($"Invalid counter type ({counterType}).");
455                                                 }
456
457                                                 list.Add (new CounterSamplesEvent.CounterSample {
458                                                         Index = index,
459                                                         Type = counterType,
460                                                         Value = value,
461                                                 });
462                                         }
463
464                                         ev.Samples = list;
465
466                                         return ev;
467                                 }
468                                 default:
469                                         throw new LogException ($"Invalid extended event type ({extType}).");
470                                 }
471                         case LogEventType.Runtime:
472                                 switch (extType) {
473                                 case LogEventType.RuntimeJitHelper: {
474                                         var helperType = (LogJitHelper) Reader.ReadByte ();
475
476                                         return new JitHelperEvent {
477                                                 Type = helperType,
478                                                 BufferPointer = ReadPointer (),
479                                                 BufferSize = (long) Reader.ReadULeb128 (),
480                                                 Name = helperType == LogJitHelper.SpecificTrampoline ? Reader.ReadCString () : string.Empty,
481                                         };
482                                 }
483                                 default:
484                                         throw new LogException ($"Invalid extended event type ({extType}).");
485                                 }
486                         case LogEventType.Meta:
487                                 switch (extType) {
488                                 case LogEventType.MetaSynchronizationPoint:
489                                         return new SynchronizationPointEvent {
490                                                 Type = (LogSynchronizationPoint) Reader.ReadByte (),
491                                         };
492                                 default:
493                                         throw new LogException ($"Invalid extended event type ({extType}).");
494                                 }
495                         default:
496                                 throw new LogException ($"Invalid basic event type ({basicType}).");
497                         }
498                 }
499
500                 long ReadPointer ()
501                 {
502                         var ptr = Reader.ReadSLeb128 () + _bufferHeader.PointerBase;
503
504                         return StreamHeader.PointerSize == sizeof (long) ? ptr : ptr & 0xffffffffL;
505                 }
506
507                 long ReadObject ()
508                 {
509                         return Reader.ReadSLeb128 () + _bufferHeader.ObjectBase << 3;
510                 }
511
512                 long ReadMethod ()
513                 {
514                         return _bufferHeader.CurrentMethod += Reader.ReadSLeb128 ();
515                 }
516
517                 ulong ReadTime ()
518                 {
519                         return _bufferHeader.CurrentTime += Reader.ReadULeb128 ();
520                 }
521
522                 IReadOnlyList<long> ReadBacktrace (bool actuallyRead, bool managed = true)
523                 {
524                         if (!actuallyRead)
525                                 return Array.Empty<long> ();
526
527                         var list = new long [(int) Reader.ReadULeb128 ()];
528
529                         for (var i = 0; i < list.Length; i++)
530                                 list [i] = managed ? ReadMethod () : ReadPointer ();
531
532                         return list;
533                 }
534         }
535 }