GCRoot: Report GCFrame and in-flight exception objects#129145
Open
leculver wants to merge 4 commits into
Open
Conversation
…nces WalkStackReferences (GetStackReferences) reports the per-thread GC roots that the GC scans in gcenv.ee.cpp ScanStackRoots. Alongside the frame references it now also reports: - The thread's GCFrame (GCPROTECT) chain: each GCFrame keeps a set of object references alive across a runtime operation. New data descriptors expose the chain -- Thread.GCFrame (m_pGCFrame) plus a GCFrame type (Next/ObjRefs/NumObjRefs/GCFlags) in vm/frames.h, vm/threads.h, datadescriptor.inc, and the managed Data/GCFrame.cs + DataType. The managed Thread.GCFrame field is optional, so a target that does not describe it is skipped. Interior promotion is applied when GCFlags != 0, matching GCFrame::GcScanRoots. - The thread's exception-tracking (ExInfo) chain: the current in-flight exception plus any superseded/nested ones, via the existing IException.GetNestedExceptionInfo contract. Adds two dump-based StackReferenceDumpTests with dedicated debuggees. NestedException builds a superseded exception chain and verifies the nested exception (reachable only through the ExInfo chain) is reported. GCProtect crashes inside an AppDomain.AssemblyResolve handler the runtime invokes while holding a GCPROTECT frame, and verifies a GCFrame-protected object is reported. Both use Full dumps and pass against a locally built runtime.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR extends cDAC stack reference enumeration to also report two additional GC root sources that were previously missed: (1) objects protected by the thread’s GCFrame (GCPROTECT) chain and (2) in-flight exception objects held on the thread’s ExInfo chain. It also adds new dump debuggees and dump tests to validate these root sources show up in IStackWalk.WalkStackReferences.
Changes:
- Add cDAC data contract support for
GCFrameand expose the thread’sm_pGCFramechain via the Thread contract. - Extend
StackWalk_1.WalkStackReferencesto report GCFrame roots and ExInfo (exception tracker) roots. - Add two new dump debuggees and corresponding dump tests covering GCFrame roots and nested in-flight exceptions.
Show a summary per file
| File | Description |
|---|---|
| src/native/managed/cdac/tests/DumpTests/StackReferenceDumpTests.cs | Adds new dump-based tests for ExInfo and GCFrame root reporting; GCFrame test needs to avoid potential false positives. |
| src/native/managed/cdac/tests/DumpTests/Debuggees/NestedException/Program.cs | New debuggee that crashes while nested exceptions are still in flight. |
| src/native/managed/cdac/tests/DumpTests/Debuggees/NestedException/NestedException.csproj | Registers the NestedException debuggee and requests Full dumps. |
| src/native/managed/cdac/tests/DumpTests/Debuggees/GCProtect/Program.cs | New debuggee that FailFasts inside an AssemblyResolve handler under a native GCPROTECT scope. |
| src/native/managed/cdac/tests/DumpTests/Debuggees/GCProtect/GCProtect.csproj | Registers the GCProtect debuggee and requests Full dumps. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/DataType.cs | Adds GCFrame to the public DataType enum (new public surface). |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Thread.cs | Adds an optional GCFrame pointer field to the managed Thread data contract. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GCFrame.cs | New managed data contract type describing the native GCFrame layout. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs | Reports GCFrame-protected roots and ExInfo-tracked exception roots as stack references. |
| src/coreclr/vm/threads.h | Exposes Thread::m_pGCFrame offset via cdac_data<Thread>. |
| src/coreclr/vm/frames.h | Adds cdac_data<GCFrame> describing the native GCFrame layout. |
| src/coreclr/vm/datadescriptor/datadescriptor.inc | Adds Thread.GCFrame field and a new GCFrame CDAC type descriptor. |
Copilot's findings
- Files reviewed: 12/12 changed files
- Comments generated: 4
…ng, match GCFrame roots by Source - ReportGCFrameRoots: forward the full GCFrame.GCFlags bitmask to the promote func via (GcScanFlags) cast, mirroring GCFrame::GcScanRoots, instead of remapping any nonzero value to GC_CALL_INTERIOR. - ReportGCFrameRoots/ReportExceptionTrackerRoots: wrap each helper body in try/catch so a bad read yields partial results instead of failing the whole WalkStackReferences walk, matching the per-frame loop. - GCProtect_GCFrameRootsAreReported: identify GCFrame roots by matching StackReferenceData.Source against the thread's actual GCFrame chain node addresses, instead of the SP-null heuristic that also matches ExInfo-sourced roots.
- Report GCFrame/ExInfo roots with a non-zero StackPointer (the on-stack node address) instead of 0, matching native DacStackReferenceWalker which gives frame-sourced roots a real SP that SOSDacImpl forwards. - Move DataType.GCFrame from the exception-type group into the Frame group where it belongs (GCFrame is a Frame subtype). DataType lookups are name-keyed, so ordering is not load-bearing. - Document Thread.GCFrame and the GCFrame type descriptors, plus the new GCFrame/ExInfo root reporting, in the Thread and StackWalk contract docs. - Guard the test's MethodTable read with try/catch (a reported GCFrame root may be an interior pointer), mirroring GCRoots_RefsPointToValidObjects.
Bring the in-process cdacstress oracle into parity with GetStackReferences. WalkStackReferences now reports the GCFrame (GCPROTECT) chain and the in-flight ExInfo chain, but CollectRuntimeStackRefs skipped GCFrame outright and never collected ExInfo, so any thread holding a live GCPROTECT frame or an in-flight exception produced a spurious cDAC/runtime count mismatch. Mirror ScanStackRoots on the runtime side so the two sets line up. Also correct the StackWalk data-contract doc: WalkStackReferences mirrors the GC's own ScanStackRoots (a superset of DacStackReferenceWalker, which covers only the per-frame roots), and add the Exception contract dependency.
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.
Threads' GCFrame objects were not being reported correctly as GCRoots. Similarly, an in-flight exception was also not being reported, though it typically has other roots it's just good to fix the gap.
This is my first real cdac change, so detailed feedback and nitpicks are very appreciated.