Emit a resolvable type name for Runtime Async continuations in GC heap dumps - #131684
Emit a resolvable type name for Runtime Async continuations in GC heap dumps#131684tommcdon wants to merge 5 commits into
Conversation
…p dumps The GCHeapDump BulkType event set fixedSizedData.TypeNameID = TypeHandle::GetCl() and the type name from TypeHandle::GetName(). For a metadata-less Runtime Async continuation MethodTable, GetCl() is the nil TypeDef token (0x02000000) and GetName() yields an empty string, so dotnet-gcdump renders every continuation as "Type(0x02000000)" with no usable name. Describe continuations using the base Continuation type (via UpCastTypeIfNeeded) for the module id, name token, and name, while keeping the per-object TypeID as the derived MethodTable so GCBulkNode entries still resolve to this record. This also avoids MethodTable::GetModule()'s IsContinuationWithoutMetadata() assert on checked builds. Validated with dotnet-gcdump against a process holding 128 live continuations: before, 128 objects showed as "Type(0x02000000)"; after, they show as "System.Runtime.CompilerServices.Continuation" and the capture still succeeds. Relates to dotnet#120800 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Regression test for dotnet#120800. Creates live Runtime Async continuations (metadata-less continuation MethodTables), captures a GC heap snapshot over EventPipe (GCHeapSnapshot keyword), and asserts every logged type in the GCBulkType events carries a non-empty name and that a continuation type is observed. Before the accompanying runtime fix, the metadata-less continuations were logged with an empty name (rendered as "Type(0x02000000)" by dotnet-gcdump); the test's _emptyTypeNameCount==0 assertion guards that. Models the existing gcdump test; enables Runtime Async via <Features>runtime-async=on</Features> plus a DOTNET_RuntimeAsync=1 CLRTestEnvironmentVariable, and skips itself if Runtime Async is not active. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
There was a problem hiding this comment.
Pull request overview
This PR updates CoreCLR GC heap dump type emission so runtime-async metadata-less continuation subtypes are reported with a resolvable, non-empty type name (by using an upcast “named” TypeHandle for module/token/name), and adds an EventPipe regression test to cover the scenario.
Changes:
- Adjust
GCHeapDumpBulkType logging to useTypeHandle::UpCastTypeIfNeeded()forModuleID,TypeNameID, and the emitted type name, while keepingTypeIDas the actual objectMethodTable. - Add a new EventPipe test app that creates suspended runtime-async continuations and validates that BulkType entries never have an empty
TypeName. - Add a dedicated csproj configuring the feature flag and runtime-async enablement for the test.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/coreclr/vm/eventtrace_bulktype.cpp | Emits module/token/name using an upcast type for metadata-less runtime-async continuation subtypes. |
| src/tests/tracing/eventpipe/gcdump_runtimeasync/gcdump_runtimeasync.csproj | New test project enabling runtime-async=on and setting DOTNET_RuntimeAsync=1. |
| src/tests/tracing/eventpipe/gcdump_runtimeasync/gcdump_runtimeasync.cs | New regression test that captures a GC heap snapshot and asserts type names are non-empty (and continuation types appear). |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e8cd920d-9e3e-4b34-b898-de7bdad68f24
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/tests/tracing/eventpipe/gcdump_runtimeasync/gcdump_runtimeasync.csproj:10
- The comment says runtime-async is enabled via an AppContext switch set in Main, but the test doesn't set any AppContext switch. The only runtime enablement here appears to be the DOTNET_RuntimeAsync test environment variable below, so the comment is misleading.
<!-- Emit Runtime Async method bodies so the runtime creates the dynamic metadata-less
continuation MethodTables this test exercises. The runtime side is enabled in-process
via the AppContext switch set in Main. -->
<Features>$(Features);runtime-async=on</Features>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/tests/tracing/eventpipe/gcdump_runtimeasync/gcdump_runtimeasync.cs:72
- This runtime-async guard makes the test silently pass (return 100) if runtime-async isn’t actually enabled, which can make the test vacuous and hide regressions. It also risks a NullReferenceException if reflection ever fails to locate the method. Prefer treating this as a hard failure (or use attribute-based skip logic) and avoid the possible null dereference.
// Verify Runtime Async is actually active; otherwise the test would be vacuous.
var mi = typeof(GCDumpRuntimeAsyncTest).GetMethod(nameof(Suspended),
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
bool runtimeAsync = (mi.MethodImplementationFlags & System.Reflection.MethodImplAttributes.Async) != 0;
if (!runtimeAsync)
The
GCHeapDumpBulkTypeevent setfixedSizedData.TypeNameID = TypeHandle::GetCl()and the type name fromTypeHandle::GetName(). For a metadata-less Runtime Async continuationMethodTable,GetCl()is thenilTypeDeftoken (0x02000000) andGetName()yields an empty string, so eventpipe readers such as dotnet-gcdump render every continuation asType(0x02000000)with no usable name.Describe continuations using the base
Continuationtype (viaUpCastTypeIfNeeded) for the module id, name token, and name, while keeping the per-objectTypeIDas the derivedMethodTablesoGCBulkNodeentries still resolve to this record. Thisalso avoids MethodTable::GetModule()'s IsContinuationWithoutMetadata() assert on
checked builds.
Validated with
dotnet-gcdumpagainst a process holding 128 live continuations:Type(0x02000000)System.Runtime.CompilerServices.Continuationand the capture still succeeds.Adds a test case for runtime-async to cover this scenario.
Relates to #120800