Skip to content

Handle minimal MethodTables in dotnet-pgo trace processing#127818

Merged
DrewScoggins merged 3 commits into
dotnet:mainfrom
DrewScoggins:fix-dotnet-pgo-minimal-mt
May 22, 2026
Merged

Handle minimal MethodTables in dotnet-pgo trace processing#127818
DrewScoggins merged 3 commits into
dotnet:mainfrom
DrewScoggins:fix-dotnet-pgo-minimal-mt

Conversation

@DrewScoggins
Copy link
Copy Markdown
Member

BulkType ETW events emit TypeNameID = th.GetCl() (eventtrace_bulktype.cpp). For "minimal" MethodTables created by CreateMinimalMethodTable (used for Reflection.Emit DynamicMethod hosts in dynamicmethod.cpp and the IL stub cache in ilstubcache.cpp), SetCl is never called, so GetCl returns mdTypeDefNil (0x02000000, rid 0).

dotnet-pgo previously treated this as a valid TypeDef token, and the lazy EcmaType.Name access would throw BadImageFormatException: Read out of bounds because rid 0 is before the typedef table.

Validate the typedef row before constructing the TypeDefinitionHandle and treat out-of-range rids (rid 0, or rid greater than the typedef table size) as unresolvable dynamic types so trace processing can continue.

This started failing on the SDK training pipeline after #126330 (Migrate CLR to COM stubs to be IL stubs) and #125352 (Move struct marshaling to transient IL) flowed through, which routed new categories of stubs through the minimal-MT path and added their BulkType events to the trace.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates dotnet-pgo’s trace-to-type resolution to gracefully handle BulkType ETW events that reference “minimal” MethodTables (and other invalid TypeDef rids) by validating the TypeDef row id before constructing a TypeDefinitionHandle, preventing BadImageFormatException during lazy metadata access and allowing trace processing to continue.

Changes:

  • Extract the TypeDef row id from TypeNameID and validate it against the TypeDef table bounds.
  • Treat rid 0 (e.g., 0x02000000) and out-of-range rids as unresolvable types by returning null and marking dependsOnKnownNonLoadableType.

Comment thread src/coreclr/tools/dotnet-pgo/TraceRuntimeDescToTypeSystemDesc.cs Outdated
@jkotas jkotas self-requested a review May 20, 2026 18:20
Comment thread src/coreclr/tools/dotnet-pgo/TraceRuntimeDescToTypeSystemDesc.cs Outdated
Copilot AI review requested due to automatic review settings May 20, 2026 18:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

Comment thread src/coreclr/tools/dotnet-pgo/TraceRuntimeDescToTypeSystemDesc.cs
DrewScoggins and others added 3 commits May 21, 2026 15:30
BulkType ETW events emit `TypeNameID = th.GetCl()` (eventtrace_bulktype.cpp). For "minimal" MethodTables created by `CreateMinimalMethodTable` (used for Reflection.Emit DynamicMethod hosts in dynamicmethod.cpp and the IL stub cache in ilstubcache.cpp), `SetCl` is never called, so `GetCl` returns `mdTypeDefNil` (0x02000000, rid 0).

dotnet-pgo previously treated this as a valid TypeDef token, and the lazy `EcmaType.Name` access would throw `BadImageFormatException: Read out of bounds` because rid 0 is before the typedef table.

Validate the typedef row before constructing the `TypeDefinitionHandle` and treat out-of-range rids (rid 0, or rid greater than the typedef table size) as unresolvable dynamic types so trace processing can continue.

This started failing on the SDK training pipeline after dotnet#126330 (Migrate CLR to COM stubs to be IL stubs) and dotnet#125352 (Move struct marshaling to transient IL) flowed through, which routed new categories of stubs through the minimal-MT path and added their BulkType events to the trace.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split the typedefRow == 0 (minimal MethodTable) and out-of-range rid
cases into separate checks. The rid == 0 case is expected for dynamic
types and remains silent. Out-of-range rids now emit a warning via
Program.PrintWarning so users can diagnose trace/assembly mismatches.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The callers already wrap resolution in try/catch and report exceptions.
Silently masking a mismatch as a non-loadable type hides real errors.
Dropping the range check is consistent with the rest of the method
which throws for invalid trace content (e.g. Invalid TypeParameterCount).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@DrewScoggins DrewScoggins force-pushed the fix-dotnet-pgo-minimal-mt branch from e572619 to b33775c Compare May 21, 2026 22:30
@DrewScoggins DrewScoggins merged commit 41bb922 into dotnet:main May 22, 2026
110 of 112 checks passed
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-preview6 milestone May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants