[cDAC] Add GC stress verification infrastructure and stack walk fixes - #6

Closed
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4
Closed

[cDAC] Add GC stress verification infrastructure and stack walk fixes#6
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4

Conversation

@max-charlamb

Copy link
Copy Markdown
Owner

Summary

Add comprehensive cDAC stress verification infrastructure (DOTNET_CdacStress) that compares the cDAC's stack reference enumeration and stack walk against the legacy DAC and runtime.

Changes

Stack Walk Fixes

  • PromoteCallerStack: GCRefMap + MetaSig + DynamicHelperFrame scanning for stub frame GC roots
  • GetExceptionClauses: Fix code start offset calculation and AMD64Unwinder null check
  • ParentOfFuncletStackFrame: Wire up funclet parent frame flag for GC reporting
  • SkipCurrentFrameInCheck: Fix regression from 650ffb5 that permanently lost InlinedCallFrames from the iterator
  • SW_SKIPPED_FRAME context restoration: Call UpdateContextFromFrame for skipped Frames
  • FilterContext: Read DebuggerFilterContext/ProfilerFilterContext matching native DAC behavior
  • IsAtFirstPassExceptionThrowSite: Suppress throw-site refs during EH first-pass

Dead Code Removal

CdacStress Infrastructure

  • DOTNET_CdacStress config with CdacStress<T>::MaybeVerify template pattern (compiles to no-op without HAVE_GCCOVER)
  • Bit flags: trigger points (ALLOC=0x1, INSTR=0x4), validation types (REFS=0x10, WALK=0x20, USE_DAC=0x40), modifiers (UNIQUE=0x100)
  • Three-way comparison: Load legacy DAC in-process via InProcessDataTarget, compare cDAC vs DAC vs RT
  • CompareStackWalks: Frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr comparison
  • 7 debuggee test apps: BasicAlloc, DeepStack, Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive

Test Results

ModeNon-EH debuggeesExceptionHandling
INSTR (0x14 + GCStress=0x4)0 failures0-2 failures
ALLOC+REFS+UNIQUE (0x111)0 failures0 failures
ALLOC+REFS (0x11)0 failures2-4 failures (known issue)
Walk comparison (0x21)0 mismatchesN/A

Known Issue

See known-issues.md — during EH first-pass dispatch, m_pFrame can be FRAME_TOP when the cDAC's AMD64Unwinder cannot unwind native frames (it only handles managed code ranges). The legacy DAC succeeds via OS-level VirtualUnwindToFirstManagedCallFrame.

Max Charlamband others added 6 commits March 25, 2026 15:27
Add GCRefMap-based and MetaSig-based scanning for stub frames in the cDAC
stack walker. This implements Frame::GcScanRoots dispatch for:
- StubDispatchFrame: GCRefMap path (when cached) + MetaSig fallback
- ExternalMethodFrame: GCRefMap path
- PrestubMethodFrame / CallCountingHelperFrame: MetaSig path
- DynamicHelperFrame: Flag-based register scanning
Key components:
- GCRefMapDecoder: managed port of native gcrefmap.h bitstream decoder
- CorSigParser: ECMA-335 signature parser with GC type classification,
including ELEMENT_TYPE_INTERNAL for dynamic method signatures
- OffsetFromGCRefMapPos: maps GCRefMap positions to TransitionBlock offsets
- Platform-guarded TransitionBlock offset globals in datadescriptor.inc
Bug fixes found during implementation:
- ScanFrameRoots was passing frame address to GetFrameName instead of the
frame's VTable identifier, causing all frames to hit the no-op default
- Added per-frame error isolation so one bad frame doesn't abort the walk
Reduces GC stress failure delta from 3 to 1 for all 55 remaining failures.
The remaining delta is from RangeList-based code heap resolution (separate issue).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix GetExceptionClauses to use code start for offset calculation.
Wire up ParentOfFuncletStackFrame and unwind-target-PC override
for catch handler GC reporting. Fix AMD64Unwinder null check.
Add GC stress verification infrastructure that compares cDAC stack
reference enumeration against the runtime at GC stress points:
- DAC-like callback for runtime stack ref collection
- xUnit test framework with 7 debuggees (BasicAlloc, DeepStack,
Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive)
- Step throttling, allocation-point hooks, and reentrancy guard
- On-demand build subset and project exclusion from main test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove code referencing runtime features that were removed in PR dotnet#119863
(Move coreclr EH second pass to native code):
- ForceGcReportingStage enum and related TODO comments
- ShouldSaveFuncletInfo, ShouldParentToFuncletReportSavedFuncletSlots,
IsFilterFunclet, IsFilterFuncletCached fields from GCFrameData
- funcletNotSeen, foundFirstFunclet variables
- Unreachable ExInfo block gated by '&& false'
- Dead PeekByte() and ClassifyElementType() from CorSigParser
- Inner try/catch around ScanFrameRoots (outer catch suffices)
- Exclude GCStressTests from main cDAC test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce a separate DOTNET_CdacStress config with bit flags for
controlling cDAC stack reference verification independently of GCStress:
0x1 ALLOC - verify at allocation points (fast, no JIT overhead)
0x2 GC - verify at GC trigger points (future)
0x4 UNIQUE - deduplicate by (IP, SP) hash
0x8 INSTR - verify at instruction traps (needs GCStress=0x4)
Follow the GCStress<T> template pattern with CdacStress<T>::MaybeVerify
that compiles to nothing when HAVE_GCCOVER is not defined, eliminating
#ifdef guards at call sites.
Rename CdacGcStress -> CdacStress (class, files, config vars) to reflect
that this verifies the cDAC's stack walk, not GC behavior.
Legacy DOTNET_GCStress=0x20 continues to work (maps to CDACSTRESS_ALLOC).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match the native DAC behavior for both ClrDataStackWalk::Init and
DacStackReferenceWalker::WalkStack: check the thread's
DebuggerFilterContext and ProfilerFilterContext before falling back
to TryGetThreadContext. During debugger breaks or profiler stack
walks, these contexts hold the correct managed frame state.
Add DebuggerFilterContext and ProfilerFilterContext fields to the
Thread data descriptor and Data.Thread class.
Add diagnostic logging for unique Source IPs in cDAC stress failures
to show which frames the cDAC actually walked.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix SkipDuplicateActiveICF regression from base branch commit 650ffb5:
restore one-shot SkipCurrentFrameInCheck behavior so InlinedCallFrames
are not permanently lost from the FrameIterator.
Fix SW_SKIPPED_FRAME context restoration: call UpdateContextFromFrame
for skipped Frames so SoftwareExceptionFrame context is restored.
Add IsAtFirstPassExceptionThrowSite to suppress throw-site refs during
exception first-pass dispatch, matching legacy DAC behavior.
Restructure CdacStress flags into trigger points (ALLOC/GC/INSTR),
validation types (REFS/WALK/USE_DAC), and modifiers (UNIQUE).
Add three-way comparison infrastructure:
- Load legacy DAC (mscordaccore.dll) in-process via InProcessDataTarget
- CompareStackWalks: frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr
- CompareRefSets: two-phase ref matching (stack + register refs)
- CollectStackRefs: merged cDAC/DAC collection into single function
- FilterAndDedup: combined interior pointer filter + dedup
Refactor VerifyAtStressPoint into clean 5-step flow:
1. Collect raw refs (cDAC always, DAC if USE_DAC, RT always)
2. Compare cDAC vs DAC raw (before filtering)
3. Filter cDAC refs and compare vs RT
4. Pass/fail based on RT match; DAC mismatch logged separately
5. Log all three ref sets on failure
Update known-issues.md with current findings: single remaining issue
is m_pFrame=FRAME_TOP during EH first-pass dispatch where the cDAC
cannot unwind through native frames.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 24, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

1 participant

@max-charlamb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

[cDAC] Add GC stress verification infrastructure and stack walk fixes - #6

Closed
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4
Closed

[cDAC] Add GC stress verification infrastructure and stack walk fixes#6
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4

