[profiler] Introduce the Mono.Profiler.Log library. API still unstable.
authorAlex Rønne Petersen <alexrp@xamarin.com>
Mon, 12 Jun 2017 20:53:56 +0000 (22:53 +0200)
committerAlex Rønne Petersen <alexrp@xamarin.com>
Mon, 19 Jun 2017 22:47:52 +0000 (00:47 +0200)
16 files changed:
mcs/Makefile
mcs/class/Makefile
mcs/class/Mono.Profiler.Log/Assembly/AssemblyInfo.cs [new file with mode: 0644]
mcs/class/Mono.Profiler.Log/Makefile [new file with mode: 0644]
mcs/class/Mono.Profiler.Log/Mono.Profiler.Log.dll.sources [new file with mode: 0644]
mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogBufferHeader.cs [new file with mode: 0644]
mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEnums.cs [new file with mode: 0644]
mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvent.cs [new file with mode: 0644]
mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEventVisitor.cs [new file with mode: 0644]
mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvents.cs [new file with mode: 0644]
mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogException.cs [new file with mode: 0644]
mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs [new file with mode: 0644]
mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogReader.cs [new file with mode: 0644]
mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogRuntimeProfiler.cs [new file with mode: 0644]
mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogStream.cs [new file with mode: 0644]
mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogStreamHeader.cs [new file with mode: 0644]

index d04525bd34fc2a4b5a4425082b6f53cf7258cfc2..1d76f3fc0e1c04a086b67807c0bd670b7ca1d130 100644 (file)
@@ -142,7 +142,7 @@ distcheck: dist-tarball
 MONO_API_SNAPSHOT_PATH := $(topdir)../external/api-snapshot/
 GENAPI := $(MONO_API_SNAPSHOT_PATH)tools/genapi/GenAPI.exe
 MONO_API_SNAPSHOT_PROFILE_PATH := $(MONO_API_SNAPSHOT_PATH)profiles/$(PROFILE)/
