debuggable
Make your Rust types readable in GDB and LLDB with one derive.
Debuggers show the representation of your types: pointers, capacities, bookkeeping fields,
and slots that aren't in use. debuggable lets you describe what a value means, once, next to
the type, and every user of your crate sees that in GDB, LLDB and VS Code.
(gdb) print buf before
$1 = my_crate::RawBuf {ptr: core::ptr::non_null::NonNull<u16> {pointer: 0x5555555aded0}, len: 3, cap: 4}
(gdb) print buf after
$1 = 3/4 = {len = 3, cap = 4, [0] = 10, [1] = 20, [2] = 30}
(lldb) v map before
(my_crate::SlotMap<u8, alloc::string::String>) map = {
slots = size=3 {
[0] = {...}
[1] = {...}
[2] = {...}
}
free_head = 3
len = 2
_k =
}
(lldb) v map after
(my_crate::SlotMap<u8, alloc::string::String>) map = 2 items {
[0] = v1 Some("alpha") {...}
[1] = v3 Some("beta") {...}
}
The third slot is free (len is 2), so debuggable doesn't show it. ({...} marks collapsed
values, as in VS Code's variables view.)
Make your crate debuggable in 5 minutes
1. Add the dependency
2. Describe your types
use Debuggable;
use PhantomData;
use NonNull;
Mistakes are compile errors that point at the problem:
error: summary refers to unknown field `lenght`; did you mean `length`?
--> src/lib.rs:2:24
|
2 | #[debuggable(summary = "{lenght} items")]
| ^^^^^^^^^^^^^^^^
3. Set up your debugger (once per machine; GDB once per project)
This configures whichever of GDB, LLDB and VS Code (CodeLLDB) you have. It only edits clearly
marked blocks in your config files, backs each file up first, and cargo debuggable setup --remove
undoes everything exactly.
4. Debug as usual: rust-gdb, rust-lldb, lldb, or F5 in VS Code with CodeLLDB.
5. If something doesn't show up
It checks your debuggers, your configuration and the binary, and tells you the exact fix. See also troubleshooting.
For library authors
Your users get the visualizers automatically: the descriptions are compiled into their binaries
along with your types. They only run cargo debuggable setup once, which they may have done
already for another crate.
What it costs them:
- Binary size: about 300 bytes per derived type, plus a 5 KB runtime once per Linux binary.
This is data in a section the debugger reads; no code runs in your program. To leave it out,
build with
RUSTFLAGS="--cfg debuggable_disable". - Compile time: about 1 ms per derived type on rebuilds without incremental compilation (0.5–1.0 ms measured; guarded in CI). The derive uses
syn3, whichserde,tokio,thiserrorandclapalready pull in, so most projects compile nothing extra. - Code: none. No
unsafeis required in your crate, and it works under#![forbid(unsafe_code)]. - MSRV: Rust 1.75.
Attributes
| Attribute | On | Effect |
|---|---|---|
summary = "..." |
struct, enum variant | One-line summary. {field} (or {0} for tuple fields) inserts a field; {{ and }} are literal braces. |
hide |
field | Hide the field. PhantomData fields are hidden automatically. |
rename = "..." |
field | Show the field under another name. |
items |
field | Show the field's elements in place of the field: a Vec<T>, an array [T; N], or a *const T, *mut T or NonNull<T> together with len. MaybeUninit<T> elements are shown as T. |
text |
field | Show the field's bytes as a string, "hello": a [u8; N], Vec<u8> or byte pointer, with len as for items. Invalid UTF-8 shows as \xNN. |
len = "field" |
with items or text |
The number of elements to show: for a Vec or array, at most this many; for a pointer, required. |
Full reference: docs.rs/debuggable.
Support
| GDB | LLDB | VS Code (CodeLLDB) | |
|---|---|---|---|
| Linux (x86-64) | 15 | 18, 20, 22 | yes |
| macOS | n/a | Apple LLDB | expected (not yet tested) |
| Windows (MSVC) | not yet: nothing is emitted, so your crate builds unchanged |
Every GDB and LLDB version above is tested in CI on every commit, in debug, release, thin-LTO and fat-LTO builds; VS Code was tested by hand on Linux. Details and known issues: compatibility.
Limitations
- Types defined inside function bodies aren't matched by the debugger (
cargo debuggable doctorpoints them out). - Summaries refer to fields by name; there is no expression language or format specs (yet).
- Windows/Natvis isn't supported yet.
- In plain
gdb, standard library types (String,Vec, ...) inside your summaries show raw; userust-gdb, which loads rustc's printers for them.
How it works
The derive embeds a small JSON description of each type in the binary. On Linux, a section
GDB auto-loads also holds a 5 KB runtime that renders those descriptions, so GDB needs no setup
beyond trusting your build directory. LLDB can't auto-load, so cargo debuggable setup installs
a loader that reads the same descriptions. Details: how it works.
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.