Skip to content

Debugging: PC in a frame at a callsite should be the return address, not the call.#12750

Merged
cfallin merged 3 commits intobytecodealliance:mainfrom
cfallin:one-may-try-to-return-but-it-is-never-to-the-place-one-started
Mar 10, 2026
Merged

Debugging: PC in a frame at a callsite should be the return address, not the call.#12750
cfallin merged 3 commits intobytecodealliance:mainfrom
cfallin:one-may-try-to-return-but-it-is-never-to-the-place-one-started

Conversation

@cfallin
Copy link
Member

@cfallin cfallin commented Mar 10, 2026

In working out why a finish command in
LLDB-attached-to-Wasmtime-via-gdbstub wasn't working, I discovered that our current debugging APIs, when presenting info from a frame suspended at a callsite up the stack, present the current PC as at the call instruction, rather than past it (at the return address). The latter is conventional on all real ISAs, and is hence what the debugger expects.

This PR makes the most straightforward fix: the debug tuple attached to the call, and hence the metadata read out by the debug frame walker, now encodes the PC of the next opcode. This is sufficient to fix finish within LLDB.

An alternative I considered, and prototyped, is also worth mentioning: one might see the argument for allowing a debugger to see the callsite that invoked the next frame, and separately, see the return address (i.e., both pieces of information are useful). In an alternative branch, there is a new table in the debug frame info metadata giving the size of each callsite, so the debug frame-handle API can present a get-return-address accessor on a frame resource alongside get-pc. Ultimately I opted not to go with this because it has more overhead and complexity and a concrete use-case wasn't forthcoming to me, but I'm happy to reconsider if someone wants that instead.

…not the call.

In working out why a `finish` command in
LLDB-attached-to-Wasmtime-via-gdbstub wasn't working, I discovered that
our current debugging APIs, when presenting info from a frame suspended
at a callsite up the stack, present the current PC as *at* the call
instruction, rather than *past it* (at the return address). The latter
is conventional on all real ISAs, and is hence what the debugger
expects.

This PR makes the most straightforward fix: the debug tuple attached to
the call, and hence the metadata read out by the debug frame walker, now
encodes the PC of the next opcode. This is sufficient to fix `finish`
within LLDB.

An alternative I considered, and prototyped, is also worth mentioning:
one might see the argument for allowing a debugger to see the callsite
that invoked the next frame, and separately, see the return address
(i.e., both pieces of information are useful). In [an alternative
branch], there is a new table in the debug frame info metadata giving
the size of each callsite, so the debug frame-handle API can present a
`get-return-address` accessor on a `frame` resource alongside `get-pc`.
Ultimately I opted not to go with this because it has more overhead and
complexity and a *concrete* use-case wasn't forthcoming to me, but I'm
happy to reconsider if someone wants that instead.

[an alternative branch]: https://github.com/cfallin/wasmtime/tree/debugger-return-address-separate
@cfallin cfallin requested review from a team as code owners March 10, 2026 05:53
@cfallin cfallin requested review from alexcrichton and removed request for a team March 10, 2026 05:53
@cfallin
Copy link
Member Author

cfallin commented Mar 10, 2026

(To expand out why this was necessary in more detail, LLDB's "finish" / "step-out-of" command works by looking at the return address of the frame one level up, and setting a temporary breakpoint there, then continuing. It expects the callstack to report the return address rather than the call-instruction address for this to work.)

Copy link
Member

@alexcrichton alexcrichton left a comment

Choose a reason for hiding this comment

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

Oof I suspect that was quite the debugging journey to reach on this, but seems reasonable to me!

@cfallin cfallin enabled auto-merge March 10, 2026 17:36
@cfallin cfallin added this pull request to the merge queue Mar 10, 2026
Merged via the queue into bytecodealliance:main with commit 9593a3a Mar 10, 2026
45 checks passed
@cfallin cfallin deleted the one-may-try-to-return-but-it-is-never-to-the-place-one-started branch March 10, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants