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