Conversation

@max-charlamb

Copy link
Copy Markdown
Owner

Summary

Add comprehensive cDAC stress verification infrastructure (DOTNET_CdacStress) that compares the cDAC's stack reference enumeration and stack walk against the legacy DAC and runtime.

Changes

Stack Walk Fixes

  • PromoteCallerStack: GCRefMap + MetaSig + DynamicHelperFrame scanning for stub frame GC roots
  • GetExceptionClauses: Fix code start offset calculation and AMD64Unwinder null check
  • ParentOfFuncletStackFrame: Wire up funclet parent frame flag for GC reporting
  • SkipCurrentFrameInCheck: Fix regression from 650ffb5 that permanently lost InlinedCallFrames from the iterator
  • SW_SKIPPED_FRAME context restoration: Call UpdateContextFromFrame for skipped Frames
  • FilterContext: Read DebuggerFilterContext/ProfilerFilterContext matching native DAC behavior
  • IsAtFirstPassExceptionThrowSite: Suppress throw-site refs during EH first-pass

Dead Code Removal

CdacStress Infrastructure

  • DOTNET_CdacStress config with CdacStress<T>::MaybeVerify template pattern (compiles to no-op without HAVE_GCCOVER)
  • Bit flags: trigger points (ALLOC=0x1, INSTR=0x4), validation types (REFS=0x10, WALK=0x20, USE_DAC=0x40), modifiers (UNIQUE=0x100)
  • Three-way comparison: Load legacy DAC in-process via InProcessDataTarget, compare cDAC vs DAC vs RT
  • CompareStackWalks: Frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr comparison
  • 7 debuggee test apps: BasicAlloc, DeepStack, Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive

Test Results

ModeNon-EH debuggeesExceptionHandling
INSTR (0x14 + GCStress=0x4)0 failures0-2 failures
ALLOC+REFS+UNIQUE (0x111)0 failures0 failures
ALLOC+REFS (0x11)0 failures2-4 failures (known issue)
Walk comparison (0x21)0 mismatchesN/A

Known Issue

See known-issues.md — during EH first-pass dispatch, m_pFrame can be FRAME_TOP when the cDAC's AMD64Unwinder cannot unwind native frames (it only handles managed code ranges). The legacy DAC succeeds via OS-level VirtualUnwindToFirstManagedCallFrame.

Max Charlamband others added 6 commits March 25, 2026 15:27
Add GCRefMap-based and MetaSig-based scanning for stub frames in the cDAC
stack walker. This implements Frame::GcScanRoots dispatch for:
- StubDispatchFrame: GCRefMap path (when cached) + MetaSig fallback
- ExternalMethodFrame: GCRefMap path
- PrestubMethodFrame / CallCountingHelperFrame: MetaSig path
- DynamicHelperFrame: Flag-based register scanning
Key components:
- GCRefMapDecoder: managed port of native gcrefmap.h bitstream decoder
- CorSigParser: ECMA-335 signature parser with GC type classification,
including ELEMENT_TYPE_INTERNAL for dynamic method signatures
- OffsetFromGCRefMapPos: maps GCRefMap positions to TransitionBlock offsets
- Platform-guarded TransitionBlock offset globals in datadescriptor.inc
Bug fixes found during implementation:
- ScanFrameRoots was passing frame address to GetFrameName instead of the
frame's VTable identifier, causing all frames to hit the no-op default
- Added per-frame error isolation so one bad frame doesn't abort the walk
Reduces GC stress failure delta from 3 to 1 for all 55 remaining failures.
The remaining delta is from RangeList-based code heap resolution (separate issue).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix GetExceptionClauses to use code start for offset calculation.
Wire up ParentOfFuncletStackFrame and unwind-target-PC override
for catch handler GC reporting. Fix AMD64Unwinder null check.
Add GC stress verification infrastructure that compares cDAC stack
reference enumeration against the runtime at GC stress points:
- DAC-like callback for runtime stack ref collection
- xUnit test framework with 7 debuggees (BasicAlloc, DeepStack,
Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive)
- Step throttling, allocation-point hooks, and reentrancy guard
- On-demand build subset and project exclusion from main test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove code referencing runtime features that were removed in PR dotnet#119863
(Move coreclr EH second pass to native code):
- ForceGcReportingStage enum and related TODO comments
- ShouldSaveFuncletInfo, ShouldParentToFuncletReportSavedFuncletSlots,
IsFilterFunclet, IsFilterFuncletCached fields from GCFrameData
- funcletNotSeen, foundFirstFunclet variables
- Unreachable ExInfo block gated by '&& false'
- Dead PeekByte() and ClassifyElementType() from CorSigParser
- Inner try/catch around ScanFrameRoots (outer catch suffices)
- Exclude GCStressTests from main cDAC test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce a separate DOTNET_CdacStress config with bit flags for
controlling cDAC stack reference verification independently of GCStress:
0x1 ALLOC - verify at allocation points (fast, no JIT overhead)
0x2 GC - verify at GC trigger points (future)
0x4 UNIQUE - deduplicate by (IP, SP) hash
0x8 INSTR - verify at instruction traps (needs GCStress=0x4)
Follow the GCStress<T> template pattern with CdacStress<T>::MaybeVerify
that compiles to nothing when HAVE_GCCOVER is not defined, eliminating
#ifdef guards at call sites.
Rename CdacGcStress -> CdacStress (class, files, config vars) to reflect
that this verifies the cDAC's stack walk, not GC behavior.
Legacy DOTNET_GCStress=0x20 continues to work (maps to CDACSTRESS_ALLOC).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match the native DAC behavior for both ClrDataStackWalk::Init and
DacStackReferenceWalker::WalkStack: check the thread's
DebuggerFilterContext and ProfilerFilterContext before falling back
to TryGetThreadContext. During debugger breaks or profiler stack
walks, these contexts hold the correct managed frame state.
Add DebuggerFilterContext and ProfilerFilterContext fields to the
Thread data descriptor and Data.Thread class.
Add diagnostic logging for unique Source IPs in cDAC stress failures
to show which frames the cDAC actually walked.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix SkipDuplicateActiveICF regression from base branch commit 650ffb5:
restore one-shot SkipCurrentFrameInCheck behavior so InlinedCallFrames
are not permanently lost from the FrameIterator.
Fix SW_SKIPPED_FRAME context restoration: call UpdateContextFromFrame
for skipped Frames so SoftwareExceptionFrame context is restored.
Add IsAtFirstPassExceptionThrowSite to suppress throw-site refs during
exception first-pass dispatch, matching legacy DAC behavior.
Restructure CdacStress flags into trigger points (ALLOC/GC/INSTR),
validation types (REFS/WALK/USE_DAC), and modifiers (UNIQUE).
Add three-way comparison infrastructure:
- Load legacy DAC (mscordaccore.dll) in-process via InProcessDataTarget
- CompareStackWalks: frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr
- CompareRefSets: two-phase ref matching (stack + register refs)
- CollectStackRefs: merged cDAC/DAC collection into single function
- FilterAndDedup: combined interior pointer filter + dedup
Refactor VerifyAtStressPoint into clean 5-step flow:
1. Collect raw refs (cDAC always, DAC if USE_DAC, RT always)
2. Compare cDAC vs DAC raw (before filtering)
3. Filter cDAC refs and compare vs RT
4. Pass/fail based on RT match; DAC mismatch logged separately
5. Log all three ref sets on failure
Update known-issues.md with current findings: single remaining issue
is m_pFrame=FRAME_TOP during EH first-pass dispatch where the cDAC
cannot unwind through native frames.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 24, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

1 participant

@max-charlamb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[cDAC] Add GC stress verification infrastructure and stack walk fixes - #6

Closed
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4
Closed

[cDAC] Add GC stress verification infrastructure and stack walk fixes#6
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4

Conversation

@max-charlamb

Copy link
Copy Markdown
Owner

Summary

Add comprehensive cDAC stress verification infrastructure (DOTNET_CdacStress) that compares the cDAC's stack reference enumeration and stack walk against the legacy DAC and runtime.

Changes

Stack Walk Fixes

  • PromoteCallerStack: GCRefMap + MetaSig + DynamicHelperFrame scanning for stub frame GC roots
  • GetExceptionClauses: Fix code start offset calculation and AMD64Unwinder null check
  • ParentOfFuncletStackFrame: Wire up funclet parent frame flag for GC reporting
  • SkipCurrentFrameInCheck: Fix regression from 650ffb5 that permanently lost InlinedCallFrames from the iterator
  • SW_SKIPPED_FRAME context restoration: Call UpdateContextFromFrame for skipped Frames
  • FilterContext: Read DebuggerFilterContext/ProfilerFilterContext matching native DAC behavior
  • IsAtFirstPassExceptionThrowSite: Suppress throw-site refs during EH first-pass

Dead Code Removal

CdacStress Infrastructure

  • DOTNET_CdacStress config with CdacStress<T>::MaybeVerify template pattern (compiles to no-op without HAVE_GCCOVER)
  • Bit flags: trigger points (ALLOC=0x1, INSTR=0x4), validation types (REFS=0x10, WALK=0x20, USE_DAC=0x40), modifiers (UNIQUE=0x100)
  • Three-way comparison: Load legacy DAC in-process via InProcessDataTarget, compare cDAC vs DAC vs RT
  • CompareStackWalks: Frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr comparison
  • 7 debuggee test apps: BasicAlloc, DeepStack, Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive

Test Results

ModeNon-EH debuggeesExceptionHandling
INSTR (0x14 + GCStress=0x4)0 failures0-2 failures
ALLOC+REFS+UNIQUE (0x111)0 failures0 failures
ALLOC+REFS (0x11)0 failures2-4 failures (known issue)
Walk comparison (0x21)0 mismatchesN/A

Known Issue

See known-issues.md — during EH first-pass dispatch, m_pFrame can be FRAME_TOP when the cDAC's AMD64Unwinder cannot unwind native frames (it only handles managed code ranges). The legacy DAC succeeds via OS-level VirtualUnwindToFirstManagedCallFrame.

Max Charlamband others added 6 commits March 25, 2026 15:27
Add GCRefMap-based and MetaSig-based scanning for stub frames in the cDAC
stack walker. This implements Frame::GcScanRoots dispatch for:
- StubDispatchFrame: GCRefMap path (when cached) + MetaSig fallback
- ExternalMethodFrame: GCRefMap path
- PrestubMethodFrame / CallCountingHelperFrame: MetaSig path
- DynamicHelperFrame: Flag-based register scanning
Key components:
- GCRefMapDecoder: managed port of native gcrefmap.h bitstream decoder
- CorSigParser: ECMA-335 signature parser with GC type classification,
including ELEMENT_TYPE_INTERNAL for dynamic method signatures
- OffsetFromGCRefMapPos: maps GCRefMap positions to TransitionBlock offsets
- Platform-guarded TransitionBlock offset globals in datadescriptor.inc
Bug fixes found during implementation:
- ScanFrameRoots was passing frame address to GetFrameName instead of the
frame's VTable identifier, causing all frames to hit the no-op default
- Added per-frame error isolation so one bad frame doesn't abort the walk
Reduces GC stress failure delta from 3 to 1 for all 55 remaining failures.
The remaining delta is from RangeList-based code heap resolution (separate issue).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix GetExceptionClauses to use code start for offset calculation.
Wire up ParentOfFuncletStackFrame and unwind-target-PC override
for catch handler GC reporting. Fix AMD64Unwinder null check.
Add GC stress verification infrastructure that compares cDAC stack
reference enumeration against the runtime at GC stress points:
- DAC-like callback for runtime stack ref collection
- xUnit test framework with 7 debuggees (BasicAlloc, DeepStack,
Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive)
- Step throttling, allocation-point hooks, and reentrancy guard
- On-demand build subset and project exclusion from main test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove code referencing runtime features that were removed in PR dotnet#119863
(Move coreclr EH second pass to native code):
- ForceGcReportingStage enum and related TODO comments
- ShouldSaveFuncletInfo, ShouldParentToFuncletReportSavedFuncletSlots,
IsFilterFunclet, IsFilterFuncletCached fields from GCFrameData
- funcletNotSeen, foundFirstFunclet variables
- Unreachable ExInfo block gated by '&& false'
- Dead PeekByte() and ClassifyElementType() from CorSigParser
- Inner try/catch around ScanFrameRoots (outer catch suffices)
- Exclude GCStressTests from main cDAC test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce a separate DOTNET_CdacStress config with bit flags for
controlling cDAC stack reference verification independently of GCStress:
0x1 ALLOC - verify at allocation points (fast, no JIT overhead)
0x2 GC - verify at GC trigger points (future)
0x4 UNIQUE - deduplicate by (IP, SP) hash
0x8 INSTR - verify at instruction traps (needs GCStress=0x4)
Follow the GCStress<T> template pattern with CdacStress<T>::MaybeVerify
that compiles to nothing when HAVE_GCCOVER is not defined, eliminating
#ifdef guards at call sites.
Rename CdacGcStress -> CdacStress (class, files, config vars) to reflect
that this verifies the cDAC's stack walk, not GC behavior.
Legacy DOTNET_GCStress=0x20 continues to work (maps to CDACSTRESS_ALLOC).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match the native DAC behavior for both ClrDataStackWalk::Init and
DacStackReferenceWalker::WalkStack: check the thread's
DebuggerFilterContext and ProfilerFilterContext before falling back
to TryGetThreadContext. During debugger breaks or profiler stack
walks, these contexts hold the correct managed frame state.
Add DebuggerFilterContext and ProfilerFilterContext fields to the
Thread data descriptor and Data.Thread class.
Add diagnostic logging for unique Source IPs in cDAC stress failures
to show which frames the cDAC actually walked.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix SkipDuplicateActiveICF regression from base branch commit 650ffb5:
restore one-shot SkipCurrentFrameInCheck behavior so InlinedCallFrames
are not permanently lost from the FrameIterator.
Fix SW_SKIPPED_FRAME context restoration: call UpdateContextFromFrame
for skipped Frames so SoftwareExceptionFrame context is restored.
Add IsAtFirstPassExceptionThrowSite to suppress throw-site refs during
exception first-pass dispatch, matching legacy DAC behavior.
Restructure CdacStress flags into trigger points (ALLOC/GC/INSTR),
validation types (REFS/WALK/USE_DAC), and modifiers (UNIQUE).
Add three-way comparison infrastructure:
- Load legacy DAC (mscordaccore.dll) in-process via InProcessDataTarget
- CompareStackWalks: frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr
- CompareRefSets: two-phase ref matching (stack + register refs)
- CollectStackRefs: merged cDAC/DAC collection into single function
- FilterAndDedup: combined interior pointer filter + dedup
Refactor VerifyAtStressPoint into clean 5-step flow:
1. Collect raw refs (cDAC always, DAC if USE_DAC, RT always)
2. Compare cDAC vs DAC raw (before filtering)
3. Filter cDAC refs and compare vs RT
4. Pass/fail based on RT match; DAC mismatch logged separately
5. Log all three ref sets on failure
Update known-issues.md with current findings: single remaining issue
is m_pFrame=FRAME_TOP during EH first-pass dispatch where the cDAC
cannot unwind through native frames.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 24, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

1 participant

@max-charlamb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[cDAC] Add GC stress verification infrastructure and stack walk fixes - #6

Closed
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4
Closed

[cDAC] Add GC stress verification infrastructure and stack walk fixes#6
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4

Conversation

@max-charlamb

Copy link
Copy Markdown
Owner

Summary

Add comprehensive cDAC stress verification infrastructure (DOTNET_CdacStress) that compares the cDAC's stack reference enumeration and stack walk against the legacy DAC and runtime.

