[cDAC] WebAssembly: real-data R2R stack-walk validation tests - #131161
Open
lewing wants to merge 6 commits into
Open
[cDAC] WebAssembly: real-data R2R stack-walk validation tests#131161lewing wants to merge 6 commits into
lewing wants to merge 6 commits into
Conversation
The runtime registers R2R virtual-IP ranges lazily, so s_pFunctionTableIndexRangeList is an empty (null) list head until the first managed stack walk — which is the actual runtime state today. Add a test asserting FindSection treats an empty list as a clean "no R2R sections" (TryGetVirtualIPBase / TryGetUnwindData return false) rather than dereferencing the null head. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: afa9efc6-f596-41bd-9070-dc651db8b7b4
Anchors the WasmR2RInfo / WasmUnwinder forward path to values captured from a live
dispatching composite ReadyToRun WASM build (a merged CoreLib R2R image): the
FunctionTableIndexRangeSection node { MinFunctionTableIndex = 6259,
NumRuntimeFunctions = 45283 }, ReadyToRunInfo.MinVirtualIP = 0, and a dispatched
System.Exception..ctor frame storing its function-table index (6259 + 1040) at
frame+0 and its function-local virtual IP / 2 at frame+4.
- RealDispatchingBuild_ResolvesFunctionTableIndexInRange: a real function-table index
resolves through the single range node; base virtual IP = MinVirtualIP + BeginAddress;
the [Min, Min + Num) boundaries are exclusive at the top.
- RealDispatchingBuild_UnwinderDecodesRealFrameVirtualIP: end-to-end GetVirtualIP over
the real frame through the real WasmR2RInfo, validating the * 2 encoding on the frame
field with no shift on BeginAddress.
Full cDAC suite: 2766 passed / 0 failed / 16 skipped.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: afa9efc6-f596-41bd-9070-dc651db8b7b4
|
Azure Pipelines: Successfully started running 4 pipeline(s). 12 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
|
Tagging subscribers to this area: @agocke |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR strengthens cDAC’s WebAssembly ReadyToRun (R2R) stack-walk coverage by adding unit tests that validate WasmR2RInfo / WasmUnwinder behavior against an empty range-list state and against a “real-data” fixture modeled after a dispatching composite R2R WASM build.
Changes:
- Extend the test target builder to optionally model an empty
FunctionTableIndexRangeList(null list head). - Add
EmptyRangeList_ResolvesToNoSectionto lock in “empty list => clean miss” behavior for bothTryGetVirtualIPBaseandTryGetUnwindData. - Add two “real-data” fixture tests that validate range inclusion boundaries and end-to-end virtual IP reconstruction through
WasmUnwinder.GetVirtualIP.
Comment-only; no behavior change. - WasmR2RInfoTests: the empty-range-list test claimed the runtime registers virtual-IP ranges "lazily ... until the first managed stack walk". That is wrong: ReadyToRunInfo::RegisterVirtualIPRange runs per module while applying eager fixups at module load (Module::RunEagerFixupsUnlocked). An empty list is still a legitimate state -- an interpreter-only process, or before any ReadyToRun module has loaded -- so the test stands; only its rationale was incorrect. - WasmR2RInfoTests: the captured-value comment implied the per-frame values were read from an observed live frame. They come from disassembling the composite's R2R prolog for System.Exception..ctor; say so. - WasmR2RInfo: the class summary still listed ExecutionManager::IsFuncletFunctionIndex among the mirrored native APIs after that member was removed from IWasmR2RInfo. The class still reads the funclet flag internally, so only the API list changes. - WasmUnwinder: point at src/coreclr/vm/wasm/callhelpers.hpp, which defines the frame offsets and sentinels the unwinder mirrors, alongside helpers.cpp (the walk). Full cDAC suite: 2766 passed / 0 failed / 16 skipped. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: afa9efc6-f596-41bd-9070-dc651db8b7b4
lewing
added a commit
that referenced
this pull request
Jul 25, 2026
…(wasi-sdk) (#131241) ## Summary On non-emscripten wasm (wasi-sdk), the cDAC contract descriptor getter `GetDotNetRuntimeContractDescriptor` is not exported, so `--gc-sections` strips the descriptor from the final module. This prevents a native host / the cDAC reader from discovering the runtime data-contract descriptor on WASI. The emscripten path already handles this via `EMSCRIPTEN_KEEPALIVE`, which both exports the getter and keeps the descriptor alive. There was no equivalent for the wasi-sdk (`__wasm__`, non-emscripten) toolchain. ## Fix Add an `#elif defined(EXPORT_CONTRACT) && defined(__wasm__)` branch that emits the getter with `__attribute__((export_name("Get" #CONTRACT_NAME)))`. The explicit wasm export both makes the symbol reachable by a native host and roots it so `--gc-sections` does not remove the descriptor — mirroring what `EMSCRIPTEN_KEEPALIVE` does on the emscripten path. The `GETTER_NAME` token-paste macros are hoisted above the `#if` (shared by both branches) and a `GETTER_NAME_STR` stringize pair is added to produce the literal export name `"GetDotNetRuntimeContractDescriptor"`. ## Validation - WASI CoreCLR build (`clr -os wasi -c Release`): compiles clean (0W/0E). - Confirmed the compiled object exports `GetDotNetRuntimeContractDescriptor` with the `no_strip` flag, so it survives `--gc-sections`. This unblocks live cDAC discovery on WASI (non-emscripten) — the runtime-side counterpart to the cDAC-wasm validation work (#131161). Fixture-based cDAC tests do not depend on it. > [!NOTE] > This PR was authored with the assistance of GitHub Copilot. --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Copilot-Session: 2902374a-f352-4c46-bcec-8a35c97733ae Copilot-Session: 50ddfb2c-b6f8-4847-81e7-44d37d44a175
lewing
marked this pull request as ready for review
July 25, 2026 18:51
|
Azure Pipelines: Successfully started running 4 pipeline(s). 12 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
…dac-wasm-r2r-validation
Comment on lines
+169
to
+173
| // A contiguous runtime function table covering local indices [0, RealLocalIndex]. Only the | ||
| // entry at RealLocalIndex carries a non-zero (non-funclet) BeginAddress; the rest read as zero. | ||
| uint runtimeFunctionStride = runtimeFunctionLayout.Stride; | ||
| uint tableEntries = RealLocalIndex + 1; | ||
| var runtimeFuncTableFrag = allocator.Allocate((ulong)(tableEntries * runtimeFunctionStride), "RuntimeFunctions"); |
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
Follow-up to #130988 (cDAC WebAssembly support). That PR added the WASM cDAC stack-walk plug-in (
WasmContext/WasmUnwinder/WasmR2RInfo/WasmFrameHandler) and the metadata-resolution contract fixes, with unit coverage built on synthetic mock values. This PR anchors the R2R forward path to real runtime behavior and corrects several inaccurate comments.Tests
Empty range-list guard —
s_pFunctionTableIndexRangeListis an empty (null) list head until the runtime populates it per module while applying eager fixups at module load. That empty state is legitimate — an interpreter-only process, or before any ReadyToRun module has loaded — soWasmR2RInfo.FindSectionmust treat it as a clean "no R2R sections" answer rather than dereferencing the null head.EmptyRangeList_ResolvesToNoSectionlocks that in.Real-data R2R validation — two tests anchored to values captured from a live dispatching composite ReadyToRun WASM build (a merged CoreLib R2R image):
FunctionTableIndexRangeSectionnode:MinFunctionTableIndex = 6259,NumRuntimeFunctions = 45283, single node (pNext = 0).ReadyToRunInfo.MinVirtualIP = 0(single-image composite → base virtual IP reduces toBeginAddress).System.Exception..ctor: on entry it stores its function-table index (6259 + 1040) atframe+0and its function-local virtual IP / 2 atframe+4.RealDispatchingBuild_ResolvesFunctionTableIndexInRangevalidatesFindSection/TryGetVirtualIPBaseagainst the real range (including the exclusive[Min, Min+Num)top boundary).RealDispatchingBuild_UnwinderDecodesRealFrameVirtualIPdrivesWasmUnwinder.GetVirtualIPthrough the realWasmR2RInfoend-to-end — the first test exercising the unwinder against the real section reader rather than a fake — validating the× 2encoding on the frame field with no shift onBeginAddress.Comment corrections (no behavior change)
WasmR2RInfo: the class summary still listedExecutionManager::IsFuncletFunctionIndexamong the mirrored native APIs after that member was removed fromIWasmR2RInfoin review. The class still reads the funclet flag internally, so only the API list changes.WasmUnwinder: now also points atsrc/coreclr/vm/wasm/callhelpers.hpp, which defines the frame offsets and sentinels the unwinder mirrors, alongsidehelpers.cpp(the walk itself).WasmR2RInfoTests: an earlier draft of the empty-list test claimed the runtime registers virtual-IP ranges "lazily ... until the first managed stack walk". That is incorrect —ReadyToRunInfo::RegisterVirtualIPRangeruns per module during eager fixups at module load (Module::RunEagerFixupsUnlocked). The test stands; only its rationale was wrong.Context
The captured values come from cross-session live debugging of a dispatching R2R-WASM build. A notable finding from that work: on WASM the R2R virtual-IP walk is DAC/reader-driven — managed
StackTrace/ EH / GC do not invoke it in-process; theWasmUnwinderitself is the walker, driven against a paused target. So these fixture-based tests (replaying captured structure values) are the right CI shape and require no WASM runtime in CI. A future PR can add a live reader-drives-debugger pass once broader R2R dispatch is available.Testing
× 2to× 3fails it (9746 vs 9747), confirming it guards the real encoding rather than passing vacuously.Note
This pull request was authored with the assistance of GitHub Copilot.