Handle minimal MethodTables in dotnet-pgo trace processing#127818
Merged
DrewScoggins merged 3 commits intoMay 22, 2026
Conversation
Contributor
There was a problem hiding this comment.
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
TypeNameIDand validate it against the TypeDef table bounds. - Treat rid 0 (e.g.,
0x02000000) and out-of-range rids as unresolvable types by returningnulland markingdependsOnKnownNonLoadableType.
jkotas
reviewed
May 5, 2026
jkotas
approved these changes
May 20, 2026
jkotas
reviewed
May 20, 2026
jkotas
approved these changes
May 20, 2026
davidwrighton
approved these changes
May 20, 2026
This was referenced May 21, 2026
Open
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>
e572619 to
b33775c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
BulkType ETW events emit
TypeNameID = th.GetCl()(eventtrace_bulktype.cpp). For "minimal" MethodTables created byCreateMinimalMethodTable(used for Reflection.Emit DynamicMethod hosts in dynamicmethod.cpp and the IL stub cache in ilstubcache.cpp),SetClis never called, soGetClreturnsmdTypeDefNil(0x02000000, rid 0).dotnet-pgo previously treated this as a valid TypeDef token, and the lazy
EcmaType.Nameaccess would throwBadImageFormatException: Read out of boundsbecause rid 0 is before the typedef table.Validate the typedef row before constructing the
TypeDefinitionHandleand 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.