Changes

Stack Walk Fixes

  • PromoteCallerStack: GCRefMap + MetaSig + DynamicHelperFrame scanning for stub frame GC roots
  • GetExceptionClauses: Fix code start offset calculation and AMD64Unwinder null check
  • ParentOfFuncletStackFrame: Wire up funclet parent frame flag for GC reporting
  • SkipCurrentFrameInCheck: Fix regression from 650ffb5 that permanently lost InlinedCallFrames from the iterator
  • SW_SKIPPED_FRAME context restoration: Call UpdateContextFromFrame for skipped Frames
  • FilterContext: Read DebuggerFilterContext/ProfilerFilterContext matching native DAC behavior
  • IsAtFirstPassExceptionThrowSite: Suppress throw-site refs during EH first-pass

Dead Code Removal

CdacStress Infrastructure

  • DOTNET_CdacStress config with CdacStress<T>::MaybeVerify template pattern (compiles to no-op without HAVE_GCCOVER)
  • Bit flags: trigger points (ALLOC=0x1, INSTR=0x4), validation types (REFS=0x10, WALK=0x20, USE_DAC=0x40), modifiers (UNIQUE=0x100)
  • Three-way comparison: Load legacy DAC in-process via InProcessDataTarget, compare cDAC vs DAC vs RT
  • CompareStackWalks: Frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr comparison
  • 7 debuggee test apps: BasicAlloc, DeepStack, Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive

Test Results

ModeNon-EH debuggeesExceptionHandling
INSTR (0x14 + GCStress=0x4)0 failures0-2 failures
ALLOC+REFS+UNIQUE (0x111)0 failures0 failures
ALLOC+REFS (0x11)0 failures2-4 failures (known issue)
Walk comparison (0x21)0 mismatchesN/A

Known Issue

See known-issues.md — during EH first-pass dispatch, m_pFrame can be FRAME_TOP when the cDAC's AMD64Unwinder cannot unwind native frames (it only handles managed code ranges). The legacy DAC succeeds via OS-level VirtualUnwindToFirstManagedCallFrame.

Max Charlamband others added 6 commits March 25, 2026 15:27
Add GCRefMap-based and MetaSig-based scanning for stub frames in the cDAC
stack walker. This implements Frame::GcScanRoots dispatch for:
- StubDispatchFrame: GCRefMap path (when cached) + MetaSig fallback
- ExternalMethodFrame: GCRefMap path
- PrestubMethodFrame / CallCountingHelperFrame: MetaSig path
- DynamicHelperFrame: Flag-based register scanning
Key components:
- GCRefMapDecoder: managed port of native gcrefmap.h bitstream decoder
- CorSigParser: ECMA-335 signature parser with GC type classification,
including ELEMENT_TYPE_INTERNAL for dynamic method signatures
- OffsetFromGCRefMapPos: maps GCRefMap positions to TransitionBlock offsets
- Platform-guarded TransitionBlock offset globals in datadescriptor.inc
Bug fixes found during implementation:
- ScanFrameRoots was passing frame address to GetFrameName instead of the
frame's VTable identifier, causing all frames to hit the no-op default
- Added per-frame error isolation so one bad frame doesn't abort the walk
Reduces GC stress failure delta from 3 to 1 for all 55 remaining failures.
The remaining delta is from RangeList-based code heap resolution (separate issue).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix GetExceptionClauses to use code start for offset calculation.
Wire up ParentOfFuncletStackFrame and unwind-target-PC override
for catch handler GC reporting. Fix AMD64Unwinder null check.
Add GC stress verification infrastructure that compares cDAC stack
reference enumeration against the runtime at GC stress points:
- DAC-like callback for runtime stack ref collection
- xUnit test framework with 7 debuggees (BasicAlloc, DeepStack,
Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive)
- Step throttling, allocation-point hooks, and reentrancy guard
- On-demand build subset and project exclusion from main test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove code referencing runtime features that were removed in PR dotnet#119863
(Move coreclr EH second pass to native code):
- ForceGcReportingStage enum and related TODO comments
- ShouldSaveFuncletInfo, ShouldParentToFuncletReportSavedFuncletSlots,
IsFilterFunclet, IsFilterFuncletCached fields from GCFrameData
- funcletNotSeen, foundFirstFunclet variables
- Unreachable ExInfo block gated by '&& false'
- Dead PeekByte() and ClassifyElementType() from CorSigParser
- Inner try/catch around ScanFrameRoots (outer catch suffices)
- Exclude GCStressTests from main cDAC test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce a separate DOTNET_CdacStress config with bit flags for
controlling cDAC stack reference verification independently of GCStress:
0x1 ALLOC - verify at allocation points (fast, no JIT overhead)
0x2 GC - verify at GC trigger points (future)
0x4 UNIQUE - deduplicate by (IP, SP) hash
0x8 INSTR - verify at instruction traps (needs GCStress=0x4)
Follow the GCStress<T> template pattern with CdacStress<T>::MaybeVerify
that compiles to nothing when HAVE_GCCOVER is not defined, eliminating
#ifdef guards at call sites.
Rename CdacGcStress -> CdacStress (class, files, config vars) to reflect
that this verifies the cDAC's stack walk, not GC behavior.
Legacy DOTNET_GCStress=0x20 continues to work (maps to CDACSTRESS_ALLOC).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match the native DAC behavior for both ClrDataStackWalk::Init and
DacStackReferenceWalker::WalkStack: check the thread's
DebuggerFilterContext and ProfilerFilterContext before falling back
to TryGetThreadContext. During debugger breaks or profiler stack
walks, these contexts hold the correct managed frame state.
Add DebuggerFilterContext and ProfilerFilterContext fields to the
Thread data descriptor and Data.Thread class.
Add diagnostic logging for unique Source IPs in cDAC stress failures
to show which frames the cDAC actually walked.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix SkipDuplicateActiveICF regression from base branch commit 650ffb5:
restore one-shot SkipCurrentFrameInCheck behavior so InlinedCallFrames
are not permanently lost from the FrameIterator.
Fix SW_SKIPPED_FRAME context restoration: call UpdateContextFromFrame
for skipped Frames so SoftwareExceptionFrame context is restored.
Add IsAtFirstPassExceptionThrowSite to suppress throw-site refs during
exception first-pass dispatch, matching legacy DAC behavior.
Restructure CdacStress flags into trigger points (ALLOC/GC/INSTR),
validation types (REFS/WALK/USE_DAC), and modifiers (UNIQUE).
Add three-way comparison infrastructure:
- Load legacy DAC (mscordaccore.dll) in-process via InProcessDataTarget
- CompareStackWalks: frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr
- CompareRefSets: two-phase ref matching (stack + register refs)
- CollectStackRefs: merged cDAC/DAC collection into single function
- FilterAndDedup: combined interior pointer filter + dedup
Refactor VerifyAtStressPoint into clean 5-step flow:
1. Collect raw refs (cDAC always, DAC if USE_DAC, RT always)
2. Compare cDAC vs DAC raw (before filtering)
3. Filter cDAC refs and compare vs RT
4. Pass/fail based on RT match; DAC mismatch logged separately
5. Log all three ref sets on failure
Update known-issues.md with current findings: single remaining issue
is m_pFrame=FRAME_TOP during EH first-pass dispatch where the cDAC
cannot unwind through native frames.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 24, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

1 participant

@max-charlamb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

[cDAC] Add GC stress verification infrastructure and stack walk fixes - #6

Closed
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4
Closed

[cDAC] Add GC stress verification infrastructure and stack walk fixes#6
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4

Conversation

@max-charlamb

Copy link
Copy Markdown
Owner

Summary

Add comprehensive cDAC stress verification infrastructure (DOTNET_CdacStress) that compares the cDAC's stack reference enumeration and stack walk against the legacy DAC and runtime.

Changes

Stack Walk Fixes

  • PromoteCallerStack: GCRefMap + MetaSig + DynamicHelperFrame scanning for stub frame GC roots
  • GetExceptionClauses: Fix code start offset calculation and AMD64Unwinder null check
  • ParentOfFuncletStackFrame: Wire up funclet parent frame flag for GC reporting
  • SkipCurrentFrameInCheck: Fix regression from 650ffb5 that permanently lost InlinedCallFrames from the iterator
  • SW_SKIPPED_FRAME context restoration: Call UpdateContextFromFrame for skipped Frames
  • FilterContext: Read DebuggerFilterContext/ProfilerFilterContext matching native DAC behavior
  • IsAtFirstPassExceptionThrowSite: Suppress throw-site refs during EH first-pass

Dead Code Removal

CdacStress Infrastructure

  • DOTNET_CdacStress config with CdacStress<T>::MaybeVerify template pattern (compiles to no-op without HAVE_GCCOVER)
  • Bit flags: trigger points (ALLOC=0x1, INSTR=0x4), validation types (REFS=0x10, WALK=0x20, USE_DAC=0x40), modifiers (UNIQUE=0x100)
  • Three-way comparison: Load legacy DAC in-process via InProcessDataTarget, compare cDAC vs DAC vs RT
  • CompareStackWalks: Frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr comparison
  • 7 debuggee test apps: BasicAlloc, DeepStack, Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive

Test Results

ModeNon-EH debuggeesExceptionHandling
INSTR (0x14 + GCStress=0x4)0 failures0-2 failures
ALLOC+REFS+UNIQUE (0x111)0 failures0 failures
ALLOC+REFS (0x11)0 failures2-4 failures (known issue)
Walk comparison (0x21)0 mismatchesN/A

Known Issue

See known-issues.md — during EH first-pass dispatch, m_pFrame can be FRAME_TOP when the cDAC's AMD64Unwinder cannot unwind native frames (it only handles managed code ranges). The legacy DAC succeeds via OS-level VirtualUnwindToFirstManagedCallFrame.

Max Charlamband others added 6 commits March 25, 2026 15:27
Add GCRefMap-based and MetaSig-based scanning for stub frames in the cDAC
stack walker. This implements Frame::GcScanRoots dispatch for:
- StubDispatchFrame: GCRefMap path (when cached) + MetaSig fallback
- ExternalMethodFrame: GCRefMap path
- PrestubMethodFrame / CallCountingHelperFrame: MetaSig path
- DynamicHelperFrame: Flag-based register scanning
Key components:
- GCRefMapDecoder: managed port of native gcrefmap.h bitstream decoder
- CorSigParser: ECMA-335 signature parser with GC type classification,
including ELEMENT_TYPE_INTERNAL for dynamic method signatures
- OffsetFromGCRefMapPos: maps GCRefMap positions to TransitionBlock offsets
- Platform-guarded TransitionBlock offset globals in datadescriptor.inc
Bug fixes found during implementation:
- ScanFrameRoots was passing frame address to GetFrameName instead of the
frame's VTable identifier, causing all frames to hit the no-op default
- Added per-frame error isolation so one bad frame doesn't abort the walk
Reduces GC stress failure delta from 3 to 1 for all 55 remaining failures.
The remaining delta is from RangeList-based code heap resolution (separate issue).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix GetExceptionClauses to use code start for offset calculation.
Wire up ParentOfFuncletStackFrame and unwind-target-PC override
for catch handler GC reporting. Fix AMD64Unwinder null check.
Add GC stress verification infrastructure that compares cDAC stack
reference enumeration against the runtime at GC stress points:
- DAC-like callback for runtime stack ref collection
- xUnit test framework with 7 debuggees (BasicAlloc, DeepStack,
Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive)
- Step throttling, allocation-point hooks, and reentrancy guard
- On-demand build subset and project exclusion from main test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove code referencing runtime features that were removed in PR dotnet#119863
(Move coreclr EH second pass to native code):
- ForceGcReportingStage enum and related TODO comments
- ShouldSaveFuncletInfo, ShouldParentToFuncletReportSavedFuncletSlots,
IsFilterFunclet, IsFilterFuncletCached fields from GCFrameData
- funcletNotSeen, foundFirstFunclet variables
- Unreachable ExInfo block gated by '&& false'
- Dead PeekByte() and ClassifyElementType() from CorSigParser
- Inner try/catch around ScanFrameRoots (outer catch suffices)
- Exclude GCStressTests from main cDAC test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce a separate DOTNET_CdacStress config with bit flags for
controlling cDAC stack reference verification independently of GCStress:
0x1 ALLOC - verify at allocation points (fast, no JIT overhead)
0x2 GC - verify at GC trigger points (future)
0x4 UNIQUE - deduplicate by (IP, SP) hash
0x8 INSTR - verify at instruction traps (needs GCStress=0x4)
Follow the GCStress<T> template pattern with CdacStress<T>::MaybeVerify
that compiles to nothing when HAVE_GCCOVER is not defined, eliminating
#ifdef guards at call sites.
Rename CdacGcStress -> CdacStress (class, files, config vars) to reflect
that this verifies the cDAC's stack walk, not GC behavior.
Legacy DOTNET_GCStress=0x20 continues to work (maps to CDACSTRESS_ALLOC).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match the native DAC behavior for both ClrDataStackWalk::Init and
DacStackReferenceWalker::WalkStack: check the thread's
DebuggerFilterContext and ProfilerFilterContext before falling back
to TryGetThreadContext. During debugger breaks or profiler stack
walks, these contexts hold the correct managed frame state.
Add DebuggerFilterContext and ProfilerFilterContext fields to the
Thread data descriptor and Data.Thread class.
Add diagnostic logging for unique Source IPs in cDAC stress failures
to show which frames the cDAC actually walked.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix SkipDuplicateActiveICF regression from base branch commit 650ffb5:
restore one-shot SkipCurrentFrameInCheck behavior so InlinedCallFrames
are not permanently lost from the FrameIterator.
Fix SW_SKIPPED_FRAME context restoration: call UpdateContextFromFrame
for skipped Frames so SoftwareExceptionFrame context is restored.
Add IsAtFirstPassExceptionThrowSite to suppress throw-site refs during
exception first-pass dispatch, matching legacy DAC behavior.
Restructure CdacStress flags into trigger points (ALLOC/GC/INSTR),
validation types (REFS/WALK/USE_DAC), and modifiers (UNIQUE).
Add three-way comparison infrastructure:
- Load legacy DAC (mscordaccore.dll) in-process via InProcessDataTarget
- CompareStackWalks: frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr
- CompareRefSets: two-phase ref matching (stack + register refs)
- CollectStackRefs: merged cDAC/DAC collection into single function
- FilterAndDedup: combined interior pointer filter + dedup
Refactor VerifyAtStressPoint into clean 5-step flow:
1. Collect raw refs (cDAC always, DAC if USE_DAC, RT always)
2. Compare cDAC vs DAC raw (before filtering)
3. Filter cDAC refs and compare vs RT
4. Pass/fail based on RT match; DAC mismatch logged separately
5. Log all three ref sets on failure
Update known-issues.md with current findings: single remaining issue
is m_pFrame=FRAME_TOP during EH first-pass dispatch where the cDAC
cannot unwind through native frames.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 24, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

1 participant

@max-charlamb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[cDAC] Add GC stress verification infrastructure and stack walk fixes - #6

Closed
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4
Closed

[cDAC] Add GC stress verification infrastructure and stack walk fixes#6
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4

Conversation

@max-charlamb

Copy link
Copy Markdown
Owner

Summary

Add comprehensive cDAC stress verification infrastructure (DOTNET_CdacStress) that compares the cDAC's stack reference enumeration and stack walk against the legacy DAC and runtime.

Changes

