Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Rønne Petersen <alexrp@xamarin.com>2017-06-12 23:53:56 +0300
committerAlex Rønne Petersen <alexrp@xamarin.com>2017-06-20 01:47:52 +0300
commitfbb36d5500450ba5336286ea7646463f3f76ee2a (patch)
tree6ebf488a95369170a394afb422919514cb688e0c /mcs/class/Mono.Profiler.Log
parentd421ee95b03dd2ffee3dcea9ef24406a5d064684 (diff)
[profiler] Introduce the Mono.Profiler.Log library. API still unstable.
Diffstat (limited to 'mcs/class/Mono.Profiler.Log')
-rw-r--r--mcs/class/Mono.Profiler.Log/Assembly/AssemblyInfo.cs21
-rw-r--r--mcs/class/Mono.Profiler.Log/Makefile12
-rw-r--r--mcs/class/Mono.Profiler.Log/Mono.Profiler.Log.dll.sources14
-rw-r--r--mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogBufferHeader.cs46
-rw-r--r--mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEnums.cs212
-rw-r--r--mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvent.cs75
-rw-r--r--mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEventVisitor.cs197
-rw-r--r--mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvents.cs625
-rw-r--r--mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogException.cs16
-rw-r--r--mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs577
-rw-r--r--mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogReader.cs157
-rw-r--r--mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogRuntimeProfiler.cs13
-rw-r--r--mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogStream.cs71
-rw-r--r--mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogStreamHeader.cs55
14 files changed, 2091 insertions, 0 deletions
diff --git a/mcs/class/Mono.Profiler.Log/Assembly/AssemblyInfo.cs b/mcs/class/Mono.Profiler.Log/Assembly/AssemblyInfo.cs
new file mode 100644
index 00000000000..8d62855418f
--- /dev/null
+++ b/mcs/class/Mono.Profiler.Log/Assembly/AssemblyInfo.cs
@@ -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
index 00000000000..1f860ab0eb4
--- /dev/null
+++ b/mcs/class/Mono.Profiler.Log/Makefile
@@ -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
index 00000000000..bfb0fea4fed
--- /dev/null
+++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log.dll.sources
@@ -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
index 00000000000..149a6f35173
--- /dev/null
+++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogBufferHeader.cs
@@ -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
index 00000000000..52385963af2
--- /dev/null
+++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEnums.cs
@@ -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
index 00000000000..759a1e64ff8
--- /dev/null
+++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvent.cs
@@ -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
index 00000000000..129a1284bf3
--- /dev/null
+++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEventVisitor.cs
@@ -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
index 00000000000..248863e6066
--- /dev/null
+++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogEvents.cs
@@ -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
index 00000000000..eca0c42daac
--- /dev/null
+++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogException.cs
@@ -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
index 00000000000..f57d4ef651d
--- /dev/null
+++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogProcessor.cs
@@ -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
index 00000000000..4b241ba155d
--- /dev/null
+++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogReader.cs
@@ -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
index 00000000000..8ed09930acb
--- /dev/null
+++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogRuntimeProfiler.cs
@@ -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
index 00000000000..4fd0daf227d
--- /dev/null
+++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogStream.cs
@@ -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
index 00000000000..b8eb4413fd7
--- /dev/null
+++ b/mcs/class/Mono.Profiler.Log/Mono.Profiler.Log/LogStreamHeader.cs
@@ -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 ();
+ }
+ }
+}