Xamarin Public Jenkins (auto-signing) 7d7f676260 Imported Upstream version 5.16.0.100
Former-commit-id: 38faa55fb9669e35e7d8448b15c25dc447f25767
2018-08-07 15:19:03 +00:00

664 lines
19 KiB
C#

// 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 LogStream Stream { get; }
public LogEventVisitor ImmediateVisitor { get; }
public LogEventVisitor SortedVisitor { get; }
public LogStreamHeader StreamHeader { get; private set; }
LogReader _reader;
LogBufferHeader _bufferHeader;
ulong _time;
bool _used;
public LogProcessor (LogStream stream, LogEventVisitor immediateVisitor, LogEventVisitor sortedVisitor)
{
if (stream == null)
throw new ArgumentNullException (nameof (stream));
Stream = stream;
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;
_reader = new LogReader (Stream, true);
StreamHeader = new LogStreamHeader (_reader);
var events = new List<LogEvent> (Environment.ProcessorCount * 1000);
while (!Stream.EndOfStream) {
token.ThrowIfCancellationRequested ();
_bufferHeader = new LogBufferHeader (StreamHeader, _reader);
// Read the entire buffer into a MemoryStream ahead of time to
// reduce the amount of I/O system calls we do. This should be
// fine since the profiler tries to keep buffers small and
// flushes them every second at minimum. This also has the
// advantage that we can use the Position and Length properties
// even if the stream we read the buffer from is actually
// non-seekable.
var stream = new MemoryStream (_reader.ReadBytes (_bufferHeader.Length), false);
using (var reader = new LogReader (stream, false)) {
var oldReader = _reader;
_reader = reader;
while (stream.Position < stream.Length) {
token.ThrowIfCancellationRequested ();
var ev = ReadEvent ();
ProcessEvent (ImmediateVisitor, ev);
events.Add (ev);
if (ev is SynchronizationPointEvent)
ProcessEvents (events, token);
}
_reader = oldReader;
}
}
ProcessEvents (events, token);
}
LogEvent ReadEvent ()
{
var type = _reader.ReadByte ();
var basicType = (LogEventType) (type & 0xf);
var extType = (LogEventType) (type & 0xf0);
_time = ReadTime ();
LogEvent ev = null;
switch (basicType) {
case LogEventType.Allocation:
switch (extType) {
case LogEventType.AllocationBacktrace:
case LogEventType.AllocationNoBacktrace:
ev = new AllocationEvent {
ClassPointer = StreamHeader.FormatVersion < 15 ? ReadPointer () : 0,
VTablePointer = StreamHeader.FormatVersion >= 15 ? ReadPointer () : 0,
ObjectPointer = ReadObject (),
ObjectSize = (long) _reader.ReadULeb128 (),
Backtrace = ReadBacktrace (extType == LogEventType.AllocationBacktrace),
};
break;
default:
throw new LogException ($"Invalid extended event type ({extType}).");
}
break;
case LogEventType.GC:
switch (extType) {
case LogEventType.GCEvent:
ev = new GCEvent {
Type = (LogGCEvent) _reader.ReadByte (),
Generation = _reader.ReadByte (),
};
break;
case LogEventType.GCResize:
ev = new GCResizeEvent {
NewSize = (long) _reader.ReadULeb128 (),
};
break;
case LogEventType.GCMove: {
var list = new long [(int) _reader.ReadULeb128 ()];
for (var i = 0; i < list.Length; i++)
list [i] = ReadObject ();
ev = new GCMoveEvent {
OldObjectPointers = list.Where ((_, i) => i % 2 == 0).ToArray (),
NewObjectPointers = list.Where ((_, i) => i % 2 != 0).ToArray (),
};
break;
}
case LogEventType.GCHandleCreationNoBacktrace:
case LogEventType.GCHandleCreationBacktrace:
ev = new GCHandleCreationEvent {
Type = (LogGCHandleType) _reader.ReadULeb128 (),
Handle = (long) _reader.ReadULeb128 (),
ObjectPointer = ReadObject (),
Backtrace = ReadBacktrace (extType == LogEventType.GCHandleCreationBacktrace),
};
break;
case LogEventType.GCHandleDeletionNoBacktrace:
case LogEventType.GCHandleDeletionBacktrace:
ev = new GCHandleDeletionEvent {
Type = (LogGCHandleType) _reader.ReadULeb128 (),
Handle = (long) _reader.ReadULeb128 (),
Backtrace = ReadBacktrace (extType == LogEventType.GCHandleDeletionBacktrace),
};
break;
case LogEventType.GCFinalizeBegin:
ev = new GCFinalizeBeginEvent ();
break;
case LogEventType.GCFinalizeEnd:
ev = new GCFinalizeEndEvent ();
break;
case LogEventType.GCFinalizeObjectBegin:
ev = new GCFinalizeObjectBeginEvent {
ObjectPointer = ReadObject (),
};
break;
case LogEventType.GCFinalizeObjectEnd:
ev = new GCFinalizeObjectEndEvent {
ObjectPointer = ReadObject (),
};
break;
default:
throw new LogException ($"Invalid extended event type ({extType}).");
}
break;
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) {
ev = new ClassLoadEvent {
ClassPointer = ReadPointer (),
ImagePointer = ReadPointer (),
Name = _reader.ReadCString (),
};
} else
throw new LogException ("Invalid class metadata event.");
break;
case LogMetadataType.Image:
if (load) {
var ile = new ImageLoadEvent {
ImagePointer = ReadPointer (),
Name = _reader.ReadCString (),
};
if (StreamHeader.FormatVersion >= 16) {
var guid = _reader.ReadCString ();
ile.ModuleVersionId = guid == string.Empty ? Guid.Empty : Guid.Parse (guid);
}
ev = ile;
} else if (unload) {
ev = new ImageUnloadEvent {
ImagePointer = ReadPointer (),
Name = _reader.ReadCString (),
};
} else
throw new LogException ("Invalid image metadata event.");
break;
case LogMetadataType.Assembly:
if (load) {
ev = new AssemblyLoadEvent {
AssemblyPointer = ReadPointer (),
ImagePointer = StreamHeader.FormatVersion >= 14 ? ReadPointer () : 0,
Name = _reader.ReadCString (),
};
} else if (unload) {
ev = new AssemblyUnloadEvent {
AssemblyPointer = ReadPointer (),
ImagePointer = StreamHeader.FormatVersion >= 14 ? ReadPointer () : 0,
Name = _reader.ReadCString (),
};
} else
throw new LogException ("Invalid assembly metadata event.");
break;
case LogMetadataType.AppDomain:
if (load) {
ev = new AppDomainLoadEvent {
AppDomainId = ReadPointer (),
};
} else if (unload) {
ev = new AppDomainUnloadEvent {
AppDomainId = ReadPointer (),
};
} else {
ev = new AppDomainNameEvent {
AppDomainId = ReadPointer (),
Name = _reader.ReadCString (),
};
}
break;
case LogMetadataType.Thread:
if (load) {
ev = new ThreadStartEvent {
ThreadId = ReadPointer (),
};
} else if (unload) {
ev = new ThreadEndEvent {
ThreadId = ReadPointer (),
};
} else {
ev = new ThreadNameEvent {
ThreadId = ReadPointer (),
Name = _reader.ReadCString (),
};
}
break;
case LogMetadataType.Context:
if (load) {
ev = new ContextLoadEvent {
ContextId = ReadPointer (),
AppDomainId = ReadPointer (),
};
} else if (unload) {
ev = new ContextUnloadEvent {
ContextId = ReadPointer (),
AppDomainId = ReadPointer (),
};
} else
throw new LogException ("Invalid context metadata event.");
break;
case LogMetadataType.VTable:
if (load) {
ev = new VTableLoadEvent {
VTablePointer = ReadPointer (),
AppDomainId = ReadPointer (),
ClassPointer = ReadPointer (),
};
} else
throw new LogException ("Invalid VTable metadata event.");
break;
default:
throw new LogException ($"Invalid metadata type ({metadataType}).");
}
break;
}
case LogEventType.Method:
switch (extType) {
case LogEventType.MethodLeave:
ev = new LeaveEvent {
MethodPointer = ReadMethod (),
};
break;
case LogEventType.MethodEnter:
ev = new EnterEvent {
MethodPointer = ReadMethod (),
};
break;
case LogEventType.MethodLeaveExceptional:
ev = new ExceptionalLeaveEvent {
MethodPointer = ReadMethod (),
};
break;
case LogEventType.MethodJit:
ev = new JitEvent {
MethodPointer = ReadMethod (),
CodePointer = ReadPointer (),
CodeSize = (long) _reader.ReadULeb128 (),
Name = _reader.ReadCString (),
};
break;
default:
throw new LogException ($"Invalid extended event type ({extType}).");
}
break;
case LogEventType.Exception:
switch (extType) {
case LogEventType.ExceptionThrowNoBacktrace:
case LogEventType.ExceptionThrowBacktrace:
ev = new ThrowEvent {
ObjectPointer = ReadObject (),
Backtrace = ReadBacktrace (extType == LogEventType.ExceptionThrowBacktrace),
};
break;
case LogEventType.ExceptionClause:
ev = new ExceptionClauseEvent {
Type = (LogExceptionClause) _reader.ReadByte (),
Index = (long) _reader.ReadULeb128 (),
MethodPointer = ReadMethod (),
ObjectPointer = StreamHeader.FormatVersion >= 14 ? ReadObject () : 0,
};
break;
default:
throw new LogException ($"Invalid extended event type ({extType}).");
}
break;
case LogEventType.Monitor:
if (StreamHeader.FormatVersion < 14) {
if (extType.HasFlag (LogEventType.MonitorBacktrace)) {
extType = LogEventType.MonitorBacktrace;
} else {
extType = LogEventType.MonitorNoBacktrace;
}
}
switch (extType) {
case LogEventType.MonitorNoBacktrace:
case LogEventType.MonitorBacktrace:
ev = new MonitorEvent {
Event = StreamHeader.FormatVersion >= 14 ?
(LogMonitorEvent) _reader.ReadByte () :
(LogMonitorEvent) ((((byte) type & 0xf0) >> 4) & 0x3),
ObjectPointer = ReadObject (),
Backtrace = ReadBacktrace (extType == LogEventType.MonitorBacktrace),
};
break;
default:
throw new LogException ($"Invalid extended event type ({extType}).");
}
break;
case LogEventType.Heap:
switch (extType) {
case LogEventType.HeapBegin:
ev = new HeapBeginEvent ();
break;
case LogEventType.HeapEnd:
ev = new HeapEndEvent ();
break;
case LogEventType.HeapObject: {
HeapObjectEvent hoe = new HeapObjectEvent {
ObjectPointer = ReadObject (),
ClassPointer = StreamHeader.FormatVersion < 15 ? ReadPointer () : 0,
VTablePointer = StreamHeader.FormatVersion >= 15 ? ReadPointer () : 0,
ObjectSize = (long) _reader.ReadULeb128 (),
Generation = StreamHeader.FormatVersion >= 16 ? _reader.ReadByte () : 0,
};
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 (),
};
}
hoe.References = list;
ev = hoe;
break;
}
case LogEventType.HeapRoots: {
var hre = new HeapRootsEvent ();
var list = new HeapRootsEvent.HeapRoot [(int) _reader.ReadULeb128 ()];
if (StreamHeader.FormatVersion < 15)
hre.MaxGenerationCollectionCount = (long) _reader.ReadULeb128 ();
for (var i = 0; i < list.Length; i++) {
list [i] = new HeapRootsEvent.HeapRoot {
SlotPointer = StreamHeader.FormatVersion >= 15 ? ReadPointer () : 0,
ObjectPointer = ReadObject (),
Attributes = StreamHeader.FormatVersion < 15 ?
(StreamHeader.FormatVersion == 13 ?
(LogHeapRootAttributes) _reader.ReadByte () :
(LogHeapRootAttributes) _reader.ReadULeb128 ()) :
0,
ExtraInfo = StreamHeader.FormatVersion < 15 ? (long) _reader.ReadULeb128 () : 0,
};
}
hre.Roots = list;
ev = hre;
break;
}
case LogEventType.HeapRootRegister:
ev = new HeapRootRegisterEvent {
RootPointer = ReadPointer (),
RootSize = (long) _reader.ReadULeb128 (),
Source = (LogHeapRootSource) _reader.ReadByte (),
Key = ReadPointer (),
Name = _reader.ReadCString (),
};
break;
case LogEventType.HeapRootUnregister:
ev = new HeapRootUnregisterEvent {
RootPointer = ReadPointer (),
};
break;
default:
throw new LogException ($"Invalid extended event type ({extType}).");
}
break;
case LogEventType.Sample:
switch (extType) {
case LogEventType.SampleHit:
if (StreamHeader.FormatVersion < 14) {
// Read SampleType (always set to .Cycles) for versions < 14
_reader.ReadByte ();
}
ev = new SampleHitEvent {
ThreadId = ReadPointer (),
UnmanagedBacktrace = ReadBacktrace (true, false),
ManagedBacktrace = ReadBacktrace (true).Reverse ().ToArray (),
};
break;
case LogEventType.SampleUnmanagedSymbol:
ev = new UnmanagedSymbolEvent {
CodePointer = ReadPointer (),
CodeSize = (long) _reader.ReadULeb128 (),
Name = _reader.ReadCString (),
};
break;
case LogEventType.SampleUnmanagedBinary:
ev = new UnmanagedBinaryEvent {
SegmentPointer = StreamHeader.FormatVersion >= 14 ? ReadPointer () : _reader.ReadSLeb128 (),
SegmentOffset = (long) _reader.ReadULeb128 (),
SegmentSize = (long) _reader.ReadULeb128 (),
FileName = _reader.ReadCString (),
};
break;
case LogEventType.SampleCounterDescriptions: {
var cde = 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 () : null,
CounterName = _reader.ReadCString (),
Type = StreamHeader.FormatVersion < 15 ? (LogCounterType) _reader.ReadByte () : (LogCounterType) _reader.ReadULeb128 (),
Unit = StreamHeader.FormatVersion < 15 ? (LogCounterUnit) _reader.ReadByte () : (LogCounterUnit) _reader.ReadULeb128 (),
Variance = StreamHeader.FormatVersion < 15 ? (LogCounterVariance) _reader.ReadByte () : (LogCounterVariance) _reader.ReadULeb128 (),
Index = (long) _reader.ReadULeb128 (),
};
}
cde.Descriptions = list;
ev = cde;
break;
}
case LogEventType.SampleCounters: {
var cse = new CounterSamplesEvent ();
var list = new List<CounterSamplesEvent.CounterSample> ();
while (true) {
var index = (long) _reader.ReadULeb128 ();
if (index == 0)
break;
var counterType = StreamHeader.FormatVersion < 15 ? (LogCounterType) _reader.ReadByte () : (LogCounterType) _reader.ReadULeb128 ();
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,
});
}
cse.Samples = list;
ev = cse;
break;
}
default:
throw new LogException ($"Invalid extended event type ({extType}).");
}
break;
case LogEventType.Runtime:
switch (extType) {
case LogEventType.RuntimeJitHelper: {
var helperType = (LogJitHelper) _reader.ReadByte ();
if (StreamHeader.FormatVersion < 14)
helperType--;
ev = new JitHelperEvent {
Type = helperType,
BufferPointer = ReadPointer (),
BufferSize = (long) _reader.ReadULeb128 (),
Name = helperType == LogJitHelper.SpecificTrampoline ? _reader.ReadCString () : null,
};
break;
}
default:
throw new LogException ($"Invalid extended event type ({extType}).");
}
break;
case LogEventType.Meta:
switch (extType) {
case LogEventType.MetaSynchronizationPoint:
ev = new SynchronizationPointEvent {
Type = (LogSynchronizationPoint) _reader.ReadByte (),
};
break;
case LogEventType.MetaAotId:
ev = new AotIdEvent {
AotId = Guid.Parse (_reader.ReadCString ()),
};
break;
default:
throw new LogException ($"Invalid extended event type ({extType}).");
}
break;
default:
throw new LogException ($"Invalid basic event type ({basicType}).");
}
ev.Timestamp = _time;
ev.Buffer = _bufferHeader;
return ev;
}
long ReadPointer ()
{
var ptr = _reader.ReadSLeb128 () + _bufferHeader.PointerBase;
return StreamHeader.PointerSize == sizeof (long) ? ptr : ptr & 0xffffffffL;
}
long ReadObject ()
{
return _reader.ReadSLeb128 () + _bufferHeader.ObjectBase << 3;
}
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;
}
}
}