| name | rust-debugging |
| description | Rust debugging skill for systems programming. Use when debugging Rust binaries with GDB or LLDB, enabling Rust pretty-printers, interpreting panics and backtraces, debugging async/await with tokio-console, stepping through no_std code, or using dbg! and tracing macros effectively. Activates on queries about rust-gdb, rust-lldb, RUST_BACKTRACE, Rust panics, debugging async Rust, tokio-console, or pretty-printers. |
Rust Debugging
Purpose
Guide agents through debugging Rust programs: GDB/LLDB with Rust pretty-printers, backtrace configuration, panic triage, async debugging with tokio-console, and #[no_std] debugging strategies.
Triggers
- "How do I use GDB/LLDB to debug a Rust binary?"
- "How do I get a full backtrace from a Rust panic?"
- "How do I debug async Rust / Tokio?"
- "Rust pretty-printers aren't working in GDB"
- "How do I debug a Rust panic in production?"
- "How do I use dbg! and tracing in Rust?"
Workflow
1. Build for debugging
cargo build
cargo build --release --profile release-with-debug
cargo run
cargo run -- arg1 arg2
2. GDB with Rust pretty-printers
rust-gdb target/debug/myapp
Common GDB session for Rust:
# Basic
(gdb) break main
(gdb) run arg1 arg2
(gdb) next # step over
(gdb) step # step into
(gdb) continue
# Rust-aware inspection
(gdb) print my_string # Shows String content via pretty-printer
(gdb) print my_vec # Shows Vec elements
(gdb) print my_option # Shows Some(value) or None
(gdb) info locals
# Break on panic
(gdb) break rust_panic
(gdb) break core::panicking::panic
# Backtrace
(gdb) bt # Short backtrace
(gdb) bt full # Full with locals
3. LLDB with Rust pretty-printers
rust-lldb target/debug/myapp
lldb target/debug/myapp
(lldb) command script import /path/to/rust/lib/rustlib/etc/lldb_lookup.py
(lldb) command source /path/to/rust/lib/rustlib/etc/lldb_commands
Common LLDB session:
(lldb) b main::main
(lldb) r arg1 arg2
(lldb) n # next (step over)
(lldb) s # step into
(lldb) c # continue
(lldb) frame variable # show locals
(lldb) p my_string # print variable with pretty-printer
(lldb) bt # backtrace
(lldb) bt all # all threads
4. Backtrace configuration
RUST_BACKTRACE=1 ./myapp
RUST_BACKTRACE=full ./myapp
RUST_BACKTRACE=full ./target/debug/myapp
use std::backtrace::Backtrace;
let bt = Backtrace::capture();
eprintln!("{bt}");
For release binaries, keep debug symbols in a separate file:
cargo build --release
objcopy --only-keep-debug target/release/myapp target/release/myapp.debug
strip --strip-debug target/release/myapp
objcopy --add-gnu-debuglink=target/release/myapp.debug target/release/myapp
5. Panic triage
use std::panic;
panic::set_hook(Box::new(|info| {
let backtrace = std::backtrace::Backtrace::force_capture();
eprintln!("PANIC: {info}");
eprintln!("{backtrace}");
}));
Common panic patterns:
| Panic message | Likely cause |
|---|
index out of bounds: the len is N but the index is M | Array/vec OOB access |
called Option::unwrap() on a None value | Unwrap on None |
called Result::unwrap() on an Err value | Unwrap on error |
attempt to subtract with overflow | Integer underflow (debug build) |
assertion failed | Failed assert! or assert_eq! |
stack overflow | Infinite recursion |
Use panic = "abort" in release to get a crash dump instead of unwind.
6. The dbg! macro
let result = dbg!(some_computation(x));
let (a, b) = dbg!((compute_a(), compute_b()));
let sum: i32 = (0..10)
.filter(|x| dbg!(x % 2 == 0))
.map(|x| dbg!(x * x))
.sum();
7. Structured logging with tracing
[dependencies]
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
use tracing::{debug, error, info, instrument, warn};
#[instrument]
fn process(id: u64, data: &str) -> Result<(), Error> {
debug!("Processing item");
info!(item_id = id, "Started processing");
if data.is_empty() {
warn!(item_id = id, "Empty data");
return Err(Error::EmptyData);
}
error!(item_id = id, err = ?some_result, "Failed");
Ok(())
}
tracing_subscriber::fmt()
.with_env_filter("myapp=debug,warn")
.init();
RUST_LOG=debug ./myapp
RUST_LOG=myapp::module=trace,warn ./myapp
8. Async debugging with tokio-console
[dependencies]
console-subscriber = "0.3"
tokio = { version = "1", features = ["full", "tracing"] }
console_subscriber::init();
cargo install tokio-console
tokio-console
tokio-console shows: task states, waker activity, blocked tasks, poll durations.
For GDB/LLDB command reference and pretty-printer setup, see references/rust-gdb-pretty-printers.md.
Related skills
- Use
skills/rust/rustc-basics for debug info flags and build configuration
- Use
skills/debuggers/gdb for GDB fundamentals
- Use
skills/debuggers/lldb for LLDB fundamentals
- Use
skills/rust/rust-sanitizers-miri for memory safety and undefined behaviour