-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
External backtraces for async tasks #73524
Comments
Emit line info for generator variants Debuggers should be able to read a generator / async fn state machine and show the line it's suspended at. Eventually, this could grow into an "async stack trace" feature of sorts. While no debugger support this for Rust today, this PR adds the debuginfo necessary for that support to exist. [This gist](https://gist.github.com/tmandry/6d7004fa008684f76809208847459f9b) shows the resulting debuginfo for a simple example. Here's a snippet: ``` 0x00000986: DW_TAG_variant DW_AT_discr_value (0x03) 0x00000988: DW_TAG_member DW_AT_name ("3") DW_AT_type (0x000009bc "Suspend0") DW_AT_decl_file ("/home/tmandry/code/playground/generator-simple.rs") DW_AT_decl_line (6) DW_AT_alignment (8) DW_AT_data_member_location (0x00) ``` The file and line have been added here. The line currently points to the beginning of the statement containing the yield (or await), because that's what the MIR source info points to for the yield terminator. (We may want to point to the yield or await line specifically, but that can be done independently of this change.) Debuggers don't know how to use this kind of info yet. However, we're hoping to experiment with adding such support to Fuchsia's debugger. It would be exciting if someone were interested in adding similar to support to gdb/lldb. r? @oli-obk cc @eddyb @jonas-schievink Part of rust-lang#73524.
Emit line info for generator variants Debuggers should be able to read a generator / async fn state machine and show the line it's suspended at. Eventually, this could grow into an "async stack trace" feature of sorts. While no debugger support this for Rust today, this PR adds the debuginfo necessary for that support to exist. [This gist](https://gist.github.com/tmandry/6d7004fa008684f76809208847459f9b) shows the resulting debuginfo for a simple example. Here's a snippet: ``` 0x00000986: DW_TAG_variant DW_AT_discr_value (0x03) 0x00000988: DW_TAG_member DW_AT_name ("3") DW_AT_type (0x000009bc "Suspend0") DW_AT_decl_file ("/home/tmandry/code/playground/generator-simple.rs") DW_AT_decl_line (6) DW_AT_alignment (8) DW_AT_data_member_location (0x00) ``` The file and line have been added here. The line currently points to the beginning of the statement containing the yield (or await), because that's what the MIR source info points to for the yield terminator. (We may want to point to the yield or await line specifically, but that can be done independently of this change.) Debuggers don't know how to use this kind of info yet. However, we're hoping to experiment with adding such support to Fuchsia's debugger. It would be exciting if someone were interested in adding similar to support to gdb/lldb. r? @oli-obk cc @eddyb @jonas-schievink Part of rust-lang#73524.
Emit line info for generator variants Debuggers should be able to read a generator / async fn state machine and show the line it's suspended at. Eventually, this could grow into an "async stack trace" feature of sorts. While no debugger support this for Rust today, this PR adds the debuginfo necessary for that support to exist. [This gist](https://gist.github.com/tmandry/6d7004fa008684f76809208847459f9b) shows the resulting debuginfo for a simple example. Here's a snippet: ``` 0x00000986: DW_TAG_variant DW_AT_discr_value (0x03) 0x00000988: DW_TAG_member DW_AT_name ("3") DW_AT_type (0x000009bc "Suspend0") DW_AT_decl_file ("/home/tmandry/code/playground/generator-simple.rs") DW_AT_decl_line (6) DW_AT_alignment (8) DW_AT_data_member_location (0x00) ``` The file and line have been added here. The line currently points to the beginning of the statement containing the yield (or await), because that's what the MIR source info points to for the yield terminator. (We may want to point to the yield or await line specifically, but that can be done independently of this change.) Debuggers don't know how to use this kind of info yet. However, we're hoping to experiment with adding such support to Fuchsia's debugger. It would be exciting if someone were interested in adding similar to support to gdb/lldb. r? @oli-obk cc @eddyb @jonas-schievink Part of rust-lang#73524.
Another missing piece: Debuggers need the ability to identify the local variable currently being awaited. This could be as simple as a special symbol name (something that can't be spelled in normal Rust, like |
My understanding from this thread is that if using Apple's libdispatch as an executor, it's possible to encode the dispatch stack trace as part of the executor. This could be useful for macOS/iOS, albeit a bit specific. Though probably not quite as good or general-purpose a DWARF approach would be. On Windows I've found that MSVC provides a built-in way to debug multi-threaded code (ref). If we can find out which symbols they're using to instrument their libraries, perhaps we can employ the same mechanism for our builds too. I would imagine this one to be a bit more similar to the DWARF proposal. |
It would be nice if debuggers supported printing a backtrace of await points within an async stack machine. In particular, if I have a reference to an async task, my debugger should be able to read the state of all (nested) generators compiled into that object, and turn that into something that looks like a stack trace (except, of course, there is no actual stack).
See #73522 for a more general discussion of debugger support for async/await.
Prior art
Javascript has async stack traces, but AIUI, it is really solving a different problem. In Javascript, awaiting something is the equivalent of spawning an async task in Rust and then awaiting completion of that task: if you set and hit breakpoint inside an async function, you won't have any context on what's calling that function from a "normal" stack backtrace.
This is not the case for Rust: the stack trace from inside a poll function shows you all the futures that polled you inside your task. (However, such a technique would be useful for seeing across task boundaries.)
The problem we are really trying to solve in this issue is an external backtrace: I should be able to peer into the state of an async task that isn't running and inspect the "stack" of Futures in it (both async fns and hand-rolled futures, ideally).
Trees, not stacks
A complication with the backtrace analogy is that in general, we are dealing with trees of futures (think
select!()
), not stacks. This means that in addition to making sure the debugger has all the information it needs, we'll need to experiment with different ways of presenting that information in the various debugging environments. I hope some prior art will be informative here.Implementation history
The text was updated successfully, but these errors were encountered: