Skip to content

[cDAC] Use NativeCodeVersion entry for GetMethodVarInfo offset#128154

Merged
max-charlamb merged 4 commits into
dotnet:mainfrom
max-charlamb:fix/cdac-getmethodvarinfo-tiered-compilation
May 14, 2026
Merged

[cDAC] Use NativeCodeVersion entry for GetMethodVarInfo offset#128154
max-charlamb merged 4 commits into
dotnet:mainfrom
max-charlamb:fix/cdac-getmethodvarinfo-tiered-compilation

Conversation

@max-charlamb
Copy link
Copy Markdown
Member

@max-charlamb max-charlamb commented May 13, 2026

Note

This PR was authored with assistance from GitHub Copilot.

Problem

runtime-diagnostics build 1418267 failed with:

Process terminated. Assertion failed.
GetNumLocations cDAC: 0, DAC: 1
   at Microsoft.Diagnostics.DataContractReader.Legacy.ClrDataValue...GetNumLocations(UInt32*)

The asserting frame was the this parameter of ManualResetEventSlim.Wait during !ClrStack -a against the WebApp3 dump.

Root cause

DebugInfo_2.GetMethodVarInfo computed the IP -> codeOffset relative to MethodDesc.NativeCode. That field tracks only the most recently-compiled tier. With tiered compilation, an older-tier frame still on the stack gets the wrong base, no varInfo ranges match, and cDAC returns an empty location list while the legacy DAC finds the entry.

The legacy DAC (src/coreclr/debug/daccess/daccess.cpp:5591) instead uses ExecutionManager::GetNativeCodeVersion(address).GetNativeCode() -- the entry point of the specific NativeCodeVersion that owns the IP.

This bug has been latent since #125463. dotnet/diagnostics#5767 (merged 2026-05-12) rebuilt the debuggees with the .NET 11 test SDK, which increased the chance of hot methods like ManualResetEventSlim.Wait straddling tier boundaries at dump-capture time and surfaced the existing bug.

Fix

Resolve the IP-specific NativeCodeVersion via the ICodeVersions contract (already mirrors ExecutionManager::GetNativeCodeVersion + NativeCodeVersion::GetNativeCode for versionable and non-versionable methods). Throw on an invalid NativeCodeVersion, matching the native DAC's E_INVALIDARG behavior.

Type-correctness fix (uncovered while debugging an ARM32 regression)

While validating the fix above, an ARM32 regression in IXCLRDataValueDumpTests.GetSize_ReturnsExpectedSizes revealed a long-standing mistype:

  • IExecutionManager.GetStartAddress / GetFuncletStartAddress declared TargetCodePointer, but the value stored in CodeBlock is the raw code-block start with no ARM32 thumb bit. That matches native -- EECodeInfo::GetStartAddress and CodeHeader::GetCodeStartAddress both return TADDR -- but the cdac type signature was lying about what the value represented.
  • On ARM32, CodeVersions_1.GetSpecificNativeCodeVersion compared MethodDesc.NativeCode (a PCODE with the thumb bit set) against the raw TADDR from GetStartAddress, so the equality check never matched and GetNativeCodeVersionForIP returned Invalid -- which broke the new GetMethodVarInfo path on ARM32.

The first commit in this PR changes the return types to TargetPointer (matching native TADDR semantics), drops the now-redundant AddressFromCodePointer conversions at the callers, and at the one site that genuinely needs a PCODE for comparison uses CodePointerFromAddress (the cdac analogue of native PINSTRToPCODE). The second commit is the original GetMethodVarInfo change, now safe on ARM32.

Validation

  • Built cdac Contracts and Legacy projects: clean.
  • dotnet test on the full cdac suite: 2133/2133 passed (16 unrelated skips).
  • runtime-diagnostics CI will validate end-to-end behavior on x64 and ARM32.

@dotnet-policy-service
Copy link
Copy Markdown
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
Copy Markdown
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

Fixes a tiered-compilation bug in the managed cDAC's DebugInfo_2.GetMethodVarInfo: the IP-to-codeOffset calculation was based on MethodDesc.NativeCode (most recently compiled tier), causing wrong base offsets for older-tier frames still on the stack and producing empty variable location lists. The fix resolves the IP-specific NativeCodeVersion via the ICodeVersions contract, mirroring the legacy DAC's use of ExecutionManager::GetNativeCodeVersion(address).GetNativeCode().