Stack Walk Fixes

  • PromoteCallerStack: GCRefMap + MetaSig + DynamicHelperFrame scanning for stub frame GC roots
  • GetExceptionClauses: Fix code start offset calculation and AMD64Unwinder null check
  • ParentOfFuncletStackFrame: Wire up funclet parent frame flag for GC reporting
  • SkipCurrentFrameInCheck: Fix regression from 650ffb5 that permanently lost InlinedCallFrames from the iterator
  • SW_SKIPPED_FRAME context restoration: Call UpdateContextFromFrame for skipped Frames
  • FilterContext: Read DebuggerFilterContext/ProfilerFilterContext matching native DAC behavior
  • IsAtFirstPassExceptionThrowSite: Suppress throw-site refs during EH first-pass

Dead Code Removal

CdacStress Infrastructure

  • DOTNET_CdacStress config with CdacStress<T>::MaybeVerify template pattern (compiles to no-op without HAVE_GCCOVER)
  • Bit flags: trigger points (ALLOC=0x1, INSTR=0x4), validation types (REFS=0x10, WALK=0x20, USE_DAC=0x40), modifiers (UNIQUE=0x100)
  • Three-way comparison: Load legacy DAC in-process via InProcessDataTarget, compare cDAC vs DAC vs RT
  • CompareStackWalks: Frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr comparison
  • 7 debuggee test apps: BasicAlloc, DeepStack, Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive

Test Results

ModeNon-EH debuggeesExceptionHandling
INSTR (0x14 + GCStress=0x4)0 failures0-2 failures
ALLOC+REFS+UNIQUE (0x111)0 failures0 failures
ALLOC+REFS (0x11)0 failures2-4 failures (known issue)
Walk comparison (0x21)0 mismatchesN/A

Known Issue

See known-issues.md — during EH first-pass dispatch, m_pFrame can be FRAME_TOP when the cDAC's AMD64Unwinder cannot unwind native frames (it only handles managed code ranges). The legacy DAC succeeds via OS-level VirtualUnwindToFirstManagedCallFrame.

Max Charlamband others added 6 commits March 25, 2026 15:27
Add GCRefMap-based and MetaSig-based scanning for stub frames in the cDAC
stack walker. This implements Frame::GcScanRoots dispatch for:
- StubDispatchFrame: GCRefMap path (when cached) + MetaSig fallback
- ExternalMethodFrame: GCRefMap path
- PrestubMethodFrame / CallCountingHelperFrame: MetaSig path
- DynamicHelperFrame: Flag-based register scanning
Key components:
- GCRefMapDecoder: managed port of native gcrefmap.h bitstream decoder
- CorSigParser: ECMA-335 signature parser with GC type classification,
including ELEMENT_TYPE_INTERNAL for dynamic method signatures
- OffsetFromGCRefMapPos: maps GCRefMap positions to TransitionBlock offsets
- Platform-guarded TransitionBlock offset globals in datadescriptor.inc
Bug fixes found during implementation:
- ScanFrameRoots was passing frame address to GetFrameName instead of the
frame's VTable identifier, causing all frames to hit the no-op default
- Added per-frame error isolation so one bad frame doesn't abort the walk
Reduces GC stress failure delta from 3 to 1 for all 55 remaining failures.
The remaining delta is from RangeList-based code heap resolution (separate issue).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix GetExceptionClauses to use code start for offset calculation.
Wire up ParentOfFuncletStackFrame and unwind-target-PC override
for catch handler GC reporting. Fix AMD64Unwinder null check.
Add GC stress verification infrastructure that compares cDAC stack
reference enumeration against the runtime at GC stress points:
- DAC-like callback for runtime stack ref collection
- xUnit test framework with 7 debuggees (BasicAlloc, DeepStack,
Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive)
- Step throttling, allocation-point hooks, and reentrancy guard
- On-demand build subset and project exclusion from main test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove code referencing runtime features that were removed in PR dotnet#119863
(Move coreclr EH second pass to native code):
- ForceGcReportingStage enum and related TODO comments
- ShouldSaveFuncletInfo, ShouldParentToFuncletReportSavedFuncletSlots,
IsFilterFunclet, IsFilterFuncletCached fields from GCFrameData
- funcletNotSeen, foundFirstFunclet variables
- Unreachable ExInfo block gated by '&& false'
- Dead PeekByte() and ClassifyElementType() from CorSigParser
- Inner try/catch around ScanFrameRoots (outer catch suffices)
- Exclude GCStressTests from main cDAC test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce a separate DOTNET_CdacStress config with bit flags for
controlling cDAC stack reference verification independently of GCStress:
0x1 ALLOC - verify at allocation points (fast, no JIT overhead)
0x2 GC - verify at GC trigger points (future)
0x4 UNIQUE - deduplicate by (IP, SP) hash
0x8 INSTR - verify at instruction traps (needs GCStress=0x4)
Follow the GCStress<T> template pattern with CdacStress<T>::MaybeVerify
that compiles to nothing when HAVE_GCCOVER is not defined, eliminating
#ifdef guards at call sites.
Rename CdacGcStress -> CdacStress (class, files, config vars) to reflect
that this verifies the cDAC's stack walk, not GC behavior.
Legacy DOTNET_GCStress=0x20 continues to work (maps to CDACSTRESS_ALLOC).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match the native DAC behavior for both ClrDataStackWalk::Init and
DacStackReferenceWalker::WalkStack: check the thread's
DebuggerFilterContext and ProfilerFilterContext before falling back
to TryGetThreadContext. During debugger breaks or profiler stack
walks, these contexts hold the correct managed frame state.
Add DebuggerFilterContext and ProfilerFilterContext fields to the
Thread data descriptor and Data.Thread class.
Add diagnostic logging for unique Source IPs in cDAC stress failures
to show which frames the cDAC actually walked.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix SkipDuplicateActiveICF regression from base branch commit 650ffb5:
restore one-shot SkipCurrentFrameInCheck behavior so InlinedCallFrames
are not permanently lost from the FrameIterator.
Fix SW_SKIPPED_FRAME context restoration: call UpdateContextFromFrame
for skipped Frames so SoftwareExceptionFrame context is restored.
Add IsAtFirstPassExceptionThrowSite to suppress throw-site refs during
exception first-pass dispatch, matching legacy DAC behavior.
Restructure CdacStress flags into trigger points (ALLOC/GC/INSTR),
validation types (REFS/WALK/USE_DAC), and modifiers (UNIQUE).
Add three-way comparison infrastructure:
- Load legacy DAC (mscordaccore.dll) in-process via InProcessDataTarget
- CompareStackWalks: frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr
- CompareRefSets: two-phase ref matching (stack + register refs)
- CollectStackRefs: merged cDAC/DAC collection into single function
- FilterAndDedup: combined interior pointer filter + dedup
Refactor VerifyAtStressPoint into clean 5-step flow:
1. Collect raw refs (cDAC always, DAC if USE_DAC, RT always)
2. Compare cDAC vs DAC raw (before filtering)
3. Filter cDAC refs and compare vs RT
4. Pass/fail based on RT match; DAC mismatch logged separately
5. Log all three ref sets on failure
Update known-issues.md with current findings: single remaining issue
is m_pFrame=FRAME_TOP during EH first-pass dispatch where the cDAC
cannot unwind through native frames.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 24, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

1 participant

@max-charlamb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[cDAC] Add GC stress verification infrastructure and stack walk fixes - #6

Closed
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4
Closed

[cDAC] Add GC stress verification infrastructure and stack walk fixes#6
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4

Conversation

@max-charlamb

Copy link
Copy Markdown
Owner

Summary

Add comprehensive cDAC stress verification infrastructure (DOTNET_CdacStress) that compares the cDAC's stack reference enumeration and stack walk against the legacy DAC and runtime.

Changes

Stack Walk Fixes

  • PromoteCallerStack: GCRefMap + MetaSig + DynamicHelperFrame scanning for stub frame GC roots
  • GetExceptionClauses: Fix code start offset calculation and AMD64Unwinder null check
  • ParentOfFuncletStackFrame: Wire up funclet parent frame flag for GC reporting
  • SkipCurrentFrameInCheck: Fix regression from 650ffb5 that permanently lost InlinedCallFrames from the iterator
  • SW_SKIPPED_FRAME context restoration: Call UpdateContextFromFrame for skipped Frames
  • FilterContext: Read DebuggerFilterContext/ProfilerFilterContext matching native DAC behavior
  • IsAtFirstPassExceptionThrowSite: Suppress throw-site refs during EH first-pass

