Skip to content

Add typed schema for AllocationSampled (EventID 303, .NET 10+) in ClrTraceEventParser#2388

Merged
brianrob merged 4 commits intomainfrom
copilot/add-typed-schema-for-event-303
Mar 20, 2026
Merged

Add typed schema for AllocationSampled (EventID 303, .NET 10+) in ClrTraceEventParser#2388
brianrob merged 4 commits intomainfrom
copilot/add-typed-schema-for-event-303

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 19, 2026

Fixes #2385.

AllocationSampled (EventID 303), added in .NET 10 via dotnet/runtime#104955, had no entry in ClrTraceEventParser. Because CLR runtime events carry no embedded manifest in EventPipe .nettrace files, the Dynamic parser cannot synthesise a schema — all PayloadByName/PayloadValue calls silently return null or a sentinel exception string, and source.Clr.All never fires for this event.

Schema definition (from ClrEtwAll.man)

Task AllocationSampling (value=40, GUID {CC82530E-A21C-4EB0-A68A-FA4F7E66498F}), EventID 303, opcode 0 (Info), keyword AllocationSamplingKeyword = 0x80000000000.

Field Type
AllocationKind win:UInt32 (0=SOH, 1=LOH, 2=POH)
ClrInstanceID win:UInt16
TypeID win:Pointer
TypeName win:UnicodeString
Address win:Pointer
ObjectSize win:UInt64
SampledByteOffset win:UInt64

Changes

  • ClrTraceEventParser.Keywords — added AllocationSampling = 0x80000000000
  • ClrTraceEventParser — added public event Action<AllocationSampledTraceData> AllocationSampling (EventID 303, Task 40, Opcode 0). The property is named AllocationSampling rather than AllocationSampled because opcode=0 (Info) collapses EventName to just the task name, which must match the internal template/event validator
  • AllocationSampledTraceData — new sealed class with all 7 typed payload accessors, PayloadNames, PayloadValue, ToXml, Validate, and GetKeywords. Pointer-relative offsets use the HostOffset(10, 1) convention (10 = 6 fixed bytes + 4-byte minimum for TypeID pointer; HostOffset adds the remaining 4 bytes on 64-bit traces)
  • EnumerateTemplates — array size 147→148, new entry wired in
  • ClrTraceEventParser.cs.base — mirrored all changes

After this change:

source.Clr.AllocationSampled += data =>
{
    // All fields now decode correctly
    Console.WriteLine(data.TypeName);           // e.g. "System.String"
    Console.WriteLine(data.ObjectSize);         // e.g. 104
    Console.WriteLine(data.AllocationKind);     // GCAllocationKind.Small
    Console.WriteLine(data.PayloadValue(0));    // GCAllocationKind.Small (no exception sentinel)
};

Test

Added AllocationSampledEventRoutesAndDecodesPayload in EventPipeParsing.cs: builds a minimal in-memory V6 nettrace containing one AllocationSampled event with a known binary payload and asserts the event dispatches through source.Clr.AllocationSampling, all 7 typed accessors decode correctly, and PayloadByName/PayloadValue round-trip cleanly.

Original prompt

This section details on the original issue you should resolve

<issue_title>AllocationSampled (EventID 303, .NET 10+) has no typed schema in ClrTraceEventParser</issue_title>
<issue_description>## 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.Reflectio...

</details>


> **Custom agent used: Issue Triager**
> Investigates, reproduces, and fixes issues in PerfView and TraceEvent.



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes microsoft/perfview#2385

<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for you](https://github.com/microsoft/perfview/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.

…TraceEventParser

Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com>
Copilot AI changed the title [WIP] Add typed schema for AllocationSampled EventID 303 Add typed schema for AllocationSampled (EventID 303, .NET 10+) in ClrTraceEventParser Mar 19, 2026
Copilot AI requested a review from brianrob March 19, 2026 23:45
Copy link
Copy Markdown
Member

@brianrob brianrob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot, please address these comments.


// Also subscribe to Dynamic.All to confirm the event does fire (it might
// still appear in the Dynamic stream as a fallback, that is acceptable)
source.Dynamic.All += data =>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the dynamic test and dynamicHandlerHits.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 7569f1a — removed the Dynamic.All subscription and dynamicHandlerHits variable.

GCSampledObjectAllocationLow = 0x2000000,
PerfTrack = 0x20000000,
Stack = 0x40000000,
AllocationSampling = 0x80000000000,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Undo the changes in this file.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 7569f1a — reverted ClrTraceEventParser.cs.base to its pre-PR state.

WaitHandle = 0x40000000000,

/// <summary>
/// Events for randomized allocation sampling (AllocationSampled, EventID 303). Requires .NET 10+.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Events for randomized allocation sampling (AllocationSampled, EventID 303). Requires .NET 10+.
/// Events for randomized allocation sampling.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 7569f1a.

}

/// <summary>
/// Data for the AllocationSampled event (EventID 303, .NET 10+).
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Data for the AllocationSampled event (EventID 303, .NET 10+).
/// Data for the AllocationSampled event.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 7569f1a.

/// <summary>
/// Data for the AllocationSampled event (EventID 303, .NET 10+).
/// Emitted by the randomized allocation sampler when <see cref="ClrTraceEventParser.Keywords.AllocationSampling"/>
/// is enabled on the <c>Microsoft-Windows-DotNETRuntime</c> provider.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// is enabled on the <c>Microsoft-Windows-DotNETRuntime</c> provider.
/// is enabled.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 7569f1a.

…namic handler test

Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 20, 2026

@copilot, please address these comments.

All comments addressed in 7569f1a: reverted .base, simplified both doc comments, and removed the Dynamic.All handler + dynamicHandlerHits from the test.

Copilot AI requested a review from brianrob March 20, 2026 02:21
/// <summary>
/// Data for the AllocationSampled event.
/// Emitted by the randomized allocation sampler when
/// is enabled.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot, please move the last two words on this line to the line above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 7dcdd30 — merged "is enabled." onto the preceding line.

…c comment

Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com>
Copilot AI requested a review from brianrob March 20, 2026 02:26
@brianrob brianrob marked this pull request as ready for review March 20, 2026 02:34
@brianrob brianrob requested a review from a team as a code owner March 20, 2026 02:34
@brianrob brianrob enabled auto-merge (squash) March 20, 2026 02:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AllocationSampled (EventID 303, .NET 10+) has no typed schema in ClrTraceEventParser

3 participants