Changes:

  • Replace RuntimeTypeSystem.GetNativeCode(methodDesc) with CodeVersions.GetNativeCodeVersionForIP(pCode) + CodeVersions.GetNativeCode(ncvh).
  • Throw InvalidOperationException when no NativeCodeVersion is found for the IP, matching native DAC's E_INVALIDARG behavior.

@max-charlamb max-charlamb marked this pull request as ready for review May 13, 2026 17:17
@max-charlamb max-charlamb force-pushed the fix/cdac-getmethodvarinfo-tiered-compilation branch from e9fcac7 to 6ef9e65 Compare May 13, 2026 18:51
Max Charlamb and others added 2 commits May 13, 2026 14:53
IExecutionManager.GetStartAddress and GetFuncletStartAddress were declared to return TargetCodePointer but the value stored in CodeBlock is the raw code-block start with no ARM32 thumb bit. That matches native (EECodeInfo::GetStartAddress and CodeHeader::GetCodeStartAddress return TADDR), but the cdac type signature was misleading and caused subtle bugs whenever a caller needed a true PCODE.

On ARM32, CodeVersions_1.GetSpecificNativeCodeVersion compared MethodDesc.NativeCode (a PCODE with the thumb bit set) against the raw start address returned by GetStartAddress, so the equality check never matched and GetNativeCodeVersionForIP returned Invalid.

Change the return types to TargetPointer (matching native TADDR semantics), drop the now-redundant AddressFromCodePointer conversions at the callers, and at the one site in CodeVersions_1 that genuinely needs a PCODE for comparison use CodePointerFromAddress (the cdac analogue of native PINSTRToPCODE).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DebugInfo_2.GetMethodVarInfo was computing the IP->codeOffset relative to MethodDesc.NativeCode. That field tracks only the most recently-compiled tier, so under tiered compilation an older-tier frame still on the stack gets the wrong base, no varInfo ranges match, and cDAC returns an empty location list while the legacy DAC finds the entry. This trips the Debug.Assert in ClrDataValue.GetNumLocations.

Resolve the IP-specific NativeCodeVersion via the ICodeVersions contract, mirroring the legacy DAC which uses ExecutionManager::GetNativeCodeVersion(address).GetNativeCode() (daccess.cpp:5591). Throw on an invalid NativeCodeVersion, matching the native DAC's E_INVALIDARG behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb max-charlamb force-pushed the fix/cdac-getmethodvarinfo-tiered-compilation branch from 6ef9e65 to 7e3d349 Compare May 13, 2026 18:55
ReadyToRunJitManager.GetMethodInfo computed startAddress as 'imageBase + function.BeginAddress' using the raw RVA. On ARM32 thumb code, the RUNTIME_FUNCTION BeginAddress encodes the thumb bit (bit 0), so the resulting startAddress carried that bit. Native RUNTIME_FUNCTION__BeginAddress (clrnt.h, ARM32 branch) calls ThumbCodeToDataPointer to strip it before use.

With the previous commit changing IExecutionManager.GetStartAddress to return TargetPointer (a raw TADDR), this mismatch became visible: GetExceptionClauses computed 'methodRVA = methodStart - rangeStart' with the thumb bit still set, so the R2R exception-info table lookup missed by one byte and IXCLRDataValueDumpTests.GetExceptionClauses_ContainsCatchAllClause failed on Debian.13.Arm32.

Use CodePointerUtils.AddressFromCodePointer (the cdac analogue of native ThumbCodeToDataPointer) when forming the hot and cold start addresses from the R2R RVA, matching native semantics and producing a true raw TADDR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 13, 2026 21:06
Copy link
Copy Markdown
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 11 out of 11 changed files in this pull request and generated no new comments.

@max-charlamb
Copy link
Copy Markdown
Member Author

/ba-g PR passed CI before docs only change

1 similar comment
@max-charlamb
Copy link
Copy Markdown
Member Author

/ba-g PR passed CI before docs only change

@max-charlamb max-charlamb merged commit a4e4aee into dotnet:main May 14, 2026
22 of 27 checks passed
@max-charlamb max-charlamb deleted the fix/cdac-getmethodvarinfo-tiered-compilation branch May 14, 2026 18:04
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.

3 participants