Dead Code Removal

CdacStress Infrastructure

  • DOTNET_CdacStress config with CdacStress<T>::MaybeVerify template pattern (compiles to no-op without HAVE_GCCOVER)
  • Bit flags: trigger points (ALLOC=0x1, INSTR=0x4), validation types (REFS=0x10, WALK=0x20, USE_DAC=0x40), modifiers (UNIQUE=0x100)
  • Three-way comparison: Load legacy DAC in-process via InProcessDataTarget, compare cDAC vs DAC vs RT
  • CompareStackWalks: Frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr comparison
  • 7 debuggee test apps: BasicAlloc, DeepStack, Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive

Test Results

ModeNon-EH debuggeesExceptionHandling
INSTR (0x14 + GCStress=0x4)0 failures0-2 failures
ALLOC+REFS+UNIQUE (0x111)0 failures0 failures
ALLOC+REFS (0x11)0 failures2-4 failures (known issue)
Walk comparison (0x21)0 mismatchesN/A

Known Issue

See known-issues.md — during EH first-pass dispatch, m_pFrame can be FRAME_TOP when the cDAC's AMD64Unwinder cannot unwind native frames (it only handles managed code ranges). The legacy DAC succeeds via OS-level VirtualUnwindToFirstManagedCallFrame.

Max Charlamband others added 6 commits March 25, 2026 15:27
Add GCRefMap-based and MetaSig-based scanning for stub frames in the cDAC
stack walker. This implements Frame::GcScanRoots dispatch for:
- StubDispatchFrame: GCRefMap path (when cached) + MetaSig fallback
- ExternalMethodFrame: GCRefMap path
- PrestubMethodFrame / CallCountingHelperFrame: MetaSig path
- DynamicHelperFrame: Flag-based register scanning
Key components:
- GCRefMapDecoder: managed port of native gcrefmap.h bitstream decoder
- CorSigParser: ECMA-335 signature parser with GC type classification,
including ELEMENT_TYPE_INTERNAL for dynamic method signatures
- OffsetFromGCRefMapPos: maps GCRefMap positions to TransitionBlock offsets
- Platform-guarded TransitionBlock offset globals in datadescriptor.inc
Bug fixes found during implementation:
- ScanFrameRoots was passing frame address to GetFrameName instead of the
frame's VTable identifier, causing all frames to hit the no-op default
- Added per-frame error isolation so one bad frame doesn't abort the walk
Reduces GC stress failure delta from 3 to 1 for all 55 remaining failures.
The remaining delta is from RangeList-based code heap resolution (separate issue).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix GetExceptionClauses to use code start for offset calculation.
Wire up ParentOfFuncletStackFrame and unwind-target-PC override
for catch handler GC reporting. Fix AMD64Unwinder null check.
Add GC stress verification infrastructure that compares cDAC stack
reference enumeration against the runtime at GC stress points:
- DAC-like callback for runtime stack ref collection
- xUnit test framework with 7 debuggees (BasicAlloc, DeepStack,
Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive)
- Step throttling, allocation-point hooks, and reentrancy guard
- On-demand build subset and project exclusion from main test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove code referencing runtime features that were removed in PR dotnet#119863
(Move coreclr EH second pass to native code):
- ForceGcReportingStage enum and related TODO comments
- ShouldSaveFuncletInfo, ShouldParentToFuncletReportSavedFuncletSlots,
IsFilterFunclet, IsFilterFuncletCached fields from GCFrameData
- funcletNotSeen, foundFirstFunclet variables
- Unreachable ExInfo block gated by '&& false'
- Dead PeekByte() and ClassifyElementType() from CorSigParser
- Inner try/catch around ScanFrameRoots (outer catch suffices)
- Exclude GCStressTests from main cDAC test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce a separate DOTNET_CdacStress config with bit flags for
controlling cDAC stack reference verification independently of GCStress:
0x1 ALLOC - verify at allocation points (fast, no JIT overhead)
0x2 GC - verify at GC trigger points (future)
0x4 UNIQUE - deduplicate by (IP, SP) hash
0x8 INSTR - verify at instruction traps (needs GCStress=0x4)
Follow the GCStress<T> template pattern with CdacStress<T>::MaybeVerify
that compiles to nothing when HAVE_GCCOVER is not defined, eliminating
#ifdef guards at call sites.
Rename CdacGcStress -> CdacStress (class, files, config vars) to reflect
that this verifies the cDAC's stack walk, not GC behavior.
Legacy DOTNET_GCStress=0x20 continues to work (maps to CDACSTRESS_ALLOC).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match the native DAC behavior for both ClrDataStackWalk::Init and
DacStackReferenceWalker::WalkStack: check the thread's
DebuggerFilterContext and ProfilerFilterContext before falling back
to TryGetThreadContext. During debugger breaks or profiler stack
walks, these contexts hold the correct managed frame state.
Add DebuggerFilterContext and ProfilerFilterContext fields to the
Thread data descriptor and Data.Thread class.
Add diagnostic logging for unique Source IPs in cDAC stress failures
to show which frames the cDAC actually walked.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix SkipDuplicateActiveICF regression from base branch commit 650ffb5:
restore one-shot SkipCurrentFrameInCheck behavior so InlinedCallFrames
are not permanently lost from the FrameIterator.
Fix SW_SKIPPED_FRAME context restoration: call UpdateContextFromFrame
for skipped Frames so SoftwareExceptionFrame context is restored.
Add IsAtFirstPassExceptionThrowSite to suppress throw-site refs during
exception first-pass dispatch, matching legacy DAC behavior.
Restructure CdacStress flags into trigger points (ALLOC/GC/INSTR),
validation types (REFS/WALK/USE_DAC), and modifiers (UNIQUE).
Add three-way comparison infrastructure:
- Load legacy DAC (mscordaccore.dll) in-process via InProcessDataTarget
- CompareStackWalks: frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr
- CompareRefSets: two-phase ref matching (stack + register refs)
- CollectStackRefs: merged cDAC/DAC collection into single function
- FilterAndDedup: combined interior pointer filter + dedup
Refactor VerifyAtStressPoint into clean 5-step flow:
1. Collect raw refs (cDAC always, DAC if USE_DAC, RT always)
2. Compare cDAC vs DAC raw (before filtering)
3. Filter cDAC refs and compare vs RT
4. Pass/fail based on RT match; DAC mismatch logged separately
5. Log all three ref sets on failure
Update known-issues.md with current findings: single remaining issue
is m_pFrame=FRAME_TOP during EH first-pass dispatch where the cDAC
cannot unwind through native frames.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 24, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

1 participant

@max-charlamb
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

[cDAC] Add GC stress verification infrastructure and stack walk fixes - #6

Closed
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4
Closed

[cDAC] Add GC stress verification infrastructure and stack walk fixes#6
max-charlamb wants to merge 6 commits into
cdac-stackreferences-2-with-stressfrom
cdac-stackreferences-4

Conversation

@max-charlamb

Copy link
Copy Markdown
Owner

Summary

Add comprehensive cDAC stress verification infrastructure (DOTNET_CdacStress) that compares the cDAC's stack reference enumeration and stack walk against the legacy DAC and runtime.

Changes

Stack Walk Fixes

  • PromoteCallerStack: GCRefMap + MetaSig + DynamicHelperFrame scanning for stub frame GC roots
  • GetExceptionClauses: Fix code start offset calculation and AMD64Unwinder null check
  • ParentOfFuncletStackFrame: Wire up funclet parent frame flag for GC reporting
  • SkipCurrentFrameInCheck: Fix regression from 650ffb5 that permanently lost InlinedCallFrames from the iterator
  • SW_SKIPPED_FRAME context restoration: Call UpdateContextFromFrame for skipped Frames
  • FilterContext: Read DebuggerFilterContext/ProfilerFilterContext matching native DAC behavior
  • IsAtFirstPassExceptionThrowSite: Suppress throw-site refs during EH first-pass

