This is the current end-to-end workflow. Earlier notes in
guide.mdandasync_backtrace.mdare archived and no longer reflect the toolchain.
- Rust toolchain with
cargo(the bundledtests/tokio_test_projectcompiles in debug mode). - A
.cargo/config.tomlin your workspace matching the sample attests/tokio_test_project/.cargo/config.toml. Copy or adapt the[build]and[target.*]settings there socargoemits the expected LLVM bitcode and DWARF info for analysis. - LLVM tools that match your
rustcbuild. Runrustc --version --verboseand copy the reportedLLVM version. Install that version ofllvm/llvm-tools(e.g., on Ubuntusudo apt-get install llvm-20 llvm-20-tools) so theoptbinary can generate call graphs from the emitted IR. - Python 3.10+ virtualenv with the dependencies in
src/requirements.txt(runmake venvonce from the repo root). - GDB with Python support (Ubuntu
sudo apt install gdbis sufficient). - From the repo root, activate the virtualenv before launching the workflow:
source venv/bin/activate.
Run the integrated recipe from the project root:
make test-tokio_test_project
This target does the following:
- Builds
tests/tokio_test_project(debug profile) so that the binary attests/tokio_test_project/target/debug/tokio_test_projectis up to date. - Regenerates
results/tokio_test_project.callgraph.dotandresults/async_deps.json. - Starts
gdb-multiarchwithsrc/main.pypreloaded and runs the freshly built binary.
Leave the GDB session open for the remaining steps. If you only need to rerun GDB without rebuilding, the fallback target make test-tokio_test_project-no-recompile skips the compilation and artifact refresh.
Inside the GDB prompt, locate all futures that implement poll:
(gdb) find-poll-fn
The command stores a map at results/poll_map.json. Open this file in your editor and flip the "async_backtrace" flag to true for every poll function you want to instrument (typically the leaf futures you care about). Leave the flag false for noisy or irrelevant entries. Save the file before continuing.
Tip: If you rerun
find-poll-fn, it overwrites the JSON. Keep a copy (e.g.,results/poll_map.custom.json) if you maintain curated selections across sessions.
Still inside GDB, initialize the DWARF tree so the debugger can resolve future hierarchies:
(gdb) init-dwarf-analysis tests/tokio_test_project/target/debug/tokio_test_project
Replace the path if you are debugging a different binary. A successful run reports how many compilation units were indexed and exposes them as gdb.dwarf_tree and gdb.dwarf_info for ad-hoc inspection.
Kick off the tracing pipeline so the selected futures are instrumented:
(gdb) start-async-analysis
In the current repository the command name is
start-async-debug. If your setup exposesstart-async-analysisas an alias, either form is equivalent.
After the command confirms breakpoints, run or continue the program (run/continue). While it executes you can:
- Inspect live async stacks with
inspect-async. - Dump the collected event log with
dump-async-data(writes toresults/async_backtrace.json). - Exit the session with
quitwhen finished.
- All generated artifacts (call graph, async dependencies, poll map, traces) are kept under
results/so they survive across runs. - The instrumentation depth is controlled by
ENABLE_SYNC_DESCENDANTS,ENABLE_ASYNC_DESCENDANTS, andSYNC_DESCENDANT_DEPTHinsrc/core/config.py. - If you update the Rust sources, rerun
make test-tokio_test_projectto refresh the LLVM bitcode and regenerated files before returning to GDB.