diff options
author | Alex Rønne Petersen <alexrp@xamarin.com> | 2017-06-12 23:53:56 +0300 |
---|---|---|
committer | Alex Rønne Petersen <alexrp@xamarin.com> | 2017-06-20 01:47:52 +0300 |
commit | fbb36d5500450ba5336286ea7646463f3f76ee2a (patch) | |
tree | 6ebf488a95369170a394afb422919514cb688e0c /mcs/class/Mono.Profiler.Log | |
parent | d421ee95b03dd2ffee3dcea9ef24406a5d064684 (diff) |
[profiler] Introduce the Mono.Profiler.Log library. API still unstable.
Diffstat (limited to 'mcs/class/Mono.Profiler.Log')
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 (); + } + } +} |