-MONO_API_ASSEMBLIES_IGNORED := $(addprefix $(topdir)class/lib/$(PROFILE)/, Mono.CSharp.dll SystemWebTestShim.dll standalone-runner-support.dll nunit.core.dll nunit.core.extensions.dll nunit.core.interfaces.dll nunit.framework.dll nunit.framework.extensions.dll nunit.mocks.dll nunit.util.dll nunit-console-runner.dll nunitlite.dll)
+MONO_API_ASSEMBLIES_IGNORED := $(addprefix $(topdir)class/lib/$(PROFILE)/, Mono.CSharp.dll SystemWebTestShim.dll standalone-runner-support.dll nunit.core.dll nunit.core.extensions.dll nunit.core.interfaces.dll nunit.framework.dll nunit.framework.extensions.dll nunit.mocks.dll nunit.util.dll nunit-console-runner.dll nunitlite.dll Mono.Profiler.Log.dll)
 MONO_API_ASSEMBLIES := $(filter-out $(MONO_API_ASSEMBLIES_IGNORED), $(wildcard $(topdir)class/lib/$(PROFILE)/*.dll)) $(wildcard $(topdir)class/lib/$(PROFILE)/Facades/*.dll)
 MONO_API_ASSEMBLIES_CS := $(MONO_API_ASSEMBLIES:$(topdir)class/lib/$(PROFILE)/%.dll=$(MONO_API_SNAPSHOT_PROFILE_PATH)%.cs)
 
index eb3b7b09ce2ca22311f0524732326e4cff6deb7a..56e2faaf458ce3917d6167b2234bd7fd6baaeacc 100644 (file)
@@ -315,6 +315,7 @@ net_4_x_parallel_dirs := \
        System.Workflow.ComponentModel \
        System.Workflow.Runtime \
        System.Reflection.Context \
+       Mono.Profiler.Log       \
        legacy/Mono.Cecil \
        $(pcl_facade_dirs)
 
diff --git a/mcs/class/Mono.Profiler.Log/Assembly/AssemblyInfo.cs b/mcs/class/Mono.Profiler.Log/Assembly/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..8d62855
--- /dev/null
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Reflection;
+using System.Resources;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle ("Mono.Profiler.Log")]
+[assembly: AssemblyDescription ("Mono Log Profiler API Library")]
+
+[assembly: AssemblyCompany (Consts.MonoCompany)]
+[assembly: AssemblyProduct (Consts.MonoProduct)]
+[assembly: AssemblyCopyright (Consts.MonoCopyright)]
+
+[assembly: AssemblyVersion (Consts.FxVersion)]
+[assembly: SatelliteContractVersion (Consts.FxVersion)]
+[assembly: AssemblyInformationalVersion (Consts.FxFileVersion)]
+[assembly: AssemblyFileVersion (Consts.FxFileVersion)]
+
+[assembly: ComVisible (false)]
diff --git a/mcs/class/Mono.Profiler.Log/Makefile b/mcs/class/Mono.Profiler.Log/Makefile
new file mode 100644 (file)
index 0000000..1f860ab
--- /dev/null
@@ -0,0 +1,12 @@
+thisdir = class/Mono.Profiler.Log
+include ../../build/rules.make
+
+LIBRARY = Mono.Profiler.Log.dll
+LIBRARY_SNK = ../mono.snk
+
+LIB_REFS = System System.Core
+LIB_MCS_FLAGS = /unsafe /keyfile:$(LIBRARY_SNK) /publicsign
+
+NO_TEST = yes
+
+include ../../build/library.make
diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log.dll.sources b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log.dll.sources
new file mode 100644 (file)
index 0000000..bfb0fea
--- /dev/null
@@ -0,0 +1,14 @@
+Assembly/AssemblyInfo.cs
+../../build/common/Consts.cs
+../../build/common/Locale.cs
+Mono.Profiler.Log/LogBufferHeader.cs
+Mono.Profiler.Log/LogEnums.cs
+Mono.Profiler.Log/LogEvent.cs
+Mono.Profiler.Log/LogEventVisitor.cs
+Mono.Profiler.Log/LogEvents.cs
+Mono.Profiler.Log/LogException.cs
+Mono.Profiler.Log/LogProcessor.cs
+Mono.Profiler.Log/LogReader.cs
+Mono.Profiler.Log/LogRuntimeProfiler.cs
+Mono.Profiler.Log/LogStream.cs
+Mono.Profiler.Log/LogStreamHeader.cs
diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogBufferHeader.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogBufferHeader.cs
new file mode 100644 (file)
index 0000000..149a6f3
--- /dev/null
@@ -0,0 +1,46 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Mono.Profiler.Log {
+
+       public sealed class LogBufferHeader {
+
+               const int Id = 0x4d504c01;
+
+               public LogStreamHeader StreamHeader { get; }
+
+               public int Length { get; }
+
+               public ulong TimeBase { get; }
+
+               public long PointerBase { get; }
+
+               public long ObjectBase { get; }
+
+               public long ThreadId { get; }
+
+               public long MethodBase { get; }
+
+               internal ulong CurrentTime { get; set; }
+
+               internal long CurrentMethod { get; set; }
+
+               internal LogBufferHeader (LogStreamHeader streamHeader, LogReader reader)
+               {
+                       StreamHeader = streamHeader;
+
+                       var id = reader.ReadInt32 ();
+
+                       if (id != Id)
+                               throw new LogException ($"Invalid buffer header ID (0x{id:X}).");
+
+                       Length = reader.ReadInt32 ();
+                       TimeBase = CurrentTime = reader.ReadUInt64 ();
+                       PointerBase = reader.ReadInt64 ();
+                       ObjectBase = reader.ReadInt64 ();
+                       ThreadId = reader.ReadInt64 ();
+                       MethodBase = CurrentMethod = reader.ReadInt64 ();
+               }
+       }
+}
diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEnums.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEnums.cs
new file mode 100644 (file)
index 0000000..5238596
--- /dev/null
@@ -0,0 +1,212 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Mono.Profiler.Log {
+
+       // mono/profiler/log.h : TYPE_*
+       enum LogEventType {
+               Allocation = 0,
+               GC = 1,
+               Metadata = 2,
+               Method = 3,
+               Exception = 4,
+               Monitor = 5,
+               Heap = 6,
+               Sample = 7,
+               Runtime = 8,
+               Coverage = 9,
+               Meta = 10,
+
+               AllocationNoBacktrace = 0 << 4,
+               AllocationBacktrace = 1 << 4,
+
+               GCEvent = 1 << 4,
+               GCResize = 2 << 4,
+               GCMove = 3 << 4,
+               GCHandleCreationNoBacktrace = 4 << 4,
+               GCHandleDeletionNoBacktrace = 5 << 4,
+               GCHandleCreationBacktrace = 6 << 4,
+               GCHandleDeletionBacktrace = 7 << 4,
+               GCFinalizeBegin = 8 << 4,
+               GCFinalizeEnd = 9 << 4,
+               GCFinalizeObjectBegin = 10 << 4,
+               GCFinalizeObjectEnd = 11 << 4,
+
+               MetadataExtra = 0 << 4,
+               MetadataEndLoad = 2 << 4,
+               MetadataEndUnload = 4 << 4,
+
+               MethodLeave = 1 << 4,
+               MethodEnter = 2 << 4,
+               MethodLeaveExceptional = 3 << 4,
+               MethodJit = 4 << 4,
+
+               ExceptionThrowNoBacktrace = 0 << 7,
+               ExceptionThrowBacktrace = 1 << 7,
+               ExceptionClause = 1 << 4,
+
+               MonitorNoBacktrace = 0 << 7,
+               MonitorBacktrace = 1 << 7,
+
+               HeapBegin = 0 << 4,
+               HeapEnd = 1 << 4,
+               HeapObject = 2 << 4,
+               HeapRoots = 3 << 4,
+
+               SampleHit = 0 << 4,
+               SampleUnmanagedSymbol = 1 << 4,
+               SampleUnmanagedBinary = 2 << 4,
+               SampleCounterDescriptions = 3 << 4,
+               SampleCounters = 4 << 4,
+
+               RuntimeJitHelper = 1 << 4,
+
+               CoverageAssembly = 0 << 4,
+               CoverageMethod = 1 << 4,
+               CoverageStatement = 2 << 4,
+               CoverageClass = 3 << 4,
+
+               MetaSynchronizationPoint = 0 << 4,
+       }
+
+       // mono/profiler/log.h : TYPE_*
+       enum LogMetadataType {
+               Class = 1,
+               Image = 2,
+               Assembly = 3,
+               AppDomain = 4,
+               Thread = 5,
+               Context = 6,
+       }
+
+       // mono/utils/mono-counters.h : MONO_COUNTER_*
+       public enum LogCounterType {
+               Int32 = 0,
+               UInt32 = 1,
+               Word = 2,
+               Int64 = 3,
+               UInt64 = 4,
+               Double = 5,
+               String = 6,
+               Interval = 7,
+       }
+
+       // mono/utils/mono-counters.h : MONO_COUNTER_*
+       public enum LogCounterSection {
+               Jit = 1 << 8,
+               GC = 1 << 9,
+               Metadata = 1 << 10,
+               Generics = 1 << 11,
+               Security = 1 << 12,
+               Runtime = 1 << 13,
+               System = 1 << 14,
+               User = 1 << 15,
+               Profiler = 1 << 16,
+       }
+
+       // mono/utils/mono-counters.h : MONO_COUNTER_*
+       public enum LogCounterUnit {
+               Raw = 0 << 24,
+               Bytes = 1 << 24,
+               Time = 2 << 24,
+               Count = 3 << 24,
+               Percentage = 4 << 24,
+       }
+
+       // mono/utils/mono-counters.h : MONO_COUNTER_*
+       public enum LogCounterVariance {
+               Monotonic = 1 << 28,
+               Constant = 1 << 29,
+               Variable = 1 << 30,
+       }
+
+       // mono/metadata/profiler.h : MonoProfilerCodeBufferType
+       public enum LogJitHelper {
+               Unknown = 0,
+               Method = 1,
+               MethodTrampoline = 2,
+               UnboxTrampoline = 3,
+               ImtTrampoline = 4,
+               GenericsTrampoline = 5,
+               SpecificTrampoline = 6,
+               Helper = 7,
+               Monitor = 8,
+               DelegateInvoke = 9,
+               ExceptionHandling = 10,
+       }
+
+       // mono/profiler/log.h : SAMPLE_*
+       public enum LogSampleHitType {
+               Cycles = 1,
+               Instructions = 2,
+               CacheMisses = 3,
+               CacheHits = 4,
+               Branches = 5,
+               BranchMisses = 6,
+       }
+
+       // mono/metadata/profiler.h : MonoProfileGCRootType
+       [Flags]
+       public enum LogHeapRootAttributes {
+               Pinning = 1 << 8,
+               WeakReference = 2 << 8,
+               Interior = 4 << 8,
+
+               Stack = 0,
+               Finalizer = 1,
+               Handle = 2,
+               Other = 3,
+               Miscellaneous = 4,
+
+               TypeMask = 0xff,
+       }
+
+       // mono/metadata/profiler.h : MonoProfilerMonitorEvent
+       public enum LogMonitorEvent {
+               Contention = 1,
+               Done = 2,
+               Fail = 3,
+       }
+
+       // mono/metadata/metadata.h : MonoExceptionEnum
+       public enum LogExceptionClause {
+               Catch = 0,
+               Filter = 1,
+               Finally = 2,
+               Fault = 4,
+       }
+
+       // mono/metadata/profiler.h : MonoGCEvent
+       public enum LogGCEvent {
+               Begin = 0,
+               MarkBegin = 1,
+               MarkEnd = 2,
+               ReclaimBegin = 3,
+               ReclaimEnd = 4,
+               End = 5,
+               PreStopWorld = 6,
+               PostStopWorld = 7,
+               PreStartWorld = 8,
+               PostStartWorld = 9,
+               PreStopWorldLocked = 10,
+               PostStartWorldUnlocked = 11,
+       }
+
+       // mono/sgen/gc-internal-agnostic.h : GCHandleType
+       public enum LogGCHandleType {
+               Weak = 0,
+               WeakTrackResurrection = 1,
+               Normal = 2,
+               Pinned = 3,
+       }
+
+       // mono/profiler/log.h : MonoProfilerSyncPointType
+       public enum LogSynchronizationPoint {
+               Periodic = 0,
+               WorldStop = 1,
+               WorldStart = 2,
+       }
+}
diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvent.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvent.cs
new file mode 100644 (file)
index 0000000..759a1e6
--- /dev/null
@@ -0,0 +1,75 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+namespace Mono.Profiler.Log {
+
+       public abstract class LogEvent {
+
+               const BindingFlags PropertyFlags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public;
+
+               const string Indent = "  ";
+
+               internal LogEvent ()
+               {
+               }
+
+               public LogBufferHeader Buffer { get; internal set; }
+
+               public ulong Timestamp { get; internal set; }
+
+               public override string ToString ()
+               {
+                       var sb = new StringBuilder ();
+
+                       ToString (this, sb, string.Empty, GetType ().Name, 0);
+
+                       return sb.ToString ();
+               }
+
+               static void ToString (object source, StringBuilder result, string indent, string header, int level)
+               {
+                       result.AppendLine ($"{indent}{header} {{");
+
+                       foreach (var prop in source.GetType ().GetProperties (PropertyFlags).OrderBy (p => p.MetadataToken)) {
+                               var name = prop.Name;
+                               var propIndent = indent + Indent;
+                               var value = prop.GetValue (source);
+
+                               if (value is IList list) {
+                                       result.AppendLine ($"{propIndent}{name} = [{list.Count}] {{");
+
+                                       for (var i = 0; i < list.Count; i++) {
+                                               var elem = list [i];
+                                               var type = elem.GetType ();
+                                               var elemIndent = propIndent + Indent;
+                                               var elemHeader = $"[{i}] = ";
+
+                                               if (type.IsClass && type != typeof (string))
+                                                       ToString (elem, result, elemIndent, $"{elemHeader}{type.Name}", level + 1);
+                                               else
+                                                       result.AppendLine ($"{elemIndent}{elemHeader}{elem}");
+                                       }
+
+                                       result.AppendLine ($"{propIndent}}}");
+                               } else
+                                       result.AppendLine ($"{propIndent}{name} = {value}");
+                       }
+
+                       var end = $"{indent}}}";
+
+                       if (level == 0)
+                               result.Append (end);
+                       else
+                               result.AppendLine (end);
+               }
+
+               internal abstract void Accept (LogEventVisitor visitor);
+       }
+}
diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEventVisitor.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEventVisitor.cs
new file mode 100644 (file)
index 0000000..129a128
--- /dev/null
@@ -0,0 +1,197 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Mono.Profiler.Log {
+
+       public abstract class LogEventVisitor {
+
+               public virtual void VisitBefore (LogEvent ev)
+               {
+               }
+
+               public virtual void VisitAfter (LogEvent ev)
+               {
+               }
+
+               public virtual void Visit (AppDomainLoadEvent ev)
+               {
+               }
+
+               public virtual void Visit (AppDomainUnloadEvent ev)
+               {
+               }
+
+               public virtual void Visit (AppDomainNameEvent ev)
+               {
+               }
+
+               public virtual void Visit (ContextLoadEvent ev)
+               {
+               }
+
+               public virtual void Visit (ContextUnloadEvent ev)
+               {
+               }
+
+               public virtual void Visit (ThreadStartEvent ev)
+               {
+               }
+
+               public virtual void Visit (ThreadEndEvent ev)
+               {
+               }
+
+               public virtual void Visit (ThreadNameEvent ev)
+               {
+               }
+
+               public virtual void Visit (ImageLoadEvent ev)
+               {
+               }
+
+               public virtual void Visit (ImageUnloadEvent ev)
+               {
+               }
+
+               public virtual void Visit (AssemblyLoadEvent ev)
+               {
+               }
+
+               public virtual void Visit (AssemblyUnloadEvent ev)
+               {
+               }
+
+               public virtual void Visit (ClassLoadEvent ev)
+               {
+               }
+
+               public virtual void Visit (JitEvent ev)
+               {
+               }
+
+               public virtual void Visit (JitHelperEvent ev)
+               {
+               }
+
+               public virtual void Visit (AllocationEvent ev)
+               {
+               }
+
+               public virtual void Visit (HeapBeginEvent ev)
+               {
+               }
+
+               public virtual void Visit (HeapEndEvent ev)
+               {
+               }
+
+               public virtual void Visit (HeapObjectEvent ev)
+               {
+               }
+
+               public virtual void Visit (HeapRootsEvent ev)
+               {
+               }
+
+               public virtual void Visit (GCEvent ev)
+               {
+               }
+
+               public virtual void Visit (GCResizeEvent ev)
+               {
+               }
+
+               public virtual void Visit (GCMoveEvent ev)
+               {
+               }
+
+               public virtual void Visit (GCHandleCreationEvent ev)
+               {
+               }
+
+               public virtual void Visit (GCHandleDeletionEvent ev)
+               {
+               }
+
+               public virtual void Visit (GCFinalizeBeginEvent ev)
+               {
+               }
+
+               public virtual void Visit (GCFinalizeEndEvent ev)
+               {
+               }
+
+               public virtual void Visit (GCFinalizeObjectBeginEvent ev)
+               {
+               }
+
+               public virtual void Visit (GCFinalizeObjectEndEvent ev)
+               {
+               }
+
+               public virtual void Visit (ThrowEvent ev)
+               {
+               }
+
+               public virtual void Visit (ExceptionClauseEvent ev)
+               {
+               }
+
+               public virtual void Visit (EnterEvent ev)
+               {
+               }
+
+               public virtual void Visit (LeaveEvent ev)
+               {
+               }
+
+               public virtual void Visit (ExceptionalLeaveEvent ev)
+               {
+               }
+
+               public virtual void Visit (MonitorEvent ev)
+               {
+               }
+
+               public virtual void Visit (SampleHitEvent ev)
+               {
+               }
+
+               public virtual void Visit (CounterSamplesEvent ev)
+               {
+               }
+
+               public virtual void Visit (CounterDescriptionsEvent ev)
+               {
+               }
+
+               public virtual void Visit (UnmanagedBinaryEvent ev)
+               {
+               }
+
+               public virtual void Visit (UnmanagedSymbolEvent ev)
+               {
+               }
+
+               public virtual void Visit (AssemblyCoverageEvent ev)
+               {
+               }
+
+               public virtual void Visit (ClassCoverageEvent ev)
+               {
+               }
+
+               public virtual void Visit (MethodCoverageEvent ev)
+               {
+               }
+
+               public virtual void Visit (StatementCoverageEvent ev)
+               {
+               }
+
+               public virtual void Visit (SynchronizationPointEvent ev)
+               {
+               }
+       }
+}
diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvents.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvents.cs
new file mode 100644 (file)
index 0000000..248863e
--- /dev/null
@@ -0,0 +1,625 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+
+namespace Mono.Profiler.Log {
+
+       public sealed class AppDomainLoadEvent : LogEvent {
+
+               public long AppDomainId { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class AppDomainUnloadEvent : LogEvent {
+
+               public long AppDomainId { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class AppDomainNameEvent : LogEvent {
+
+               public long AppDomainId { get; internal set; }
+
+               public string Name { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class ContextLoadEvent : LogEvent {
+
+               public long ContextId { get; internal set; }
+
+               public long AppDomainId { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class ContextUnloadEvent : LogEvent {
+
+               public long ContextId { get; internal set; }
+
+               public long AppDomainId { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class ThreadStartEvent : LogEvent {
+
+               public long ThreadId { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class ThreadEndEvent : LogEvent {
+
+               public long ThreadId { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class ThreadNameEvent : LogEvent {
+
+               public long ThreadId { get; internal set; }
+
+               public string Name { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class ImageLoadEvent : LogEvent {
+
+               public long ImagePointer { get; internal set; }
+
+               public string Name { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class ImageUnloadEvent : LogEvent {
+
+               public long ImagePointer { get; internal set; }
+
+               public string Name { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class AssemblyLoadEvent : LogEvent {
+
+               public long AssemblyPointer { get; internal set; }
+
+               public long ImagePointer { get; internal set; }
+
+               public string Name { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class AssemblyUnloadEvent : LogEvent {
+
+               public long AssemblyPointer { get; internal set; }
+
+               public long ImagePointer { get; internal set; }
+
+               public string Name { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class ClassLoadEvent : LogEvent {
+
+               public long ClassPointer { get; internal set; }
+
+               public long ImagePointer { get; internal set; }
+
+               public string Name { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class JitEvent : LogEvent {
+
+               public long MethodPointer { get; internal set; }
+
+               public long CodePointer { get; internal set; }
+
+               public long CodeSize { get; internal set; }
+
+               public string Name { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class JitHelperEvent : LogEvent {
+
+               public LogJitHelper Type { get; internal set; }
+
+               public long BufferPointer { get; internal set; }
+
+               public long BufferSize { get; internal set; }
+
+               public string Name { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class AllocationEvent : LogEvent {
+
+               public long ClassPointer { get; internal set; }
+
+               public long ObjectPointer { get; internal set; }
+
+               public long ObjectSize { get; internal set; }
+
+               public IReadOnlyList<long> Backtrace { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class HeapBeginEvent : LogEvent {
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class HeapEndEvent : LogEvent {
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class HeapObjectEvent : LogEvent {
+
+               public struct HeapObjectReference {
+
+                       public long Offset { get; internal set; }
+
+                       public long ObjectPointer { get; internal set; }
+               }
+
+               public long ObjectPointer { get; internal set; }
+
+               public long ClassPointer { get; internal set; }
+
+               public long ObjectSize { get; internal set; }
+
+               public IReadOnlyList<HeapObjectReference> References { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class HeapRootsEvent : LogEvent {
+
+               public struct HeapRoot {
+
+                       public long ObjectPointer { get; internal set; }
+
+                       public LogHeapRootAttributes Attributes { get; internal set; }
+
+                       public long ExtraInfo { get; internal set; }
+               }
+
+               public long MaxGenerationCollectionCount { get; internal set; }
+
+               public IReadOnlyList<HeapRoot> Roots { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class GCEvent : LogEvent {
+
+               public LogGCEvent Type { get; internal set; }
+
+               public byte Generation { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class GCResizeEvent : LogEvent {
+
+               public long NewSize { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class GCMoveEvent : LogEvent {
+
+               public IReadOnlyList<long> OldObjectPointers { get; internal set; }
+
+               public IReadOnlyList<long> NewObjectPointers { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class GCHandleCreationEvent : LogEvent {
+
+               public LogGCHandleType Type { get; internal set; }
+
+               public long Handle { get; internal set; }
+
+               public long ObjectPointer { get; internal set; }
+
+               public IReadOnlyList<long> Backtrace { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class GCHandleDeletionEvent : LogEvent {
+
+               public LogGCHandleType Type { get; internal set; }
+
+               public long Handle { get; internal set; }
+
+               public IReadOnlyList<long> Backtrace { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class GCFinalizeBeginEvent : LogEvent {
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class GCFinalizeEndEvent : LogEvent {
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class GCFinalizeObjectBeginEvent : LogEvent {
+
+               public long ObjectPointer { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class GCFinalizeObjectEndEvent : LogEvent {
+
+               public long ObjectPointer { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class ThrowEvent : LogEvent {
+
+               public long ObjectPointer { get; internal set; }
+
+               public IReadOnlyList<long> Backtrace { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class ExceptionClauseEvent : LogEvent {
+
+               public LogExceptionClause Type { get; internal set; }
+
+               public long Index { get; internal set; }
+
+               public long MethodPointer { get; internal set; }
+
+               public long ObjectPointer { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class EnterEvent : LogEvent {
+
+               public long MethodPointer { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class LeaveEvent : LogEvent {
+
+               public long MethodPointer { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class ExceptionalLeaveEvent : LogEvent {
+
+               public long MethodPointer { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class MonitorEvent : LogEvent {
+
+               public LogMonitorEvent Event { get; internal set; }
+
+               public long ObjectPointer { get; internal set; }
+
+               public IReadOnlyList<long> Backtrace { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class SampleHitEvent : LogEvent {
+
+               public LogSampleHitType Type { get; internal set; }
+
+               public long ThreadId { get; internal set; }
+
+               public IReadOnlyList<long> UnmanagedBacktrace { get; internal set; }
+
+               public IReadOnlyList<long> ManagedBacktrace { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class CounterSamplesEvent : LogEvent {
+
+               public struct CounterSample {
+
+                       public long Index { get; internal set; }
+
+                       public LogCounterType Type { get; internal set; }
+
+                       public object Value { get; internal set; }
+               }
+
+               public IReadOnlyList<CounterSample> Samples { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class CounterDescriptionsEvent : LogEvent {
+
+               public struct CounterDescription {
+
+                       public LogCounterSection Section { get; internal set; }
+
+                       public string SectionName { get; internal set; }
+
+                       public string CounterName { get; internal set; }
+
+                       public LogCounterType Type { get; internal set; }
+
+                       public LogCounterUnit Unit { get; internal set; }
+
+                       public LogCounterVariance Variance { get; internal set; }
+
+                       public long Index { get; internal set; }
+               }
+
+               public IReadOnlyList<CounterDescription> Descriptions { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class UnmanagedBinaryEvent : LogEvent {
+
+               public long SegmentPointer { get; internal set; }
+
+               public long SegmentOffset { get; internal set; }
+
+               public long SegmentSize { get; internal set; }
+
+               public string FileName { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class UnmanagedSymbolEvent : LogEvent {
+
+               public long CodePointer { get; internal set; }
+
+               public long CodeSize { get; internal set; }
+
+               public string Name { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class AssemblyCoverageEvent : LogEvent {
+
+               public string AssemblyName { get; internal set; }
+
+               public Guid Guid { get; internal set; }
+
+               public string FileName { get; internal set; }
+
+               public long NumberOfMethods { get; internal set; }
+
+               public long FullyCovered { get; internal set; }
+
+               public long PartiallyCovered { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class ClassCoverageEvent : LogEvent {
+
+               public string AssemblyName { get; internal set; }
+
+               public string ClassName { get; internal set; }
+
+               public long NumberOfMethods { get; internal set; }
+
+               public long FullyCovered { get; internal set; }
+
+               public long PartiallyCovered { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class MethodCoverageEvent : LogEvent {
+
+               public string AssemblyName { get; internal set; }
+
+               public string ClassName { get; internal set; }
+
+               public string MethodName { get; internal set; }
+
+               public string MethodSignature { get; internal set; }
+
+               public string FileName { get; internal set; }
+
+               public ulong MetadataToken { get; internal set; }
+
+               public long MethodId { get; internal set; }
+
+               public long NumberOfStatements { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class StatementCoverageEvent : LogEvent {
+
+               public long MethodId { get; internal set; }
+
+               public long RelativeILOffset { get; internal set; }
+
+               public ulong Counter { get; internal set; }
+
+               public long Line { get; internal set; }
+
+               public long Column { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+
+       public sealed class SynchronizationPointEvent : LogEvent {
+
+               public LogSynchronizationPoint Type { get; internal set; }
+
+               internal override void Accept (LogEventVisitor visitor)
+               {
+                       visitor.Visit (this);
+               }
+       }
+}
diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogException.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogException.cs
new file mode 100644 (file)
index 0000000..eca0c42
--- /dev/null
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Mono.Profiler.Log {
+
+       public sealed class LogException : Exception {
+
+               public LogException (string message)
+                       : base (message)
+               {
+               }
+       }
+}
diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs
new file mode 100644 (file)
index 0000000..f57d4ef
--- /dev/null
@@ -0,0 +1,577 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+
+namespace Mono.Profiler.Log {
+
+       public sealed class LogProcessor {
+
+               public LogReader Reader { get; }
+
+               public LogEventVisitor ImmediateVisitor { get; }
+
+               public LogEventVisitor SortedVisitor { get; }
+
+               public LogStreamHeader StreamHeader { get; private set; }
+
+               LogBufferHeader _bufferHeader;
+
+               ulong _time;
+
+               bool _used;
+
+               public LogProcessor (LogReader reader, LogEventVisitor immediateVisitor, LogEventVisitor sortedVisitor)
+               {
+                       if (reader == null)
+                               throw new ArgumentNullException (nameof (reader));
+
+                       Reader = reader;
+                       ImmediateVisitor = immediateVisitor;
+                       SortedVisitor = sortedVisitor;
+               }
+
+               public void Process ()
+               {
+                       Process (CancellationToken.None);
+               }
+
+               static void ProcessEvent (LogEventVisitor visitor, LogEvent ev)
+               {
+                       if (visitor != null) {
+                               visitor.VisitBefore (ev);
+                               ev.Accept (visitor);
+                               visitor.VisitAfter (ev);
+                       }
+               }
+
+               void ProcessEvents (List<LogEvent> events, CancellationToken token)
+               {
+                       foreach (var ev in events.OrderBy (x => x.Timestamp)) {
+                               token.ThrowIfCancellationRequested ();
+                               ProcessEvent (SortedVisitor, ev);
+                       }
+
+                       events.Clear ();
+               }
+
+               public void Process (CancellationToken token)
+               {
+                       if (_used)
+                               throw new InvalidOperationException ("This log processor cannot be reused.");
+
+                       _used = true;
+                       StreamHeader = new LogStreamHeader (Reader);
+
+                       var events = new List<LogEvent> (Environment.ProcessorCount * 1000);
+
+                       while (!Reader.BaseStream.EndOfStream) {
+                               token.ThrowIfCancellationRequested ();
+
+                               _bufferHeader = new LogBufferHeader (StreamHeader, Reader);
+
+                               // Use the manual position tracking in LogReader so we're
+                               // compatible with non-seekable streams.
+                               var goal = Reader.Position + _bufferHeader.Length;
+
+                               while (Reader.Position < goal) {
+                                       token.ThrowIfCancellationRequested ();
+
+                                       var ev = ReadEvent ();
+
+                                       ProcessEvent (ImmediateVisitor, ev);
+                                       events.Add (ev);
+
+                                       if (ev is SynchronizationPointEvent)
+                                               ProcessEvents (events, token);
+                               }
+                       }
+
+                       ProcessEvents (events, token);
+               }
+
+               LogEvent ReadEvent ()
+               {
+                       var type = Reader.ReadByte ();
+                       var basicType = (LogEventType) (type & 0xf);
+                       var extType = (LogEventType) (type & 0xf0);
+
+                       _time = ReadTime ();
+
+                       switch (basicType) {
+                       case LogEventType.Allocation:
+                               switch (extType) {
+                               case LogEventType.AllocationBacktrace:
+                               case LogEventType.AllocationNoBacktrace:
+                                       return new AllocationEvent {
+                                               ClassPointer = ReadPointer (),
+                                               ObjectPointer = ReadObject (),
+                                               ObjectSize = (long) Reader.ReadULeb128 (),
+                                               Backtrace = ReadBacktrace (extType == LogEventType.AllocationBacktrace),
+                                       };
+                               default:
+                                       throw new LogException ($"Invalid extended event type ({extType}).");
+                               }
+                       case LogEventType.GC:
+                               switch (extType) {
+                               case LogEventType.GCEvent:
+                                       return new GCEvent {
+                                               Type = (LogGCEvent) Reader.ReadByte (),
+                                               Generation = Reader.ReadByte (),
+                                       };
+                               case LogEventType.GCResize:
+                                       return new GCResizeEvent {
+                                               NewSize = (long) Reader.ReadULeb128 (),
+                                       };
+                               case LogEventType.GCMove: {
+                                       var list = new long [(int) Reader.ReadULeb128 ()];
+
+                                       for (var i = 0; i < list.Length; i++)
+                                               list [i] = ReadObject ();
+
+                                       return new GCMoveEvent {
+                                               OldObjectPointers = list.Where ((_, i) => i % 2 == 0).ToArray (),
+                                               NewObjectPointers = list.Where ((_, i) => i % 2 != 0).ToArray (),
+                                       };
+                               }
+                               case LogEventType.GCHandleCreationNoBacktrace:
+                               case LogEventType.GCHandleCreationBacktrace:
+                                       return new GCHandleCreationEvent {
+                                               Type = (LogGCHandleType) Reader.ReadULeb128 (),
+                                               Handle = (long) Reader.ReadULeb128 (),
+                                               ObjectPointer = ReadObject (),
+                                               Backtrace = ReadBacktrace (extType == LogEventType.GCHandleCreationBacktrace),
+                                       };
+                               case LogEventType.GCHandleDeletionNoBacktrace:
+                               case LogEventType.GCHandleDeletionBacktrace:
+                                       return new GCHandleDeletionEvent {
+                                               Type = (LogGCHandleType) Reader.ReadULeb128 (),
+                                               Handle = (long) Reader.ReadULeb128 (),
+                                               Backtrace = ReadBacktrace (extType == LogEventType.GCHandleDeletionBacktrace),
+                                       };
+                               case LogEventType.GCFinalizeBegin:
+                                       return new GCFinalizeBeginEvent ();
+                               case LogEventType.GCFinalizeEnd:
+                                       return new GCFinalizeEndEvent ();
+                               case LogEventType.GCFinalizeObjectBegin:
+                                       return new GCFinalizeObjectBeginEvent {
+                                               ObjectPointer = ReadObject (),
+                                       };
+                               case LogEventType.GCFinalizeObjectEnd:
+                                       return new GCFinalizeObjectEndEvent {
+                                               ObjectPointer = ReadObject (),
+                                       };
+                               default:
+                                       throw new LogException ($"Invalid extended event type ({extType}).");
+                               }
+                       case LogEventType.Metadata: {
+                               var load = false;
+                               var unload = false;
+
+                               switch (extType) {
+                               case LogEventType.MetadataExtra:
+                                       break;
+                               case LogEventType.MetadataEndLoad:
+                                       load = true;
+                                       break;
+                               case LogEventType.MetadataEndUnload:
+                                       unload = true;
+                                       break;
+                               default:
+                                       throw new LogException ($"Invalid extended event type ({extType}).");
+                               }
+
+                               var metadataType = (LogMetadataType) Reader.ReadByte ();
+
+                               switch (metadataType) {
+                               case LogMetadataType.Class:
+                                       if (load) {
+                                               return new ClassLoadEvent {
+                                                       ClassPointer = ReadPointer (),
+                                                       ImagePointer = ReadPointer (),
+                                                       Name = Reader.ReadCString (),
+                                               };
+                                       } else
+                                               throw new LogException ("Invalid class metadata event.");
+                               case LogMetadataType.Image:
+                                       if (load) {
+                                               return new ImageLoadEvent {
+                                                       ImagePointer = ReadPointer (),
+                                                       Name = Reader.ReadCString (),
+                                               };
+                                       } else if (unload) {
+                                               return new ImageUnloadEvent {
+                                                       ImagePointer = ReadPointer (),
+                                                       Name = Reader.ReadCString (),
+                                               };
+                                       } else
+                                               throw new LogException ("Invalid image metadata event.");
+                               case LogMetadataType.Assembly:
+                                       if (load) {
+                                               return new AssemblyLoadEvent {
+                                                       AssemblyPointer = ReadPointer (),
+                                                       ImagePointer = ReadPointer (),
+                                                       Name = Reader.ReadCString (),
+                                               };
+                                       } else if (unload) {
+                                               return new AssemblyUnloadEvent {
+                                                       AssemblyPointer = ReadPointer (),
+                                                       ImagePointer = ReadPointer (),
+                                                       Name = Reader.ReadCString (),
+                                               };
+                                       } else
+                                               throw new LogException ("Invalid assembly metadata event.");
+                               case LogMetadataType.AppDomain:
+                                       if (load) {
+                                               return new AppDomainLoadEvent {
+                                                       AppDomainId = ReadPointer (),
+                                               };
+                                       } else if (unload) {
+                                               return new AppDomainUnloadEvent {
+                                                       AppDomainId = ReadPointer (),
+                                               };
+                                       } else {
+                                               return new AppDomainNameEvent {
+                                                       AppDomainId = ReadPointer (),
+                                                       Name = Reader.ReadCString (),
+                                               };
+                                       }
+                               case LogMetadataType.Thread:
+                                       if (load) {
+                                               return new ThreadStartEvent {
+                                                       ThreadId = ReadPointer (),
+                                               };
+                                       } else if (unload) {
+                                               return new ThreadEndEvent {
+                                                       ThreadId = ReadPointer (),
+                                               };
+                                       } else {
+                                               return new ThreadNameEvent {
+                                                       ThreadId = ReadPointer (),
+                                                       Name = Reader.ReadCString (),
+                                               };
+                                       }
+                               case LogMetadataType.Context:
+                                       if (load) {
+                                               return new ContextLoadEvent {
+                                                       ContextId = ReadPointer (),
+                                                       AppDomainId = ReadPointer (),
+                                               };
+                                       } else if (unload) {
+                                               return new ContextUnloadEvent {
+                                                       ContextId = ReadPointer (),
+                                                       AppDomainId = ReadPointer (),
+                                               };
+                                       } else
+                                               throw new LogException ("Invalid context metadata event.");
+                               default:
+                                       throw new LogException ($"Invalid metadata type ({metadataType}).");
+                               }
+                       }
+                       case LogEventType.Method:
+                               switch (extType) {
+                               case LogEventType.MethodLeave:
+                                       return new LeaveEvent {
+                                               MethodPointer = ReadMethod (),
+                                       };
+                               case LogEventType.MethodEnter:
+                                       return new EnterEvent {
+                                               MethodPointer = ReadMethod (),
+                                       };
+                               case LogEventType.MethodLeaveExceptional:
+                                       return new ExceptionalLeaveEvent {
+                                               MethodPointer = ReadMethod (),
+                                       };
+                               case LogEventType.MethodJit:
+                                       return new JitEvent {
+                                               MethodPointer = ReadMethod (),
+                                               CodePointer = ReadPointer (),
+                                               CodeSize = (long) Reader.ReadULeb128 (),
+                                               Name = Reader.ReadCString (),
+                                       };
+                               default:
+                                       throw new LogException ($"Invalid extended event type ({extType}).");
+                               }
+                       case LogEventType.Exception:
+                               switch (extType) {
+                               case LogEventType.ExceptionThrowNoBacktrace:
+                               case LogEventType.ExceptionThrowBacktrace:
+                                       return new ThrowEvent {
+                                               ObjectPointer = ReadObject (),
+                                               Backtrace = ReadBacktrace (extType == LogEventType.ExceptionThrowBacktrace),
+                                       };
+                               case LogEventType.ExceptionClause:
+                                       return new ExceptionClauseEvent {
+                                               Type = (LogExceptionClause) Reader.ReadByte (),
+                                               Index = (long) Reader.ReadULeb128 (),
+                                               MethodPointer = ReadMethod (),
+                                               ObjectPointer = ReadObject (),
+                                       };
+                               default:
+                                       throw new LogException ($"Invalid extended event type ({extType}).");
+                               }
+                       case LogEventType.Monitor:
+                               switch (extType) {
+                               case LogEventType.MonitorNoBacktrace:
+                               case LogEventType.MonitorBacktrace:
+                                       return new MonitorEvent {
+                                               Event = (LogMonitorEvent) Reader.ReadByte (),
+                                               ObjectPointer = ReadObject (),
+                                               Backtrace = ReadBacktrace (extType == LogEventType.MonitorBacktrace),
+                                       };
+                               default:
+                                       throw new LogException ($"Invalid extended event type ({extType}).");
+                               }
+                       case LogEventType.Heap:
+                               switch (extType) {
+                               case LogEventType.HeapBegin:
+                                       return new HeapBeginEvent ();
+                               case LogEventType.HeapEnd:
+                                       return new HeapEndEvent ();
+                               case LogEventType.HeapObject: {
+                                       var ev = new HeapObjectEvent {
+                                               ObjectPointer = ReadObject (),
+                                               ClassPointer = ReadPointer (),
+                                               ObjectSize = (long) Reader.ReadULeb128 (),
+                                       };
+
+                                       var list = new HeapObjectEvent.HeapObjectReference [(int) Reader.ReadULeb128 ()];
+
+                                       for (var i = 0; i < list.Length; i++) {
+                                               list [i] = new HeapObjectEvent.HeapObjectReference {
+                                                       Offset = (long) Reader.ReadULeb128 (),
+                                                       ObjectPointer = ReadObject (),
+                                               };
+                                       }
+
+                                       ev.References = list;
+
+                                       return ev;
+                               }
+                               case LogEventType.HeapRoots: {
+                                       // TODO: This entire event makes no sense.
+
+                                       var ev = new HeapRootsEvent ();
+                                       var list = new HeapRootsEvent.HeapRoot [(int) Reader.ReadULeb128 ()];
+
+                                       ev.MaxGenerationCollectionCount = (long) Reader.ReadULeb128 ();
+
+                                       for (var i = 0; i < list.Length; i++) {
+                                               list [i] = new HeapRootsEvent.HeapRoot {
+                                                       ObjectPointer = ReadObject (),
+                                                       Attributes = (LogHeapRootAttributes) Reader.ReadByte (),
+                                                       ExtraInfo = (long) Reader.ReadULeb128 (),
+                                               };
+                                       }
+
+                                       ev.Roots = list;
+
+                                       return ev;
+                               }
+                               default:
+                                       throw new LogException ($"Invalid extended event type ({extType}).");
+                               }
+                       case LogEventType.Sample:
+                               switch (extType) {
+                               case LogEventType.SampleHit:
+                                       return new SampleHitEvent {
+                                               Type = (LogSampleHitType) Reader.ReadByte (),
+                                               ThreadId = ReadPointer (),
+                                               UnmanagedBacktrace = ReadBacktrace (true, false),
+                                               ManagedBacktrace = ReadBacktrace (true),
+                                       };
+                               case LogEventType.SampleUnmanagedSymbol:
+                                       return new UnmanagedSymbolEvent {
+                                               CodePointer = ReadPointer (),
+                                               CodeSize = (long) Reader.ReadULeb128 (),
+                                               Name = Reader.ReadCString (),
+                                       };
+                               case LogEventType.SampleUnmanagedBinary:
+                                       return new UnmanagedBinaryEvent {
+                                               SegmentPointer = ReadPointer (),
+                                               SegmentOffset = (long) Reader.ReadULeb128 (),
+                                               SegmentSize = (long) Reader.ReadULeb128 (),
+                                               FileName = Reader.ReadCString (),
+                                       };
+                               case LogEventType.SampleCounterDescriptions: {
+                                       var ev = new CounterDescriptionsEvent ();
+                                       var list = new CounterDescriptionsEvent.CounterDescription [(int) Reader.ReadULeb128 ()];
+
+                                       for (var i = 0; i < list.Length; i++) {
+                                               var section = (LogCounterSection) Reader.ReadULeb128 ();
+
+                                               list [i] = new CounterDescriptionsEvent.CounterDescription {
+                                                       Section = section,
+                                                       SectionName = section == LogCounterSection.User ? Reader.ReadCString () : string.Empty,
+                                                       CounterName = Reader.ReadCString (),
+                                                       Type = (LogCounterType) Reader.ReadByte (),
+                                                       Unit = (LogCounterUnit) Reader.ReadByte (),
+                                                       Variance = (LogCounterVariance) Reader.ReadByte (),
+                                                       Index = (long) Reader.ReadULeb128 (),
+                                               };
+                                       }
+
+                                       ev.Descriptions = list;
+
+                                       return ev;
+                               }
+                               case LogEventType.SampleCounters: {
+                                       var ev = new CounterSamplesEvent ();
+                                       var list = new List<CounterSamplesEvent.CounterSample> ();
+
+                                       while (true) {
+                                               var index = (long) Reader.ReadULeb128 ();
+
+                                               if (index == 0)
+                                                       break;
+
+                                               var counterType = (LogCounterType) Reader.ReadByte ();
+
+                                               object value = null;
+
+                                               switch (counterType) {
+                                               case LogCounterType.String:
+                                                       value = Reader.ReadByte () == 1 ? Reader.ReadCString () : null;
+                                                       break;
+                                               case LogCounterType.Int32:
+                                               case LogCounterType.Word:
+                                               case LogCounterType.Int64:
+                                               case LogCounterType.Interval:
+                                                       value = Reader.ReadSLeb128 ();
+                                                       break;
+                                               case LogCounterType.UInt32:
+                                               case LogCounterType.UInt64:
+                                                       value = Reader.ReadULeb128 ();
+                                                       break;
+                                               case LogCounterType.Double:
+                                                       value = Reader.ReadDouble ();
+                                                       break;
+                                               default:
+                                                       throw new LogException ($"Invalid counter type ({counterType}).");
+                                               }
+
+                                               list.Add (new CounterSamplesEvent.CounterSample {
+                                                       Index = index,
+                                                       Type = counterType,
+                                                       Value = value,
+                                               });
+                                       }
+
+                                       ev.Samples = list;
+
+                                       return ev;
+                               }
+                               default:
+                                       throw new LogException ($"Invalid extended event type ({extType}).");
+                               }
+                       case LogEventType.Runtime:
+                               switch (extType) {
+                               case LogEventType.RuntimeJitHelper: {
+                                       var helperType = (LogJitHelper) Reader.ReadByte ();
+
+                                       return new JitHelperEvent {
+                                               Type = helperType,
+                                               BufferPointer = ReadPointer (),
+                                               BufferSize = (long) Reader.ReadULeb128 (),
+                                               Name = helperType == LogJitHelper.SpecificTrampoline ? Reader.ReadCString () : string.Empty,
+                                       };
+                               }
+                               default:
+                                       throw new LogException ($"Invalid extended event type ({extType}).");
+                               }
+                       case LogEventType.Coverage:
+                               switch (extType) {
+                               case LogEventType.CoverageAssembly:
+                                       return new AssemblyCoverageEvent {
+                                               AssemblyName = Reader.ReadCString (),
+                                               Guid = Guid.Parse (Reader.ReadCString ()),
+                                               FileName = Reader.ReadCString (),
+                                               NumberOfMethods = (long) Reader.ReadULeb128 (),
+                                               FullyCovered = (long) Reader.ReadULeb128 (),
+                                               PartiallyCovered = (long) Reader.ReadULeb128 (),
+                                       };
+                               case LogEventType.CoverageMethod:
+                                       return new MethodCoverageEvent {
+                                               AssemblyName = Reader.ReadCString (),
+                                               ClassName = Reader.ReadCString (),
+                                               MethodName = Reader.ReadCString (),
+                                               MethodSignature = Reader.ReadCString (),
+                                               FileName = Reader.ReadCString (),
+                                               MetadataToken = Reader.ReadULeb128 (),
+                                               MethodId = (long) Reader.ReadULeb128 (),
+                                               NumberOfStatements = (long) Reader.ReadULeb128 (),
+                                       };
+                               case LogEventType.CoverageStatement:
+                                       return new StatementCoverageEvent {
+                                               MethodId = (long) Reader.ReadULeb128 (),
+                                               RelativeILOffset = (long) Reader.ReadULeb128 (),
+                                               Counter = Reader.ReadULeb128 (),
+                                               Line = (long) Reader.ReadULeb128 (),
+                                               Column = (long) Reader.ReadULeb128 (),
+                                       };
+                               case LogEventType.CoverageClass:
+                                       return new ClassCoverageEvent {
+                                               AssemblyName = Reader.ReadCString (),
+                                               ClassName = Reader.ReadCString (),
+                                               NumberOfMethods = (long) Reader.ReadULeb128 (),
+                                               FullyCovered = (long) Reader.ReadULeb128 (),
+                                               PartiallyCovered = (long) Reader.ReadULeb128 (),
+                                       };
+                               default:
+                                       throw new LogException ($"Invalid extended event type ({extType}).");
+                               }
+                       case LogEventType.Meta:
+                               switch (extType) {
+                               case LogEventType.MetaSynchronizationPoint:
+                                       return new SynchronizationPointEvent {
+                                               Type = (LogSynchronizationPoint) Reader.ReadByte (),
+                                       };
+                               default:
+                                       throw new LogException ($"Invalid extended event type ({extType}).");
+                               }
+                       default:
+                               throw new LogException ($"Invalid basic event type ({basicType}).");
+                       }
+               }
+
+               long ReadPointer ()
+               {
+                       var ptr = Reader.ReadSLeb128 () + _bufferHeader.PointerBase;
+
+                       return StreamHeader.PointerSize == sizeof (long) ? ptr : ptr & 0xffffffffL;
+               }
+
+               long ReadObject ()
+               {
+                       return Reader.ReadSLeb128 () + _bufferHeader.ObjectBase;
+               }
+
+               long ReadMethod ()
+               {
+                       return _bufferHeader.CurrentMethod += Reader.ReadSLeb128 ();
+               }
+
+               ulong ReadTime ()
+               {
+                       return _bufferHeader.CurrentTime += Reader.ReadULeb128 ();
+               }
+
+               IReadOnlyList<long> ReadBacktrace (bool actuallyRead, bool managed = true)
+               {
+                       if (!actuallyRead)
+                               return Array.Empty<long> ();
+
+                       var list = new long [(int) Reader.ReadULeb128 ()];
+
+                       for (var i = 0; i < list.Length; i++)
+                               list [i] = managed ? ReadMethod () : ReadPointer ();
+
+                       return list;
+               }
+       }
+}
diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogReader.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogReader.cs
new file mode 100644 (file)
index 0000000..4b241ba
--- /dev/null
@@ -0,0 +1,157 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace Mono.Profiler.Log {
+
+       public sealed class LogReader : IDisposable {
+
+               public static Encoding Encoding { get; } = Encoding.UTF8;
+
+               public LogStream BaseStream => (LogStream) _reader.BaseStream;
+
+               public long Position { get; private set; }
+
+               readonly BinaryReader _reader;
+
+               byte[] _stringBuffer = new byte [1024];
+
+               public LogReader (LogStream stream, bool leaveOpen)
+               {
+                       _reader = new BinaryReader (stream, Encoding, leaveOpen);
+               }
+
+               public void Dispose ()
+               {
+                       _reader.Dispose ();
+               }
+
+               internal byte ReadByte ()
+               {
+                       var b = _reader.ReadByte ();
+
+                       Position += sizeof (byte);
+
+                       return b;
+               }
+
+               internal ushort ReadUInt16 ()
+               {
+                       var i = _reader.ReadUInt16 ();
+
+                       Position += sizeof (ushort);
+
+                       return i;
+               }
+
+               internal int ReadInt32 ()
+               {
+                       var i = _reader.ReadInt32 ();
+
+                       Position += sizeof (int);
+
+                       return i;
+               }
+
+               internal long ReadInt64 ()
+               {
+                       var i = _reader.ReadInt64 ();
+
+                       Position += sizeof (long);
+
+                       return i;
+               }
+
+               internal ulong ReadUInt64 ()
+               {
+                       var i = _reader.ReadUInt64 ();
+
+                       Position += sizeof (ulong);
+
+                       return i;
+               }
+
+               internal double ReadDouble ()
+               {
+                       var d = _reader.ReadDouble ();
+
+                       Position += sizeof (double);
+
+                       return d;
+               }
+
+               internal string ReadHeaderString ()
+               {
+                       var bytes = new byte [ReadInt32 ()];
+
+                       // ReadBytes doesn't necessarily read the specified amount of
+                       // bytes, so just do it this way.
+                       for (var i = 0; i < bytes.Length; i++)
+                               bytes [i] = ReadByte ();
+
+                       return Encoding.GetString (bytes);
+               }
+
+               internal string ReadCString ()
+               {
+                       var pos = 0;
+
+                       byte val;
+
+                       while ((val = ReadByte ()) != 0) {
+                               if (pos == _stringBuffer.Length)
+                                       Array.Resize (ref _stringBuffer, System.Math.Max (_stringBuffer.Length * 2, pos + 1));
+
+                               _stringBuffer [pos++] = val;
+                       }
+
+                       return Encoding.GetString (_stringBuffer, 0, pos);
+               }
+
+               internal long ReadSLeb128 ()
+               {
+                       long result = 0;
+                       var shift = 0;
+
+                       while (true) {
+                               var b = ReadByte ();
+
+                               result |= (long) (b & 0x7f) << shift;
+                               shift += 7;
+
+                               if ((b & 0x80) != 0x80) {
+                                       if (shift < sizeof (long) * 8 && (b & 0x40) == 0x40)
+                                               result |= -(1L << shift);
+
+                                       break;
+                               }
+                       }
+
+                       return result;
+               }
+
+               internal ulong ReadULeb128 ()
+               {
+                       ulong result = 0;
+                       var shift = 0;
+
+                       while (true) {
+                               var b = ReadByte ();
+
+                               result |= (ulong) (b & 0x7f) << shift;
+
+                               if ((b & 0x80) != 0x80)
+                                       break;
+
+                               shift += 7;
+                       }
+
+                       return result;
+               }
+       }
+}
diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogRuntimeProfiler.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogRuntimeProfiler.cs
new file mode 100644 (file)
index 0000000..8ed0993
--- /dev/null
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.InteropServices;
+
+namespace Mono.Profiler.Log {
+
+       public static class LogRuntimeProfiler {
+
+               // TODO: Runtime profiler interface.
+       }
+}
diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogStream.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogStream.cs
new file mode 100644 (file)
index 0000000..4fd0daf
--- /dev/null
@@ -0,0 +1,71 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+
+namespace Mono.Profiler.Log {
+
+       public class LogStream : Stream {
+
+               public Stream BaseStream { get; }
+
+               public virtual bool EndOfStream => BaseStream.Position == BaseStream.Length;
+
+               public override bool CanRead => true;
+
+               public override bool CanSeek => false;
+
+               public override bool CanWrite => false;
+
+               public override long Length => throw new NotSupportedException ();
+
+               public override long Position {
+                       get => throw new NotSupportedException ();
+                       set => throw new NotSupportedException ();
+               }
+
+               public LogStream (Stream baseStream)
+               {
+                       if (baseStream == null)
+                               throw new ArgumentNullException (nameof (baseStream));
+
+                       if (!baseStream.CanRead)
+                               throw new ArgumentException ("Stream does not support reading.", nameof (baseStream));
+
+                       BaseStream = baseStream;
+               }
+
+               protected override void Dispose (bool disposing)
+               {
+                       if (disposing)
+                               BaseStream.Dispose ();
+               }
+
+               public override void Flush ()
+               {
+                       throw new NotSupportedException ();
+               }
+
+               public override int Read (byte[] buffer, int offset, int count)
+               {
+                       return BaseStream.Read (buffer, offset, count);
+               }
+
+               public override long Seek (long offset, SeekOrigin origin)
+               {
+                       throw new NotSupportedException ();
+               }
+
+               public override void SetLength (long value)
+               {
+                       throw new NotSupportedException ();
+               }
+
+               public override void Write (byte[] buffer, int offset, int count)
+               {
+                       throw new NotSupportedException ();
+               }
+       }
+}
diff --git a/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogStreamHeader.cs b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogStreamHeader.cs
new file mode 100644 (file)
index 0000000..b8eb441
--- /dev/null
@@ -0,0 +1,55 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Mono.Profiler.Log {
+
+       public sealed class LogStreamHeader {
+
+               const int Id = 0x4d505a01;
+
+               public Version Version { get; }
+
+               public int FormatVersion { get; }
+
+               public byte PointerSize { get; }
+
+               public ulong StartupTime { get; }
+
+               public int TimerOverhead { get; }
+
+               public int Flags { get; }
+
+               public int ProcessId { get; }
+
+               public int Port { get; }
+
+               public string Arguments { get; }
+
+               public string Architecture { get; }
+
+               public string OperatingSystem { get; }
+
+               internal LogStreamHeader (LogReader reader)
+               {
+                       var id = reader.ReadInt32 ();
+
+                       if (id != Id)
+                               throw new LogException ($"Invalid stream header ID (0x{id:X}).");
+
+                       Version = new Version (reader.ReadByte (), reader.ReadByte ());
+                       FormatVersion = reader.ReadByte ();
+                       PointerSize = reader.ReadByte ();
+                       StartupTime = reader.ReadUInt64 ();
+                       TimerOverhead = reader.ReadInt32 ();
+                       Flags = reader.ReadInt32 ();
+                       ProcessId = reader.ReadInt32 ();
+                       Port = reader.ReadUInt16 ();
+                       Arguments = reader.ReadHeaderString ();
+                       Architecture = reader.ReadHeaderString ();
+                       OperatingSystem = reader.ReadHeaderString ();
+               }
+       }
+}