Dead Code Removal

CdacStress Infrastructure

  • DOTNET_CdacStress config with CdacStress<T>::MaybeVerify template pattern (compiles to no-op without HAVE_GCCOVER)
  • Bit flags: trigger points (ALLOC=0x1, INSTR=0x4), validation types (REFS=0x10, WALK=0x20, USE_DAC=0x40), modifiers (UNIQUE=0x100)
  • Three-way comparison: Load legacy DAC in-process via InProcessDataTarget, compare cDAC vs DAC vs RT
  • CompareStackWalks: Frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr comparison
  • 7 debuggee test apps: BasicAlloc, DeepStack, Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive

Test Results

ModeNon-EH debuggeesExceptionHandling
INSTR (0x14 + GCStress=0x4)0 failures0-2 failures
ALLOC+REFS+UNIQUE (0x111)0 failures0 failures
ALLOC+REFS (0x11)0 failures2-4 failures (known issue)
Walk comparison (0x21)0 mismatchesN/A

Known Issue

See known-issues.md — during EH first-pass dispatch, m_pFrame can be FRAME_TOP when the cDAC's AMD64Unwinder cannot unwind native frames (it only handles managed code ranges). The legacy DAC succeeds via OS-level VirtualUnwindToFirstManagedCallFrame.

Max Charlamband others added 6 commits March 25, 2026 15:27
Add GCRefMap-based and MetaSig-based scanning for stub frames in the cDAC
stack walker. This implements Frame::GcScanRoots dispatch for:
- StubDispatchFrame: GCRefMap path (when cached) + MetaSig fallback
- ExternalMethodFrame: GCRefMap path
- PrestubMethodFrame / CallCountingHelperFrame: MetaSig path
- DynamicHelperFrame: Flag-based register scanning
Key components:
- GCRefMapDecoder: managed port of native gcrefmap.h bitstream decoder
- CorSigParser: ECMA-335 signature parser with GC type classification,
including ELEMENT_TYPE_INTERNAL for dynamic method signatures
- OffsetFromGCRefMapPos: maps GCRefMap positions to TransitionBlock offsets
- Platform-guarded TransitionBlock offset globals in datadescriptor.inc
Bug fixes found during implementation:
- ScanFrameRoots was passing frame address to GetFrameName instead of the
frame's VTable identifier, causing all frames to hit the no-op default
- Added per-frame error isolation so one bad frame doesn't abort the walk
Reduces GC stress failure delta from 3 to 1 for all 55 remaining failures.
The remaining delta is from RangeList-based code heap resolution (separate issue).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix GetExceptionClauses to use code start for offset calculation.
Wire up ParentOfFuncletStackFrame and unwind-target-PC override
for catch handler GC reporting. Fix AMD64Unwinder null check.
Add GC stress verification infrastructure that compares cDAC stack
reference enumeration against the runtime at GC stress points:
- DAC-like callback for runtime stack ref collection
- xUnit test framework with 7 debuggees (BasicAlloc, DeepStack,
Generics, ExceptionHandling, PInvoke, MultiThread, Comprehensive)
- Step throttling, allocation-point hooks, and reentrancy guard
- On-demand build subset and project exclusion from main test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove code referencing runtime features that were removed in PR dotnet#119863
(Move coreclr EH second pass to native code):
- ForceGcReportingStage enum and related TODO comments
- ShouldSaveFuncletInfo, ShouldParentToFuncletReportSavedFuncletSlots,
IsFilterFunclet, IsFilterFuncletCached fields from GCFrameData
- funcletNotSeen, foundFirstFunclet variables
- Unreachable ExInfo block gated by '&& false'
- Dead PeekByte() and ClassifyElementType() from CorSigParser
- Inner try/catch around ScanFrameRoots (outer catch suffices)
- Exclude GCStressTests from main cDAC test project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce a separate DOTNET_CdacStress config with bit flags for
controlling cDAC stack reference verification independently of GCStress:
0x1 ALLOC - verify at allocation points (fast, no JIT overhead)
0x2 GC - verify at GC trigger points (future)
0x4 UNIQUE - deduplicate by (IP, SP) hash
0x8 INSTR - verify at instruction traps (needs GCStress=0x4)
Follow the GCStress<T> template pattern with CdacStress<T>::MaybeVerify
that compiles to nothing when HAVE_GCCOVER is not defined, eliminating
#ifdef guards at call sites.
Rename CdacGcStress -> CdacStress (class, files, config vars) to reflect
that this verifies the cDAC's stack walk, not GC behavior.
Legacy DOTNET_GCStress=0x20 continues to work (maps to CDACSTRESS_ALLOC).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match the native DAC behavior for both ClrDataStackWalk::Init and
DacStackReferenceWalker::WalkStack: check the thread's
DebuggerFilterContext and ProfilerFilterContext before falling back
to TryGetThreadContext. During debugger breaks or profiler stack
walks, these contexts hold the correct managed frame state.
Add DebuggerFilterContext and ProfilerFilterContext fields to the
Thread data descriptor and Data.Thread class.
Add diagnostic logging for unique Source IPs in cDAC stress failures
to show which frames the cDAC actually walked.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix SkipDuplicateActiveICF regression from base branch commit 650ffb5:
restore one-shot SkipCurrentFrameInCheck behavior so InlinedCallFrames
are not permanently lost from the FrameIterator.
Fix SW_SKIPPED_FRAME context restoration: call UpdateContextFromFrame
for skipped Frames so SoftwareExceptionFrame context is restored.
Add IsAtFirstPassExceptionThrowSite to suppress throw-site refs during
exception first-pass dispatch, matching legacy DAC behavior.
Restructure CdacStress flags into trigger points (ALLOC/GC/INSTR),
validation types (REFS/WALK/USE_DAC), and modifiers (UNIQUE).
Add three-way comparison infrastructure:
- Load legacy DAC (mscordaccore.dll) in-process via InProcessDataTarget
- CompareStackWalks: frame-by-frame IXCLRDataStackWalk IP+SP+FrameAddr
- CompareRefSets: two-phase ref matching (stack + register refs)
- CollectStackRefs: merged cDAC/DAC collection into single function
- FilterAndDedup: combined interior pointer filter + dedup
Refactor VerifyAtStressPoint into clean 5-step flow:
1. Collect raw refs (cDAC always, DAC if USE_DAC, RT always)
2. Compare cDAC vs DAC raw (before filtering)
3. Filter cDAC refs and compare vs RT
4. Pass/fail based on RT match; DAC mismatch logged separately
5. Log all three ref sets on failure
Update known-issues.md with current findings: single remaining issue
is m_pFrame=FRAME_TOP during EH first-pass dispatch where the cDAC
cannot unwind through native frames.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 22, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 23, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit that referenced this pull request Apr 24, 2026
- Fix README test filter syntax: use FullyQualifiedName~BasicAlloc (#1)
- Remove goto statements from GCInfoDecoder.EnumerateLiveSlots: extract
ReportUntrackedAndSucceed local function (#2)
- Move CheckForSkippedFrames from Next() to UpdateState (#6)
- Add XUnitConsoleRunner package reference for Helix payload (#9)
- Support TypeSpec (tag=2) in DecodeTypeDefOrRefOrSpec matching native
CorSigUncompressToken behavior (#10)
- Fix IsAppleArm64ABI: set to false until Apple platform detection is
available (filed dotnet#127282) (#11)
- Fix Unix x64 float register stride: use FloatRegisterSize instead of
hardcoded 8 (#12)
- Replace FrameIterator.OffsetFromGCRefMapPos with CallingConventionInfo
version that handles x86 reversed register layout (dotnet#13)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

1 participant

@max-charlamb