Problem
AllocationSampled (EventID 303) was added to the CLR in .NET 10 via dotnet/runtime#104955. ClrTraceEventParser.cs.base has no entry for it — EventID 303 is entirely absent from the parser, which currently covers events up to approximately EventID 190.
Because CLR runtime events do not embed ETW manifest blocks in EventPipe .nettrace files (unlike EventSource events), the Dynamic parser cannot synthesise a schema at runtime. All field accessors fail — and critically, PayloadValue fails silently: it neither throws nor returns null but returns an opaque sentinel string, making it impossible to distinguish a missing schema from a valid value:
source.Dynamic.All += data =>
{
if (data.ProviderName != "Microsoft-Windows-DotNETRuntime") return;
if ((int)data.ID != 303) return;
// All of these yield nothing useful:
Console.WriteLine(data.PayloadNames?.Length); // 0 ← empty array, no fields
Console.WriteLine(data.PayloadByName("TypeName")); // null
Console.WriteLine(data.PayloadByName("ObjectSize")); // null
Console.WriteLine(data.PayloadValue(0)); // "<<<EXCEPTION_DURING_VALUE_LOOKUP IndexOutOfRangeException>>>"
};
source.Clr.All never fires for EventID 303 — the event is not routed through ClrTraceEventParser at all.
The event carries the following payload fields (from ClrEtwAll.man):
| # |
Field |
Type |
| 0 |
AllocationKind |
win:UInt32 (0=SOH, 1=LOH, 2=POH) |
| 1 |
ClrInstanceID |
win:UInt16 |
| 2 |
TypeID |
win:Pointer |
| 3 |
TypeName |
win:UnicodeString |
| 4 |
Address |
win:Pointer |
| 5 |
ObjectSize |
win:UInt64 |
| 6 |
SampledByteOffset |
win:UInt64 |
Expected behaviour
ClrTraceEventParser should expose a typed event handler (e.g. Clr.AllocationSampled) backed by an AllocationSampledTraceData class, analogous to GCAllocationTickTraceData (EventID 10). PayloadNames should return ["AllocationKind", "ClrInstanceID", "TypeID", "TypeName", "Address", "ObjectSize", "SampledByteOffset"] and all PayloadByName/PayloadValue calls should return correctly typed values.
Actual behaviour
PayloadNames returns an empty string[] (length 0, not null). PayloadByName(...) returns null. PayloadValue(0) returns the sentinel string <<<EXCEPTION_DURING_VALUE_LOOKUP IndexOutOfRangeException>>> — TraceEvent catches the internal IndexOutOfRangeException and surfaces it as a string rather than rethrowing. The only way to extract any data is raw byte parsing via TraceEvent.EventData() using the binary layout reverse-engineered from the dotnet/runtime reference consumer.
Workaround
Parse the raw payload from data.EventData() using the known binary layout. Pointer size is read from data.PointerSize.
AllocationKind(4) + ClrInstanceID(2) + TypeID(ptr) + TypeName(UTF-16LE+\0\0)
+ Address(ptr) + ObjectSize(8) + SampledByteOffset(8)
Full implementation: TryParseAllocationSampled in the repro project.
Environment
|
|
Microsoft.Diagnostics.Tracing.TraceEvent |
3.1.30 (latest) |
| .NET runtime |
10.0+ — event is never emitted on .NET 9 or earlier |
| Provider |
Microsoft-Windows-DotNETRuntime |
| Keyword |
AllocationSamplingKeyword = 0x80000000000 |
Repro
Self-contained .NET 10 console project: https://github.com/baal2000/AllocationSampledRepro
git clone https://github.com/baal2000/AllocationSampledRepro
cd AllocationSampledRepro
dotnet run
The project instruments itself via DiagnosticsClient with AllocationSamplingKeyword, allocates 200 000 objects to guarantee sampled events, then parses the resulting trace twice — once via the native TraceEvent API (broken), once via EventData() raw bytes (workaround).
Actual console output
CI-verified on GitHub-hosted ubuntu-latest, .NET 10.0.5, TraceEvent 3.1.30 — job log:
══ Native TraceEvent API (broken) ═════════════════════════════════════
PayloadNames: <empty array>
PayloadByName("TypeName"): <null>
PayloadByName("ObjectSize"): <null>
PayloadValue(0): <<<EXCEPTION_DURING_VALUE_LOOKUP IndexOutOfRangeException>>>
source.Clr.All hits for EventID 303: 0 (0 = not routed through ClrTraceEventParser)
source.Dynamic.All hits for EventID 303: 88
══ Raw EventData() workaround (works) ══════════════════════════════════
[1] TypeName=InvokeFunc_RefArgs ObjectSize=64 bytes
[2] TypeName=System.Reflection.RuntimeParameterInfo ObjectSize=88 bytes
[3] TypeName=System.String ObjectSize=104 bytes
[4] TypeName=System.Reflection.ParameterInfo[] ObjectSize=120 bytes
[5] TypeName=System.Reflection.RuntimeParameterInfo ObjectSize=88 bytes
Total events decoded via raw bytes: 100% (88 / 88)
CI: PASS
[1] Issue confirmed — PayloadNames empty, Clr.All: 0 hits for EventID 303
[2] Workaround confirmed — raw bytes decoded 100% of sampled events
References
Problem
AllocationSampled(EventID 303) was added to the CLR in .NET 10 via dotnet/runtime#104955.ClrTraceEventParser.cs.basehas no entry for it — EventID 303 is entirely absent from the parser, which currently covers events up to approximately EventID 190.Because CLR runtime events do not embed ETW manifest blocks in EventPipe
.nettracefiles (unlikeEventSourceevents), theDynamicparser cannot synthesise a schema at runtime. All field accessors fail — and critically,PayloadValuefails silently: it neither throws nor returnsnullbut returns an opaque sentinel string, making it impossible to distinguish a missing schema from a valid value:source.Clr.Allnever fires for EventID 303 — the event is not routed throughClrTraceEventParserat all.The event carries the following payload fields (from
ClrEtwAll.man):AllocationKindwin:UInt32(0=SOH, 1=LOH, 2=POH)ClrInstanceIDwin:UInt16TypeIDwin:PointerTypeNamewin:UnicodeStringAddresswin:PointerObjectSizewin:UInt64SampledByteOffsetwin:UInt64Expected behaviour
ClrTraceEventParsershould expose a typed event handler (e.g.Clr.AllocationSampled) backed by anAllocationSampledTraceDataclass, analogous toGCAllocationTickTraceData(EventID 10).PayloadNamesshould return["AllocationKind", "ClrInstanceID", "TypeID", "TypeName", "Address", "ObjectSize", "SampledByteOffset"]and allPayloadByName/PayloadValuecalls should return correctly typed values.Actual behaviour
PayloadNamesreturns an emptystring[](length 0, notnull).PayloadByName(...)returnsnull.PayloadValue(0)returns the sentinel string<<<EXCEPTION_DURING_VALUE_LOOKUP IndexOutOfRangeException>>>— TraceEvent catches the internalIndexOutOfRangeExceptionand surfaces it as a string rather than rethrowing. The only way to extract any data is raw byte parsing viaTraceEvent.EventData()using the binary layout reverse-engineered from the dotnet/runtime reference consumer.Workaround
Parse the raw payload from
data.EventData()using the known binary layout. Pointer size is read fromdata.PointerSize.Full implementation:
TryParseAllocationSampledin the repro project.Environment
Microsoft.Diagnostics.Tracing.TraceEventMicrosoft-Windows-DotNETRuntimeAllocationSamplingKeyword = 0x80000000000Repro
Self-contained .NET 10 console project: https://github.com/baal2000/AllocationSampledRepro
git clone https://github.com/baal2000/AllocationSampledRepro cd AllocationSampledRepro dotnet runThe project instruments itself via
DiagnosticsClientwithAllocationSamplingKeyword, allocates 200 000 objects to guarantee sampled events, then parses the resulting trace twice — once via the native TraceEvent API (broken), once viaEventData()raw bytes (workaround).Actual console output
CI-verified on GitHub-hosted
ubuntu-latest, .NET 10.0.5, TraceEvent 3.1.30 — job log:References
AllocationSampledin .NET 10ClrEtwAll.man— canonical ETW manifest defining EventID 303 payload schemaAllocationProfiler/Program.cs— dotnet/runtime reference consumer using raw byte parsingClrTraceEventParser.cs.base— file requiring the new typed event definition