[cDAC][wasm] Map portable entry points to interpreter code and R2R virtual IPs - #134756
Merged
lewing merged 2 commits intoSep 29, 2026
Conversation
…rtual IPs On FEATURE_PORTABLE_ENTRYPOINTS targets (WebAssembly), a method's entry point is a PortableEntryPoint address that does not resolve through ExecutionManager lookups, so SOS/DBI reported unusable native code addresses. Add IExecutionManager.GetDiagnosticCodeStartFromEntryPoint, mirroring the native GetDiagnosticCodeStartFromEntryPoint: - Without portable entrypoints it delegates to PrecodeStubs.GetInterpreterCodeFromInterpreterPrecodeIfPresent (unchanged behavior). - With portable entrypoints it first checks whether the address is in a code range (including Wasm R2R virtual-IP ranges), then maps interpreted methods to their InterpByteCodeStart and native Wasm R2R methods from their function-table index to the synthetic virtual IP (skipping funclet entries). As in native code, the R2R path applies only to the method's own (temporary) entry point. Extract function-table-index resolution into ExecutionManagerHelpers.WasmFunctionTableIndexLookup with bounded, cycle-detecting list traversal; the stack walk's WasmR2RInfo wraps it. Switch the Legacy SOS/DBI callers to the new API. Adds the PortableEntryPoint.ActualCode/Flags and MethodDesc.InterpreterCode data descriptors, ExecutionManager contract docs, and unit tests. Fixes #134753 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines: Successfully started running 5 pipeline(s). 11 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
lewing
added this pull request to stack #134757
September 27, 2026 22:26
Contributor
|
Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara |
Contributor
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unresolved issues affect entry-point publication checks, non-WASM gating, async-local enumeration, and ReadyToRun classification.
Review effort: Lite
Findings: 1
Open (1)
What changed in this PR
Adds cDAC support for resolving WebAssembly portable entry points to interpreter code or ReadyToRun virtual IPs.
Changes:
- Adds diagnostic entry-point mapping and shared WASM function-table lookup.
- Updates SOS/DBI callers, descriptors, tests, and documentation.
- Adds cycle and bounds protection for function-table traversal.
| File | Description |
|---|---|
src/native/managed/cdac/tests/UnitTests/SOSDacInterface5Tests.cs |
Updates mocks for diagnostic address mapping. |
src/native/managed/cdac/tests/UnitTests/ExecutionManager/ExecutionManagerTests.cs |
Adds portable-entry-point and cycle tests. |
src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs |
Updates diagnostic mapping mocks. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs |
Uses resolved diagnostic addresses. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs |
Maps SOS native-code addresses. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs |
Maps DBI native-code addresses. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataMethodInstance.cs |
Maps method-instance code addresses. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PortableEntryPoint.cs |
Adds portable-entry-point fields. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDesc.cs |
Adds interpreter-code metadata. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/Wasm/WasmR2RInfo.cs |
Reuses shared WASM lookup logic. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/WasmFunctionTableIndexLookup.cs |
Implements bounded function-table traversal. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs |
Resolves diagnostic code starts. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs |
Exposes the new contract implementation. |
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs |
Adds the diagnostic mapping contract method. |
src/coreclr/vm/precode_portable.hpp |
Exposes portable-entry-point descriptor offsets. |
src/coreclr/vm/method.hpp |
Exposes interpreter-code descriptor metadata. |
src/coreclr/vm/datadescriptor/datadescriptor.inc |
Registers new data descriptors. |
docs/design/datacontracts/RuntimeTypeSystem.md |
Documents interpreter-code metadata. |
docs/design/datacontracts/ExecutionManager.md |
Documents mapping behavior and WASM data. |
docs/design/datacontracts/data-descriptor-meanings.json |
Adds descriptor meanings. |
EnumerateAsyncLocals passed the raw native-code/entry-point value to DebugInfo.GetAsyncSuspensionPoints, which requires a resolvable code block. Map it through GetDiagnosticCodeStartFromEntryPoint, and apply the matching GetInterpreterCodeFromEntryPointIfPresent mapping in the native DAC so the cDAC/DAC cross-validation stays consistent (mirrors GetMethodVarInfo in daccess.cpp). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Member
Author
|
/ba-g unrelated *DAC windows failures that BA can't match |
lewing
merged commit Sep 29, 2026
257bac4
into
lewing-fix-wasm-r2r-statemachine-profiler-calls
135 of 140 checks passed
lewing
added a commit
that referenced
this pull request
Sep 29, 2026
…rtual IPs (#134827) This replaces #134756, which was merged into #134754's branch by mistake and reverted there. The change is otherwise identical (cherry-picked onto `main`). ## Problem On `FEATURE_PORTABLE_ENTRYPOINTS` targets (WebAssembly), a method's entry point slot holds a `PortableEntryPoint` address. Native maps it before handing it to diagnostics (`GetInterpreterCodeFromEntryPointIfPresent` / `GetDiagnosticCodeStartFromEntryPoint` in `precode.cpp`), but the cDAC returned the raw address. SOS values such as `DacpMethodDescData.NativeCodeAddr` and rejit `NativeCodeAddr`, plus the DBI/`ClrDataMethodInstance` paths, therefore reported an address that doesn't resolve through `ExecutionManager` for both interpreted and R2R methods. ## Change Adds `IExecutionManager.GetDiagnosticCodeStartFromEntryPoint`, which mirrors the native `GetDiagnosticCodeStartFromEntryPoint`: - **Without portable entrypoints:** delegates to `PrecodeStubs.GetInterpreterCodeFromInterpreterPrecodeIfPresent`. Behavior is unchanged. - **With portable entrypoints:** 1. Returns the address unchanged if it lies in a code range. This is the native `FindCodeRange` check and includes Wasm R2R virtual-IP ranges from #133917. 2. Maps interpreted methods to `MethodDesc::m_interpreterCode`. 3. Maps native R2R methods from the function-table index in `PortableEntryPoint._pActualCode` to the synthetic virtual IP, stepping back past funclet entries. This matches `ExecutionManager::GetWasmVirtualIPFromFunctionTableIndex`. As in native code, it applies only to the method's own (temporary) entry point, and only when the entry point doesn't prefer the interpreter. The mapping lives in ExecutionManager rather than PrecodeStubs because ExecutionManager owns the virtual-IP ranges and R2R lookup. ExecutionManager already depends on PrecodeStubs, so this adds no contract cycle. Function-table-index resolution moves into `ExecutionManagerHelpers.WasmFunctionTableIndexLookup`, which now bounds the list walk and detects cycles, like the virtual-IP list walk. The stack walk's `WasmR2RInfo` becomes a thin wrapper over it. The Legacy SOS/DBI callers (`SOSDacImpl`, `ClrDataMethodInstance`, `DacDbiImpl`) now call the new API. `DacDbiImpl.EnumerateAsyncLocals` also maps its code address before querying async debug info. The native DAC's `EnumerateAsyncLocals` gets the matching `GetInterpreterCodeFromEntryPointIfPresent` mapping (as `GetMethodVarInfo` already does), so the debug-build cDAC/DAC cross-check stays consistent. ### Data descriptors - `PortableEntryPoint.ActualCode` and `PortableEntryPoint.Flags` (only under `FEATURE_PORTABLE_ENTRYPOINTS`). - `MethodDesc.InterpreterCode` (only under `FEATURE_INTERPRETER`). - The contract relies on `kPrefersInterpreterEntryPoint` and `INTERPRETER_CODE_POISON`; the native side now has `[cDAC]` comments marking that dependency. ### Interaction with #133890 #133890 adds `TryGetFunctionIdentity` and `TryIsFunclet` to `WasmR2RInfo`. Whichever PR lands second should add those two methods to `WasmFunctionTableIndexLookup` and have `WasmR2RInfo` forward to them. The `FunctionTableIndexRange*` descriptor meanings here already use #133890's exact wording, so that JSON should merge cleanly. ## Validation - cDAC: `./build.sh -s tools.cdac+tools.cdactests -c Debug -test` passes: 3196 unit tests (17 new), usage tests (contract cycles and generated docs up to date), and generator tests. - New ExecutionManager tests cover: - interpreted, R2R, funclet, poison, prefers-interpreter, not-own-entry-point and unknown-index cases; - an end-to-end check that the resolved virtual IP maps back to the MethodDesc through `GetCodeBlockHandle`; - readable `PortableEntryPoint`-shaped bytes inside a registered code range staying unchanged. This test fails if the range check is removed. - a cyclic function-table range list; - the non-portable delegation path. - CoreCLR `clr.runtime` builds for osx-arm64 Debug (with `FEATURE_INTERPRETER`) and browser-wasm Debug. The generated wasm descriptor has `PortableEntryPoint {ActualCode@0, MethodDesc@4, Flags@12}`. - A new `DacDbiImplTests` test covers `EnumerateAsyncLocals` mapping for both the code-address and MethodDesc paths. It fails without the fix. - cDAC dump tests (osx-arm64, local runtime): 260 passed, 0 failed. The 746 skips are net10.0 configurations plus by-design skips (Windows-only COM debuggees, and dump types a debuggee doesn't produce). - CoreCLR `clr.runtime` Release (osx-arm64) builds with the `dacdbiimpl.cpp` change. - Re-validated after cherry-picking onto `main`: `./build.sh clr -c Debug` (osx-arm64) builds, and `./build.sh -s tools.cdac+tools.cdactests -c Debug -test` passes (3196 unit tests, 46 generator tests, 4 usage tests; 0 failed). - Not run: any check against a live wasm target. Resolves #134753 > [!NOTE] > This PR description was generated with GitHub Copilot. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
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.

Important
Stacked on #134754 (targets its branch). Only the commits above #134754's head belong to this PR; retarget to
mainonce #134754 merges.Problem
On
FEATURE_PORTABLE_ENTRYPOINTStargets (WebAssembly), a method's entry point slot holds aPortableEntryPointaddress. Native maps it before handing it to diagnostics (GetInterpreterCodeFromEntryPointIfPresent/GetDiagnosticCodeStartFromEntryPointinprecode.cpp), but the cDAC returned the raw address. SOS values such asDacpMethodDescData.NativeCodeAddrand rejitNativeCodeAddr, plus the DBI/ClrDataMethodInstancepaths, therefore reported an address that doesn't resolve throughExecutionManagerfor both interpreted and R2R methods.Change
Adds
IExecutionManager.GetDiagnosticCodeStartFromEntryPoint, which mirrors the nativeGetDiagnosticCodeStartFromEntryPoint:PrecodeStubs.GetInterpreterCodeFromInterpreterPrecodeIfPresent. Behavior is unchanged.FindCodeRangecheck and includes Wasm R2R virtual-IP ranges from [cDAC][wasm] Resolve ReadyToRun virtual IP ranges #133917.MethodDesc::m_interpreterCode.PortableEntryPoint._pActualCodeto the synthetic virtual IP, stepping back past funclet entries. This matchesExecutionManager::GetWasmVirtualIPFromFunctionTableIndex. As in native code, it applies only to the method's own (temporary) entry point, and only when the entry point doesn't prefer the interpreter.The mapping lives in ExecutionManager rather than PrecodeStubs because ExecutionManager owns the virtual-IP ranges and R2R lookup. ExecutionManager already depends on PrecodeStubs, so this adds no contract cycle.
Function-table-index resolution moves into
ExecutionManagerHelpers.WasmFunctionTableIndexLookup, which now bounds the list walk and detects cycles, like the virtual-IP list walk. The stack walk'sWasmR2RInfobecomes a thin wrapper over it.The Legacy SOS/DBI callers (
SOSDacImpl,ClrDataMethodInstance,DacDbiImpl) now call the new API.DacDbiImpl.EnumerateAsyncLocalsalso maps its code address before querying async debug info. The native DAC'sEnumerateAsyncLocalsgets the matchingGetInterpreterCodeFromEntryPointIfPresentmapping (asGetMethodVarInfoalready does), so the debug-build cDAC/DAC cross-check stays consistent.Data descriptors
PortableEntryPoint.ActualCodeandPortableEntryPoint.Flags(only underFEATURE_PORTABLE_ENTRYPOINTS).MethodDesc.InterpreterCode(only underFEATURE_INTERPRETER).kPrefersInterpreterEntryPointandINTERPRETER_CODE_POISON; the native side now has[cDAC]comments marking that dependency.Interaction with #133890
#133890 adds
TryGetFunctionIdentityandTryIsFunclettoWasmR2RInfo. Whichever PR lands second should add those two methods toWasmFunctionTableIndexLookupand haveWasmR2RInfoforward to them. TheFunctionTableIndexRange*descriptor meanings here already use #133890's exact wording, so that JSON should merge cleanly.Validation
./build.sh -s tools.cdac+tools.cdactests -c Debug -testpasses: 3196 unit tests (17 new), usage tests (contract cycles and generated docs up to date), and generator tests.GetCodeBlockHandle;PortableEntryPoint-shaped bytes inside a registered code range staying unchanged. This test fails if the range check is removed.clr.runtimebuilds for osx-arm64 Debug (withFEATURE_INTERPRETER) and browser-wasm Debug. The generated wasm descriptor hasPortableEntryPoint {ActualCode@0, MethodDesc@4, Flags@12}.DacDbiImplTeststest coversEnumerateAsyncLocalsmapping for both the code-address and MethodDesc paths. It fails without the fix.clr.runtimeRelease (osx-arm64) builds with thedacdbiimpl.cppchange.Fixes #134753
Note
This PR description was generated with GitHub Copilot.