Debugging: PC in a frame at a callsite should be the return address, not the call.#12750
Merged
cfallin merged 3 commits intobytecodealliance:mainfrom Mar 10, 2026
Conversation
…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
Member
Author
|
(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.) |
alexcrichton
approved these changes
Mar 10, 2026
Member
alexcrichton
left a comment
There was a problem hiding this comment.
Oof I suspect that was quite the debugging journey to reach on this, but seems reasonable to me!
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.
In working out why a
finishcommand inLLDB-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
finishwithin 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-addressaccessor on aframeresource alongsideget-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.