Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 121 additions & 1 deletion src/TraceEvent/Parsers/ClrTraceEventParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ public enum Keywords : long
/// </summary>
WaitHandle = 0x40000000000,

/// <summary>
/// Events for randomized allocation sampling.
/// </summary>
AllocationSampling = 0x80000000000,

/// <summary>
/// Recommend default flags (good compromise on verbosity).
/// </summary>
Expand Down Expand Up @@ -1715,6 +1720,19 @@ public event Action<WaitHandleWaitStopTraceData> WaitHandleWaitStop
}
}

public event Action<AllocationSampledTraceData> AllocationSampling
{
add
{
// action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName
RegisterTemplate(new AllocationSampledTraceData(value, 303, 40, "AllocationSampling", AllocationSamplingTaskGuid, 0, "AllocationSampled", ProviderGuid, ProviderName));
}
remove
{
source.UnregisterEventTemplate(value, 303, AllocationSamplingTaskGuid);
}
}

public event Action<ModuleLoadUnloadTraceData> LoaderModuleDCStartV2
{
add
Expand Down Expand Up @@ -2194,6 +2212,10 @@ static private WaitHandleWaitStopTraceData WaitHandleWaitStopTemplate(Action<Wai
{ // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName
return new WaitHandleWaitStopTraceData(action, 302, 39, "WaitHandleWait", WaitHandleWaitTaskGuid, 2, "Stop", ProviderGuid, ProviderName);
}
static private AllocationSampledTraceData AllocationSampledTemplate(Action<AllocationSampledTraceData> action)
{ // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName
return new AllocationSampledTraceData(action, 303, 40, "AllocationSampling", AllocationSamplingTaskGuid, 0, "AllocationSampled", ProviderGuid, ProviderName);
}
static private JitInstrumentationDataTraceData JitInstrumentationDataTemplate(Action<JitInstrumentationDataTraceData> action)
{ // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName
return new JitInstrumentationDataTraceData(action, 297, 34, "JitInstrumentationData", JitInstrumentationDataTaskGuid, 11, "InstrumentationData", ProviderGuid, ProviderName);
Expand Down Expand Up @@ -2224,7 +2246,7 @@ protected internal override void EnumerateTemplates(Func<string, string, EventFi
{
if (s_templates == null)
{
var templates = new TraceEvent[147];
var templates = new TraceEvent[148];
templates[0] = new GCStartTraceData(null, 1, 1, "GC", GCTaskGuid, 1, "Start", ProviderGuid, ProviderName);
templates[1] = new GCEndTraceData(null, 2, 1, "GC", GCTaskGuid, 2, "Stop", ProviderGuid, ProviderName);
templates[2] = new GCNoUserDataTraceData(null, 3, 1, "GC", GCTaskGuid, 132, "RestartEEStop", ProviderGuid, ProviderName);
Expand Down Expand Up @@ -2382,6 +2404,7 @@ protected internal override void EnumerateTemplates(Func<string, string, EventFi

templates[145] = new WaitHandleWaitStartTraceData(null, 301, 39, "WaitHandleWait", WaitHandleWaitTaskGuid, 1, "Start", ProviderGuid, ProviderName);
templates[146] = new WaitHandleWaitStopTraceData(null, 302, 39, "WaitHandleWait", WaitHandleWaitTaskGuid, 2, "Stop", ProviderGuid, ProviderName);
templates[147] = AllocationSampledTemplate(null);

s_templates = templates;
}
Expand Down Expand Up @@ -2450,6 +2473,7 @@ protected internal override void EnumerateTemplates(Func<string, string, EventFi
private static readonly Guid TieredCompilationTaskGuid = new Guid(unchecked((int)0xa77f474d), unchecked((short)0x9d0d), unchecked((short)0x4311), 0xb9, 0x8e, 0xcf, 0xbc, 0xf8, 0x4b, 0x9e, 0xf);
private static readonly Guid TypeLoadTaskGuid = new Guid(unchecked((int)0x9db1562b), unchecked((short)0x512f), unchecked((short)0x475d), 0x8d, 0x4c, 0x0c, 0x6d, 0x97, 0xc1, 0xe7, 0x3c);
private static readonly Guid WaitHandleWaitTaskGuid = new Guid(unchecked((int)0xe90049d8), unchecked((short)0x8ab8), unchecked((short)0x4799), 0xb0, 0x72, 0xee, 0xf4, 0x12, 0x65, 0xbc, 0xa4);
private static readonly Guid AllocationSamplingTaskGuid = new Guid(unchecked((int)0xcc82530e), unchecked((short)0xa21c), unchecked((short)0x4eb0), 0xa6, 0x8a, 0xfa, 0x4f, 0x7e, 0x66, 0x49, 0x8f);
private static readonly Guid JitInstrumentationDataTaskGuid = new Guid(unchecked((int)0xf8666925), unchecked((short)0x22c8), unchecked((short)0x4b70), 0xa1, 0x31, 0x07, 0x38, 0x13, 0x7e, 0x7f, 0x25);
private static readonly Guid ExecutionCheckpointTaskGuid = new Guid(unchecked((int)0x598832c8), unchecked((short)0xdf4d), unchecked((short)0x4e9e), 0xab, 0xe6, 0x2c, 0x7b, 0xf0, 0xba, 0x2d, 0xa2);
private static readonly Guid YieldProcessorMeasurementTaskGuid = new Guid(unchecked((int)0xb4afc324), unchecked((short)0xdece), unchecked((short)0x4b02), 0x86, 0xdc, 0xaa, 0xb8, 0xf2, 0x2b, 0xc1, 0xb1);
Expand Down Expand Up @@ -10393,6 +10417,102 @@ public override object PayloadValue(int index)
#endregion
}

/// <summary>
/// Data for the AllocationSampled event.
/// Emitted by the randomized allocation sampler when is enabled.
/// </summary>
public sealed class AllocationSampledTraceData : TraceEvent
{
/// <summary>Gets the allocation kind: Small Object Heap (0), Large Object Heap (1), or Pinned Object Heap (2).</summary>
public GCAllocationKind AllocationKind { get { return (GCAllocationKind)GetInt32At(0); } }
/// <summary>Gets the CLR instance ID.</summary>
public int ClrInstanceID { get { return GetInt16At(4); } }
/// <summary>Gets the runtime type handle (method-table pointer) of the allocated object.</summary>
public Address TypeID { get { return GetAddressAt(6); } }
/// <summary>Gets the fully qualified name of the allocated type.</summary>
public string TypeName { get { return GetUnicodeStringAt(HostOffset(10, 1)); } }
/// <summary>Gets the address of the allocated object.</summary>
public Address Address { get { return GetAddressAt(SkipUnicodeString(HostOffset(10, 1))); } }
/// <summary>Gets the size of the allocated object in bytes.</summary>
public long ObjectSize { get { return (long)GetInt64At(SkipUnicodeString(HostOffset(10, 1)) + HostOffset(4, 1)); } }
/// <summary>Gets the byte offset into the sampled interval at which this allocation was chosen.</summary>
public long SampledByteOffset { get { return (long)GetInt64At(SkipUnicodeString(HostOffset(10, 1)) + HostOffset(4, 1) + 8); } }

#region Private
internal AllocationSampledTraceData(Action<AllocationSampledTraceData> action, int eventID, int task, string taskName, Guid taskGuid, int opcode, string opcodeName, Guid providerGuid, string providerName)
: base(eventID, task, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName)
{
Action = action;
}
protected internal override void Dispatch()
{
Action(this);
}
protected internal override void Validate()
{
Debug.Assert(!(Version == 0 && EventDataLength != SkipUnicodeString(HostOffset(10, 1)) + HostOffset(4, 1) + 16));
Debug.Assert(!(Version > 0 && EventDataLength < SkipUnicodeString(HostOffset(10, 1)) + HostOffset(4, 1) + 16));
}
protected internal override Delegate Target
{
get { return Action; }
set { Action = (Action<AllocationSampledTraceData>)value; }
}
public override StringBuilder ToXml(StringBuilder sb)
{
Prefix(sb);
XmlAttrib(sb, "AllocationKind", AllocationKind);
XmlAttrib(sb, "ClrInstanceID", ClrInstanceID);
XmlAttribHex(sb, "TypeID", TypeID);
XmlAttrib(sb, "TypeName", TypeName);
XmlAttribHex(sb, "Address", Address);
XmlAttrib(sb, "ObjectSize", ObjectSize);
XmlAttrib(sb, "SampledByteOffset", SampledByteOffset);
sb.Append("/>");
return sb;
}

public override string[] PayloadNames
{
get
{
if (payloadNames == null)
payloadNames = new string[] { "AllocationKind", "ClrInstanceID", "TypeID", "TypeName", "Address", "ObjectSize", "SampledByteOffset" };
return payloadNames;
}
}

public override object PayloadValue(int index)
{
switch (index)
{
case 0:
return AllocationKind;
case 1:
return ClrInstanceID;
case 2:
return TypeID;
case 3:
return TypeName;
case 4:
return Address;
case 5:
return ObjectSize;
case 6:
return SampledByteOffset;
default:
Debug.Assert(false, "Bad field index");
return null;
}
}

public static ulong GetKeywords() { return 0x80000000000; }
public static string GetProviderName() { return "Microsoft-Windows-DotNETRuntime"; }
public static Guid GetProviderGuid() { return new Guid("e13c0d23-ccbc-4e12-931b-d9cc2eee27e4"); }
private event Action<AllocationSampledTraceData> Action;
#endregion
}

[Flags]
public enum GCSettingsFlags : int
{
Expand Down
101 changes: 101 additions & 0 deletions src/TraceEvent/TraceEvent.Tests/Parsing/EventPipeParsing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2728,6 +2728,107 @@ public void EventSourceEventsDispatchedUsingGetDispatcherFromFileName()
}
}
}

/// <summary>
/// Regression test for GitHub issue: AllocationSampled (EventID 303, .NET 10+) has no typed
/// schema in ClrTraceEventParser. Verifies that the event is routed through
/// <c>source.Clr.AllocationSampling</c> and that every payload field decodes correctly.
/// </summary>
[Fact]
public void AllocationSampledEventRoutesAndDecodesPayload()
{
// AllocationSampled (EventID 303) payload layout on a 64-bit trace (PointerSize=8):
// AllocationKind : UInt32 (4 bytes)
// ClrInstanceID : UInt16 (2 bytes)
// TypeID : Pointer (8 bytes on 64-bit)
// TypeName : NullTerminated UTF-16 string
// Address : Pointer (8 bytes)
// ObjectSize : UInt64 (8 bytes)
// SampledByteOffset: UInt64 (8 bytes)

const string typeName = "System.String";
const ulong expectedTypeID = 0xDEADBEEF00000001UL;
const ulong expectedAddress = 0x00007F1234560000UL;
const long expectedObjectSize = 104;
const long expectedSampledByteOffset = 8192;

// Build an in-memory nettrace (V6) containing one AllocationSampled event.
// CLR runtime events carry no embedded metadata in EventPipe traces — the
// ClrTraceEventParser pre-registers the schema, so we just declare the event ID.
EventPipeWriterV6 writer = new EventPipeWriterV6();
writer.WriteHeaders();
writer.WriteMetadataBlock(
new EventMetadata(1, "Microsoft-Windows-DotNETRuntime", "AllocationSampled", 303));
writer.WriteThreadBlock(w =>
{
w.WriteThreadEntry(999, threadId: 1, processId: 1);
});
writer.WriteEventBlock(w =>
{
w.WriteEventBlob(1, 999, 1, p =>
{
// AllocationSampled payload layout on a 64-bit trace (PointerSize=8):
// AllocationKind : UInt32 (4 bytes)
// ClrInstanceID : UInt16 (2 bytes)
// TypeID : Pointer (8 bytes on 64-bit)
// TypeName : NullTerminated UTF-16 string
// Address : Pointer (8 bytes)
// ObjectSize : UInt64 (8 bytes)
// SampledByteOffset: UInt64 (8 bytes)
p.Write((uint)0); // AllocationKind = Small (0)
p.Write((ushort)1); // ClrInstanceID = 1
p.Write(expectedTypeID); // TypeID (8-byte pointer)
p.Write(Encoding.Unicode.GetBytes(typeName)); // TypeName chars
p.Write((ushort)0); // null terminator
p.Write(expectedAddress); // Address (8-byte pointer)
p.Write((ulong)expectedObjectSize);
p.Write((ulong)expectedSampledByteOffset);
});
});
writer.WriteEndBlock();

MemoryStream stream = new MemoryStream(writer.ToArray());
EventPipeEventSource source = new EventPipeEventSource(stream);

int clrHandlerHits = 0;

// The event MUST be routed through ClrTraceEventParser (source.Clr)
source.Clr.AllocationSampling += data =>
{
clrHandlerHits++;

// Payload names
Assert.Equal(new[] { "AllocationKind", "ClrInstanceID", "TypeID", "TypeName", "Address", "ObjectSize", "SampledByteOffset" },
data.PayloadNames);

// Typed accessors
Assert.Equal(GCAllocationKind.Small, data.AllocationKind);
Assert.Equal(1, data.ClrInstanceID);
Assert.Equal(expectedTypeID, data.TypeID);
Assert.Equal(typeName, data.TypeName);
Assert.Equal(expectedAddress, data.Address);
Assert.Equal(expectedObjectSize, data.ObjectSize);
Assert.Equal(expectedSampledByteOffset, data.SampledByteOffset);

// PayloadValue round-trip
Assert.Equal(GCAllocationKind.Small, data.PayloadValue(0));
Assert.Equal(1, data.PayloadValue(1));
Assert.Equal(expectedTypeID, data.PayloadValue(2));
Assert.Equal(typeName, data.PayloadValue(3));
Assert.Equal(expectedAddress, data.PayloadValue(4));
Assert.Equal(expectedObjectSize, data.PayloadValue(5));
Assert.Equal(expectedSampledByteOffset, data.PayloadValue(6));

// PayloadByName
Assert.Equal(typeName, data.PayloadByName("TypeName"));
Assert.Equal(expectedObjectSize, data.PayloadByName("ObjectSize"));
};

source.Process();

// The event must have been dispatched through the typed CLR handler
Assert.Equal(1, clrHandlerHits);
}
}


Expand Down
Loading