Fix Wasm async continuation stack walk - #133995
Conversation
Use the caller shadow stack virtual IP for the interpreter continuation transition frame and exercise both cold and warm FCall paths. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8a57fff8-6c30-4d3c-ac72-a80975122a96
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
Add the active re-enable tracker beside the project-level WebAssembly exclusion. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8a57fff8-6c30-4d3c-ac72-a80975122a96
|
This got opened too early, fixing |
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
The transition-frame virtual IP should be derived lazily during stack inspection.
Review tier: Lite
Findings: None
What changed in this PR
Fixes Wasm async continuation stack walking and adds focused regression coverage.
Changes:
- Updates Wasm continuation transition-frame metadata.
- Adds a Wasm-specific async test runner.
- Runs cold and warm diagnostic stack-trace scenarios.
| File | Description |
|---|---|
src/tests/async/diagnostic-ip-stacktrace/diagnostic-ip-stacktrace.cs |
Runs the diagnostic scenario twice. |
src/tests/async/async.csproj |
Keeps the general async runner disabled on Wasm. |
src/tests/async/async-wasm.csproj |
Adds the focused Wasm runner. |
src/coreclr/vm/interpexec.cpp |
Updates the Wasm continuation transition frame return address. |
This reverts commit b3b2ec9.
Guard synchronous-wait tests with the multithreading capability, quarantine the two exact unsupported Wasm cases, and preserve all native coverage. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8a57fff8-6c30-4d3c-ac72-a80975122a96
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara |
Enable the full runner, gate multithreading-dependent tests by capability, and quarantine only exact unsupported Wasm scenarios. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8a57fff8-6c30-4d3c-ac72-a80975122a96
Keep the shared TestLibrary reference on standalone async children while relying on the parent test targets for the merged runner. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8a57fff8-6c30-4d3c-ac72-a80975122a96
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unresolved critical and moderate test-gating issues remain in pinvoke.cs and simple-eh.cs.
Get a fresh assessment by requesting another Copilot review.
Review tier: Lite
Findings: 1
Open (1)
Resolved since last review (2)
| public class AwaitNotAsync | ||
| { | ||
| [Fact] | ||
| [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] |
There was a problem hiding this comment.
Is this really how we want to deal with this for wasm? I do not think it is very reasonable to have to mark all async tests this way if they don't finish synchronously....
There was a problem hiding this comment.
Browser has single thread.
.Wait() is or should be PNSE.
The better way is to make the whole unit test method async and await instead of .Wait().
I'm not sure if some of the tests actually validate .Wait() specifically, those should stay ConditionalFact ... IsMultithreadingSupported
There was a problem hiding this comment.
The better way is to make the whole unit test method async and
awaitinstead of.Wait().
Yes, I agree. I think this requires work in the test generator to support Task-returning tests and to hook that up in the way WASM expects.
I'm not sure if some of the tests actually validate
.Wait()specifically, those should stayConditionalFact ... IsMultithreadingSupported
We may start and run async tasks in parallel, but I do not think any runtime async test should be validating Task.Wait() specifically. It is all about waiting for the tasks to finish.
There was a problem hiding this comment.
@lewing could you please ask your copilot to do that as follow up?
## Summary - use the caller shadow stack's virtual IP as the return address for the Wasm `ResumeInterpreterContinuation` transition frame - enable the full runtime-async runner on Wasm while filtering only tests that require multithreading - use class-level capability conditions where every test in a class requires multithreading and method-level conditions for mixed classes - quarantine only the exact unsupported cases: `Async2PInvoke.TestEntryPoint` under dotnet#124219 and `Async2CollectibleAlc` under dotnet#134001 - run the diagnostic stack-trace scenario twice to cover cold and warm FCall paths - make reflection method ordering deterministic with `StringComparer.Ordinal` ## Root cause The Wasm FCall supplied the managed caller's R2R shadow stack pointer but used the native `ResumeInterpreterContinuation` entrypoint as the transition block return address. On the warm path, stack walking paired those unrelated values and failed the `GetWasmVirtualIPFromStackPointer(sp) == controlPC` invariant. dotnet#133768 also enabled the full `src/tests/async` runner on Wasm. Many tests intentionally block on incomplete tasks and therefore require multithreading; those tests now use `PlatformDetection.IsMultithreadingSupported` rather than disabling the whole runner. ## Validation - browser-Wasm Checked CoreCLR build - browser-Wasm interpreter runner: 142 discovered, 42 passed, 0 failed, 100 capability/issue filtered - browser-Wasm R2R-CG2 runner: 142 discovered, 42 passed, 0 failed, 100 capability/issue filtered - WASI runner: 142 discovered, 44 passed, 0 failed, 98 capability/issue filtered - native macOS arm64 runner: 142 passed, 0 failed - the diagnostic test fails on the old runtime's warm path and passes after the fix Resolves dotnet#133931 > [!NOTE] > This pull request description was generated with GitHub Copilot assistance. --------- Copilot-Session: 8a57fff8-6c30-4d3c-ac72-a80975122a96

Summary
ResumeInterpreterContinuationtransition frameAsync2PInvoke.TestEntryPointunder [wasm][coreclr] GetUnmanagedCallersOnlyThunk: unknown thunk for unmanaged callers only method #124219 andAsync2CollectibleAlcunder Browser WASM collectible ALC test has no loadable assembly path #134001StringComparer.OrdinalRoot cause
The Wasm FCall supplied the managed caller's R2R shadow stack pointer but used the native
ResumeInterpreterContinuationentrypoint as the transition block return address. On the warm path, stack walking paired those unrelated values and failed theGetWasmVirtualIPFromStackPointer(sp) == controlPCinvariant.#133768 also enabled the full
src/tests/asyncrunner on Wasm. Many tests intentionally block on incomplete tasks and therefore require multithreading; those tests now usePlatformDetection.IsMultithreadingSupportedrather than disabling the whole runner.Validation
Resolves #133931
Note
This pull request description was generated with GitHub Copilot assistance.