Debugging with LLDB
When compiled with debug symbols (debug = true, the default in [profile.dev] and [profile.test]), rustc embeds DWARF (on Linux and macOS) or PDB (on Windows) debug metadata directly into the binary. The Rust toolchain ships with rust-lldb and rust-gdb wrapper scripts that load custom Python pretty-printers so standard types (String, Vec<T>, HashMap<K, V>, Option<T>, slices) display their human-readable contents instead of raw pointer addresses.
Source Code (`src/main.rs`)
|
v `cargo build` (generates DWARF debug info in `target/debug/my_app`)
[ Launch `rust-lldb target/debug/my_app` ]
|
+-- Set breakpoints (`b main`, `b src/main.rs:14`, `b rust_panic`)
+-- Step through lines (`n` next, `s` step into, `finish` step out)
+-- Inspect locals & expressions (`frame variable`, `p my_vec`)
+-- Inspect call stack (`bt` backtrace, `up`, `down`)
Essential rust-lldb Commands Reference
| Action | rust-lldb Command |
Short Alias | Description |
|---|---|---|---|
| Launch with args | rust-lldb -- target/debug/app arg1 |
— | Opens binary in LLDB with command-line arguments. |
| Start execution | process launch |
run / r |
Starts the program inside the debugger. |
| Break on function | breakpoint set --name compute |
b compute |
Pauses execution when entering compute. |
| Break on file:line | breakpoint set --file main.rs --line 12 |
b main.rs:12 |
Pauses execution at line 12 of main.rs. |
| Break on any panic | breakpoint set --name rust_panic |
b rust_panic |
Pauses at the exact moment a Rust panic begins unwinding! |
| Step over line | thread step-over |
next / n |
Executes the current line without stepping into function calls. |
| Step into call | thread step-in |
step / s |
Steps inside the function called on the current line. |
| Step out of fn | thread step-out |
finish |
Runs until the current stack frame returns. |
| Continue running | process continue |
continue / c |
Resumes execution until the next breakpoint or exit. |
| Inspect locals | frame variable |
v |
Prints all local variables in the current stack frame using Rust pretty-printers. |
| Evaluate expression | expression scores[0] |
p scores |
Evaluates an expression in the current frame context. |
| Stack backtrace | thread backtrace |
bt |
Prints the call stack of the current thread (bt all for all threads). |
Example Debugging Session on a Rust Binary
Consider a program that computes a moving average:
#[derive(Debug)]
struct WindowStats {
count: usize,
sum: i64,
average: f64,
}
fn analyze_window(samples: &[i64]) -> WindowStats {
let count = samples.len();
let sum: i64 = samples.iter().sum();
let average = if count > 0 {
(sum as f64) / (count as f64)
} else {
0.0
};
WindowStats {
count,
sum,
average,
}
}
fn main() {
let readings = vec![12, 18, 24, 30];
let stats = analyze_window(&readings);
println!("Stats: count={}, sum={}, avg={:.1}", stats.count, stats.sum, stats.average);
}
Stats: count=4, sum=84, avg=21.0
Running rust-lldb target/debug/app and setting a breakpoint on analyze_window displays formatted Rust slices and structs via frame variable (v):
(lldb) b analyze_window
Breakpoint 1: where = app`app::analyze_window + 24 at main.rs:9:17
(lldb) run
Process 41280 stopped
* thread #1, name = 'main', stop reason = breakpoint 1.1
frame #0: 0x0000000100004120 app`app::analyze_window(samples=&[i64] @ 0x000000016fdff140) at main.rs:9:17
6 }
7
8 fn analyze_window(samples: &[i64]) -> WindowStats {
-> 9 let count = samples.len();
10 let sum: i64 = samples.iter().sum();
(lldb) v samples
(&[i64]) samples = size=4 {
[0] = 12
[1] = 18
[2] = 24
[3] = 30
}
Catching Panics Live in the Debugger (b rust_panic)
Whenever a Rust thread panics, the standard library invokes an internal hook function named rust_panic before unwinding the stack. Setting b rust_panic in rust-lldb pauses execution while every local variable in the panicking stack frame is still alive in memory, allowing you to run bt, up, and v to inspect the exact values that caused the panic.