Skip to content

[cDAC] Implement GetArgumentByIndex and GetLocalVariableByIndex on ClrDataFrame#125463

Draft
max-charlamb wants to merge 9 commits intodotnet:mainfrom
max-charlamb:cdac-dataframe-value-apis
Draft

[cDAC] Implement GetArgumentByIndex and GetLocalVariableByIndex on ClrDataFrame#125463
max-charlamb wants to merge 9 commits intodotnet:mainfrom
max-charlamb:cdac-dataframe-value-apis

Conversation

@max-charlamb
Copy link
Member

@max-charlamb max-charlamb commented Mar 11, 2026

Summary

Implement GetArgumentByIndex and GetLocalVariableByIndex on the managed ClrDataFrame (IXCLRDataFrame), replacing the legacy-only delegation with cDAC implementations that match the behavior of the native stack.cpp implementations.

Builds on top of #125064.

Native DAC behavior change: REGNUM_AMBIENT_SP on AMD64

The JIT emits REGNUM_AMBIENT_SP as the base register for stack-based locals in non-frame-pointer methods (scopeinfo.cpp:334). On AMD64, GetRegOffsInCONTEXT did not handle this register — it fell through to the default assert and returned (SIZE_T)(-1), causing NativeVarLocations to read garbage from the CONTEXT structure. x86, ARM, and ARM64 already mapped AMBIENT_SP to 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 in util.cpp:227 acknowledging this limitation.

Changes

Contract layer (DebugInfo)

  • IDebugInfo: Added GetMethodVarInfo (returns stable DebugVarInfo entries) and GetVariableLocations (resolves physical addresses using CPU context)
  • DebugVarLocKind / DebugVarInfo: Stable public types abstracting over internal VarLocType values
  • NativeVarLocation: Resolved physical variable location (address, size, register vs memory)
  • DebugInfoHelpers: Added DoVars nibble decoder and ResolveVarLocation with register mapping for x64/x86/ARM64/ARM, including REGNUM_AMBIENT_SP handling
  • Implemented in DebugInfo_2; DebugInfo_1 left as NotImplemented
  • Internal VarLocType/NativeVarInfo in Contracts project (versioned, not publicly exposed)

Contract layer (StackWalk)

  • Added GetInstructionPointer to IStackWalk for retrieving frame IP

ClrDataFrame

  • GetArgumentByIndex: Parameter name resolution from metadata, this handling, signature type resolution for primitive size adjustment
  • GetLocalVariableByIndex: Local count validation, varInfoSlot = index + numArgs, empty names (Whidbey limitation)
  • Extracted GetMethodInfo, GetMethodSignatureInfo, GetLocalVariableCount helpers
  • Legacy verification via legacyImpl pattern on both methods

New ClrDataValue class

  • [GeneratedComClass] implementing IXCLRDataValue
  • Implements GetFlags, GetAddress, GetSize, GetBytes, GetNumLocations, GetLocationByIndex
  • Register-based variable values written directly as bytes
  • Legacy verification on all implemented methods
  • GetSize returns E_NOINTERFACE when total size is 0 (matching native DAC)
  • GetBytes catches read exceptions and returns HRESULT (matching native DAC EX_CATCH)

Native DAC fix (util.cpp)

  • Added REGNUM_AMBIENT_SP case to GetRegOffsInCONTEXT on AMD64, mapping to offsetof(CONTEXT, Rsp)
  • Matches existing x86/ARM/ARM64 behavior

Documentation

  • Added VarLocType constants, Vars encoding format, and new APIs to DebugInfo.md (Version 2 section)

Tests

  • LocalVariables debuggee: Static method with int/string args+locals, generic method with generic local
  • IXCLRDataFrame tests (6 new): GetArgumentByIndex/GetLocalVariableByIndex value retrieval, name resolution, error cases
  • IXCLRDataValue tests (10 new): GetSize, GetFlags, GetBytes, GetNumLocations, GetLocationByIndex, parameter names, cross-validation

Verification

Copilot AI review requested due to automatic review settings March 11, 2026 22:27
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 / GetLocalVariableByIndex using cDAC debug info, and add a new managed ClrDataValue (IXCLRDataValue) for returned argument/local values.
  • Add new dump debuggee (LocalVariables) and dump-based integration tests for IXCLRDataFrame, IXCLRDataValue, and IXCLRDataAppDomain.

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

@max-charlamb max-charlamb added the NO-REVIEW Experimental/testing PR, do NOT review it label Mar 11, 2026
max-charlamb and others added 4 commits March 12, 2026 14:10
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>
@max-charlamb max-charlamb force-pushed the cdac-dataframe-value-apis branch from 50b6258 to 487d34e Compare March 12, 2026 18:11
Copilot AI review requested due to automatic review settings March 12, 2026 21:55
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.

@max-charlamb max-charlamb force-pushed the cdac-dataframe-value-apis branch from 5ec7202 to 47b23bf Compare March 13, 2026 18:47
Copilot AI review requested due to automatic review settings March 13, 2026 20:47
@max-charlamb max-charlamb force-pushed the cdac-dataframe-value-apis branch from 47b23bf to 790f597 Compare March 13, 2026 20:47
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.

@max-charlamb max-charlamb force-pushed the cdac-dataframe-value-apis branch from 790f597 to 9b0a451 Compare March 13, 2026 21:52
Copilot AI review requested due to automatic review settings March 16, 2026 14:19
@max-charlamb max-charlamb force-pushed the cdac-dataframe-value-apis branch from 9b0a451 to dafa127 Compare March 16, 2026 14:19
@max-charlamb max-charlamb force-pushed the cdac-dataframe-value-apis branch from dafa127 to f00fc33 Compare March 16, 2026 14:22
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.

@max-charlamb max-charlamb force-pushed the cdac-dataframe-value-apis branch from f00fc33 to 0d65124 Compare March 16, 2026 16:05
Copilot AI review requested due to automatic review settings March 16, 2026 16:08
@max-charlamb max-charlamb force-pushed the cdac-dataframe-value-apis branch from 0d65124 to 8a5ac15 Compare March 16, 2026 16:08
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>
@max-charlamb max-charlamb force-pushed the cdac-dataframe-value-apis branch from 14e2764 to 6da2d24 Compare March 16, 2026 16:12
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 7 comments.

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>
@max-charlamb max-charlamb force-pushed the cdac-dataframe-value-apis branch from 6da2d24 to 2b8e801 Compare March 16, 2026 16:16
Max Charlamb and others added 2 commits March 16, 2026 12:41
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>
Copilot AI review requested due to automatic review settings March 16, 2026 18:51
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants