[cDAC] Implement GetArgumentByIndex and GetLocalVariableByIndex on ClrDataFrame#125463
Draft
max-charlamb wants to merge 9 commits intodotnet:mainfrom
Draft
[cDAC] Implement GetArgumentByIndex and GetLocalVariableByIndex on ClrDataFrame#125463max-charlamb wants to merge 9 commits intodotnet:mainfrom
max-charlamb wants to merge 9 commits intodotnet:mainfrom
Conversation
Contributor
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
Contributor
There was a problem hiding this comment.
Pull request overview
Implements cDAC-backed IXCLRDataFrame.GetArgumentByIndex / GetLocalVariableByIndex and introduces supporting DebugInfo/StackWalk contract APIs plus a new managed IXCLRDataValue implementation, with dump-based integration tests to validate behavior against the legacy DAC.
Changes:
- Extend DebugInfo contract v2 to decode/resolve native variable locations (vars section) and StackWalk contract to expose frame instruction pointers.
- Implement
ClrDataFrame.GetArgumentByIndex/GetLocalVariableByIndexusing cDAC debug info, and add a new managedClrDataValue(IXCLRDataValue) for returned argument/local values. - Add new dump debuggee (
LocalVariables) and dump-based integration tests forIXCLRDataFrame,IXCLRDataValue, andIXCLRDataAppDomain.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| src/native/managed/cdac/tests/DumpTests/IXCLRDataValueDumpTests.cs | New dump tests validating IXCLRDataValue returned from arg/local APIs |
| src/native/managed/cdac/tests/DumpTests/IXCLRDataFrameDumpTests.cs | New dump tests covering IXCLRDataFrame APIs including arg/local retrieval |
| src/native/managed/cdac/tests/DumpTests/IXCLRDataAppDomainDumpTests.cs | New dump tests for IXCLRDataAppDomain behavior via frame GetAppDomain |
| src/native/managed/cdac/tests/DumpTests/DumpTestBase.cs | Adds overload to initialize dump tests with per-test debuggee + dump type |
| src/native/managed/cdac/tests/DumpTests/Debuggees/StackWalk/Program.cs | Adjusts debuggee to ensure locals/args exist for stack inspection |
| src/native/managed/cdac/tests/DumpTests/Debuggees/LocalVariables/Program.cs | New debuggee producing known args/locals for value inspection |
| src/native/managed/cdac/tests/DumpTests/Debuggees/LocalVariables/LocalVariables.csproj | New debuggee project enabling full dumps |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/IXCLRData.cs | Updates COM signatures to use DacComNullableByRef for AppDomain methods |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataValue.cs | New managed IXCLRDataValue implementation with legacy cross-validation |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTask.cs | Updates ClrDataAppDomain construction to include Target |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs | Implements cDAC versions of GetAppDomain, arg/local-by-index, and helper parsing |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataAppDomain.cs | Implements cDAC AppDomain methods (GetName, GetUniqueID, GetFlags) |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs | Adds GetInstructionPointer for frame IP retrieval |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs | Returns "DefaultDomain" when FriendlyName is null |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/NativeVarInfo.cs | Adds internal var-loc types/struct mirroring runtime encoding |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/DebugInfo_2.cs | Implements GetMethodVarInfo + GetVariableLocations for v2 |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/DebugInfoHelpers.cs | Adds vars nibble-decoder and var-loc resolution across architectures |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IStackWalk.cs | Extends abstraction with GetInstructionPointer |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IDebugInfo.cs | Adds public stable DebugVarInfo/NativeVarLocation APIs |
| docs/design/datacontracts/Loader.md | Documents "DefaultDomain" fallback behavior |
| docs/design/datacontracts/DebugInfo.md | Documents DebugInfo v2 vars decoding + new APIs |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataValue.cs
Outdated
Show resolved
Hide resolved
...c/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/DebugInfoHelpers.cs
Outdated
Show resolved
Hide resolved
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataValue.cs
Outdated
Show resolved
Hide resolved
Implement GetContext using StackWalk.GetRawContext to retrieve context bytes from the frame handle, matching the native stack.cpp behavior. Implement GetAppDomain by reading the global AppDomain pointer and wrapping it in a new ClrDataAppDomain class that implements IXCLRDataAppDomain with GetName, GetUniqueID, and GetFlags. Add overloaded InitializeDumpTest to DumpTestBase so individual test methods can select their own debuggee and dump type. Add ClrDataFrameDumpTests with tests for GetContext (returns non-empty context, sets context size, buffer-too-small error) and GetAppDomain (returns valid AppDomain, has expected unique ID of 1). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add GetVariableLocations and GetMethodVarInfo to IDebugInfo for resolving native variable locations from JIT debug info. Public API (Abstractions - stable): - DebugVarLocKind enum and DebugVarInfo struct for consumer-facing var info - NativeVarLocation struct for resolved physical locations - GetMethodVarInfo returns stable DebugVarInfo entries - GetVariableLocations resolves a variable to physical addresses Internal (Contracts - versioned): - VarLocType enum and NativeVarInfo struct matching runtime internals - DoVars nibble decoder in DebugInfoHelpers - VarLoc-to-physical-address resolution with register mapping - Implemented in DebugInfo_2; DebugInfo_1 left as NotImplemented Also adds GetInstructionPointer to IStackWalk and documents the Vars encoding format and VarLocType constants in DebugInfo.md (Version 2). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rDataFrame Implement the managed cDAC versions of ClrDataFrame::GetArgumentByIndex and ClrDataFrame::GetLocalVariableByIndex, replacing legacy-only delegation. ClrDataFrame changes: - GetArgumentByIndex: parameter name resolution from metadata, 'this' handling for instance methods, value creation via DebugInfo contract - GetLocalVariableByIndex: local count validation, varInfoSlot=index+numArgs computation, empty name (matching native Whidbey limitation) - Signature type resolution: walks ECMA-335 signatures to determine element types, adjusts primitive sizes to avoid reading excess bytes - Extracted GetMethodInfo, GetMethodSignatureInfo, GetLocalVariableCount helpers to reduce duplication across methods - Legacy verification (legacyImpl pattern) on both methods - TODO for ContextSizeForFlags in GetContext New ClrDataValue class: - [GeneratedComClass] implementing IXCLRDataValue - GetFlags, GetAddress, GetSize, GetBytes with register/memory handling - GetNumLocations, GetLocationByIndex for variable location inspection - Legacy verification on GetFlags, GetAddress, GetSize, GetBytes, GetNumLocations, GetLocationByIndex - Register-based values written directly as bytes (not read from target) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…, and IXCLRDataValue New LocalVariables debuggee with known values: - StaticMethod(int intArg, string stringArg) with int and string locals - GenericMethod<T>(T value) with generic local for generic coverage - Full dump type, NoInlining + try/finally to preserve variable locations IXCLRDataFrame tests (6 new): - GetArgumentByIndex: value retrieval, parameter name, invalid index - GetLocalVariableByIndex: value retrieval, empty name, invalid index IXCLRDataValue tests (10 new): - GetSize, GetFlags, GetBytes for arguments and locals - GetNumLocations, GetLocationByIndex validation - Parameter name verification (intArg, stringArg) - Cross-validation: all args and locals retrievable by index Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
50b6258 to
487d34e
Compare
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataValue.cs
Show resolved
Hide resolved
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs
Outdated
Show resolved
Hide resolved
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs
Outdated
Show resolved
Hide resolved
...cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/NativeVarInfo.cs
Outdated
Show resolved
Hide resolved
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataValue.cs
Outdated
Show resolved
Hide resolved
5ec7202 to
47b23bf
Compare
47b23bf to
790f597
Compare
src/native/managed/cdac/tests/DumpTests/IXCLRDataFrameDumpTests.cs
Outdated
Show resolved
Hide resolved
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataValue.cs
Outdated
Show resolved
Hide resolved
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataValue.cs
Show resolved
Hide resolved
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs
Show resolved
Hide resolved
...cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/NativeVarInfo.cs
Outdated
Show resolved
Hide resolved
790f597 to
9b0a451
Compare
9b0a451 to
dafa127
Compare
dafa127 to
f00fc33
Compare
src/native/managed/cdac/tests/DumpTests/IXCLRDataFrameDumpTests.cs
Outdated
Show resolved
Hide resolved
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataValue.cs
Outdated
Show resolved
Hide resolved
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataValue.cs
Outdated
Show resolved
Hide resolved
max-charlamb
commented
Mar 16, 2026
...e/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IStackWalk.cs
Show resolved
Hide resolved
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataValue.cs
Outdated
Show resolved
Hide resolved
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataValue.cs
Outdated
Show resolved
Hide resolved
f00fc33 to
0d65124
Compare
0d65124 to
8a5ac15
Compare
8a5ac15 to
14e2764
Compare
The JIT emits REGNUM_AMBIENT_SP as the base register for stack-based locals in non-frame-pointer methods (scopeinfo.cpp:334). On AMD64 this register was not handled in GetRegOffsInCONTEXT, falling through to the default assert and returning (SIZE_T)(-1), which caused NativeVarLocations to read garbage from the CONTEXT structure. Map AMBIENT_SP to RSP matching the existing x86/ARM/ARM64 behavior. This is a best-effort approximation — ambient SP is the entry-time SP before prolog adjustments, not necessarily the current RSP. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
14e2764 to
6da2d24
Compare
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataValue.cs
Show resolved
Hide resolved
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataValue.cs
Show resolved
Hide resolved
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs
Show resolved
Hide resolved
...c/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/DebugInfoHelpers.cs
Show resolved
Hide resolved
...c/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/DebugInfoHelpers.cs
Show resolved
Hide resolved
src/native/managed/cdac/tests/DumpTests/IXCLRDataFrameDumpTests.cs
Outdated
Show resolved
Hide resolved
1. ClrDataValue.GetLocationByIndex: return addr=0 for register-based locations, matching native DAC behavior (inspect.cpp:1295). 2. DebugInfo_2.GetMethodNativeVarInfo: compute code offset from the method's native code entry point (GetNativeCode) instead of the code block start (GetStartAddress), since NativeVarInfo offsets are relative to the method start. This fixes funclet frames where GetStartAddress returns the funclet start. 3. ClrDataFrame.CreateValueFromDebugInfo: remove catch block. Legitimate 'no data' cases return empty arrays without throwing. Exceptions like NotSupportedException and VirtualReadException propagate to the caller's outer catch block, matching the native DAC's EX_CATCH behavior. 4. DebugInfoHelpers.GetRegisterName: handle REGNUM_AMBIENT_SP on all platforms, mapping to the stack pointer register. Matches the native DAC fix in util.cpp. 5. ClrDataValue: downgrade GetLocationByIndex address comparison from assert to Debug.WriteLine log, since the native DAC produces garbage addresses for AMBIENT_SP variables on AMD64. 6. Add AllowCdacSuccess validation mode. The native DAC's MetaSig constructor can fail on certain frames (e.g., EH dispatch) where the cDAC succeeds via contract-based metadata access. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
6da2d24 to
2b8e801
Compare
1. ClrDataValue.GetLocationByIndex: return addr=0 for register-based locations, matching native DAC behavior (inspect.cpp:1295). 2. DebugInfo_2.GetMethodNativeVarInfo: compute code offset from the method's native code entry point (GetNativeCode) instead of the code block start (GetStartAddress), since NativeVarInfo offsets are relative to the method start. This fixes funclet frames where GetStartAddress returns the funclet start. 3. REGNUM_AMBIENT_SP (register 17 on x64) mapped to RSP, matching the native DAC fix and existing x86/ARM/ARM64 behavior. Added TODO comment on all platforms noting this is a best-effort approximation. 4. Added AllowCdacSuccess validation mode for GetArgumentByIndex and GetLocalVariableByIndex. The native DAC's MetaSig constructor can fail on certain frames (e.g., EH dispatch) where the cDAC succeeds via contract-based metadata access. 5. VLT_REG_BYREF: removed extra dereference in ResolveRegByRef. ReadRegister already returns the register value (pointer to variable); the previous target.ReadPointer call was an incorrect additional dereference not present in native NativeVarLocations. 6. Added DereferenceOrZero helper matching native DereferenceByRefVar (util.cpp:414). Returns 0 on read failure instead of throwing, used by ResolveStack for VLT_STK_BYREF dereference. 7. Renamed NativeVarLocation.IsContextRegister to IsRegisterValue for clarity: in the cDAC model, this indicates the Address field contains the register value directly (not a target memory address to read from). 8. ClrDataValue fixes from review: - Removed unused _baseAddr field and using directive - GetSize returns E_NOINTERFACE when totalSize is 0 (matching native) - GetBytes wraps reads in try/catch, returns ex.HResult on failure - Moved ClrDataValueFlag and VLOC constants to IXCLRData.cs 9. GetLocalVariableByIndex: set nameLen even when bufLen=0/name=null, matching COM buffer-size query pattern. 10. NativeVarInfo.cs doc: fixed reference from IStackWalk to IDebugInfo. 11. Removed unused IRuntimeTypeSystem variable in dump tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataValue.cs
Show resolved
Hide resolved
...c/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/DebugInfoHelpers.cs
Show resolved
Hide resolved
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs
Outdated
Show resolved
Hide resolved
This was referenced Mar 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implement
GetArgumentByIndexandGetLocalVariableByIndexon the managedClrDataFrame(IXCLRDataFrame), replacing the legacy-only delegation with cDAC implementations that match the behavior of the nativestack.cppimplementations.Builds on top of #125064.
Native DAC behavior change: REGNUM_AMBIENT_SP on AMD64
The JIT emits
REGNUM_AMBIENT_SPas the base register for stack-based locals in non-frame-pointer methods (scopeinfo.cpp:334). On AMD64,GetRegOffsInCONTEXTdid not handle this register — it fell through to the default assert and returned(SIZE_T)(-1), causingNativeVarLocationsto read garbage from the CONTEXT structure. x86, ARM, and ARM64 already mappedAMBIENT_SPto the stack pointer.This PR adds the same mapping on AMD64 (
REGNUM_AMBIENT_SP -> RSP), matching other platforms. This is a best-effort approximation — ambient SP is the entry-time SP before prolog adjustments, not necessarily the current RSP. There is an existing TODO inutil.cpp:227acknowledging this limitation.Changes
Contract layer (DebugInfo)
IDebugInfo: AddedGetMethodVarInfo(returns stableDebugVarInfoentries) andGetVariableLocations(resolves physical addresses using CPU context)DebugVarLocKind/DebugVarInfo: Stable public types abstracting over internalVarLocTypevaluesNativeVarLocation: Resolved physical variable location (address, size, register vs memory)DebugInfoHelpers: AddedDoVarsnibble decoder andResolveVarLocationwith register mapping for x64/x86/ARM64/ARM, includingREGNUM_AMBIENT_SPhandlingDebugInfo_2;DebugInfo_1left asNotImplementedVarLocType/NativeVarInfoin Contracts project (versioned, not publicly exposed)Contract layer (StackWalk)
GetInstructionPointertoIStackWalkfor retrieving frame IPClrDataFrame
GetArgumentByIndex: Parameter name resolution from metadata,thishandling, signature type resolution for primitive size adjustmentGetLocalVariableByIndex: Local count validation,varInfoSlot = index + numArgs, empty names (Whidbey limitation)GetMethodInfo,GetMethodSignatureInfo,GetLocalVariableCounthelperslegacyImplpattern on both methodsNew ClrDataValue class
[GeneratedComClass]implementingIXCLRDataValueGetFlags,GetAddress,GetSize,GetBytes,GetNumLocations,GetLocationByIndexGetSizereturnsE_NOINTERFACEwhen total size is 0 (matching native DAC)GetBytescatches read exceptions and returns HRESULT (matching native DACEX_CATCH)Native DAC fix (util.cpp)
REGNUM_AMBIENT_SPcase toGetRegOffsInCONTEXTon AMD64, mapping tooffsetof(CONTEXT, Rsp)Documentation
DebugInfo.md(Version 2 section)Tests
Verification
!clrstack -aoutput matches byte-for-byte between legacy DAC and cDAC (verified via cdb/SOS)ClrDataStackWalk.GetContextfailure from [cDAC] Implement GetContext and GetAppDomain on ClrDataFrame #125064)