Skip to main content

debuggable/
lib.rs

1//! Make your Rust types readable in GDB and LLDB with one derive.
2//!
3//! ```
4//! use debuggable::Debuggable;
5//!
6//! #[derive(Debuggable)]
7//! #[debuggable(summary = "{len}/{cap}")]
8//! pub struct Buffer {
9//!     #[debuggable(items, len = "len")]
10//!     data: Vec<u32>,
11//!     len: usize,
12//!     cap: usize,
13//! }
14//! ```
15//!
16//! In a debugger, a `Buffer` then reads `3/8 = {len = 3, cap = 8, [0] = 1, [1] = 2, [2] = 3}`
17//! instead of its raw representation.
18//!
19//! To see it, set up your debugger once with
20//! [`cargo-debuggable`](https://crates.io/crates/cargo-debuggable):
21//! `cargo install cargo-debuggable && cargo debuggable setup`. Nothing runs inside your
22//! program: the derive only embeds a description of the type for the debugger to read.
23//!
24//! # Attributes
25//!
26//! All attributes are optional. A bare `#[derive(Debuggable)]` hides `PhantomData` fields
27//! and, for enums, shows just the variant name when the variant has no fields.
28//!
29//! ## `summary = "..."`: one-line summary
30//!
31//! On a struct, or on an enum variant. `{name}` inserts a field, `{0}` a tuple field, `{#}`
32//! the number of elements (on types with `items`), and `{{` and `}}` are literal braces.
33//! Summaries may use hidden fields. Each inserted field
34//! shows the debugger's own one-line rendering of it, so a field that is itself
35//! `Debuggable` shows its summary.
36//!
37//! ```
38//! # use debuggable::Debuggable;
39//! #[derive(Debuggable)]
40//! #[debuggable(summary = "({x}, {y})")]
41//! pub struct Point { pub x: i32, pub y: i32 }
42//!
43//! #[derive(Debuggable)]
44//! #[debuggable(summary = "{0} m")]
45//! pub struct Meters(pub f64);
46//!
47//! #[derive(Debuggable)]
48//! pub enum Shape {
49//!     #[debuggable(summary = "circle r={radius} at {center}")]
50//!     Circle { center: Point, radius: Meters },
51//!     Empty,
52//! }
53//! ```
54//!
55//! A circle then shows as `circle r=2.5 m at (1, 2)`. On an enum, put `summary` on each
56//! variant; variants without one show their name.
57//!
58//! ## `hide`: leave a field out
59//!
60//! ```
61//! # use debuggable::Debuggable;
62//! #[derive(Debuggable)]
63//! pub struct Cache {
64//!     pub hits: u64,
65//!     #[debuggable(hide)]
66//!     generation: u32,
67//!     _marker: std::marker::PhantomData<*const ()>, // hidden automatically
68//! }
69//! ```
70//!
71//! ## `rename = "..."`: show a field under another name
72//!
73//! ```
74//! # use debuggable::Debuggable;
75//! #[derive(Debuggable)]
76//! pub struct Account {
77//!     #[debuggable(rename = "balance_pence")]
78//!     balance: i64,
79//! }
80//! ```
81//!
82//! ## `items` and `len = "field"`: show a collection's elements
83//!
84//! On one field per struct. The field's elements are shown as `[0]`, `[1]`, ... after the
85//! other fields, in place of the field itself.
86//!
87//! - On a `Vec<T>`: shows its elements. With `len`, at most that many.
88//! - On an array `[T; N]` stored in the struct (an inline buffer): shows its elements. With
89//!   `len`, at most that many.
90//! - On a `*const T`, `*mut T` or `NonNull<T>`: `len` is required and gives the count.
91//!
92//! Elements of type `MaybeUninit<T>` or `ManuallyDrop<T>` are shown as `T`. Only the first
93//! `len` of them are read, so give `len` whenever some slots may be uninitialized.
94//!
95//! ```
96//! # use debuggable::Debuggable;
97//! # use std::ptr::NonNull;
98//! #[derive(Debuggable)]
99//! #[debuggable(summary = "{len} items")]
100//! pub struct Stack<T> {
101//!     #[debuggable(items, len = "len")]
102//!     slots: Vec<Option<T>>,   // only the first `len` slots are in use
103//!     #[debuggable(hide)]
104//!     len: usize,
105//! }
106//!
107//! #[derive(Debuggable)]
108//! pub struct RawParts {
109//!     #[debuggable(items, len = "len")]
110//!     ptr: NonNull<u16>,
111//!     len: usize,
112//! }
113//!
114//! #[derive(Debuggable)]
115//! pub struct Inline<T, const N: usize> {
116//!     #[debuggable(items, len = "len")]
117//!     buf: [std::mem::MaybeUninit<T>; N],
118//!     len: usize,
119//! }
120//! ```
121//!
122//! At most 10,000 elements are shown.
123//!
124//! ## `only` and `value`: slot collections
125//!
126//! Options for `items` on collections whose slots may be vacant, such as slab, arena or slot
127//! map types. `only` keeps the elements that match, and `value` shows a field of each kept
128//! element instead of the whole slot. Kept elements keep their index as their label, so
129//! `[3]` is still slot 3.
130//!
131//! - `only = "Variant"`: elements that are enums, in that variant.
132//! - `only = "field"` or `only = "field & mask"`: elements whose integer or `bool` field is
133//!   non-zero, or has a bit of `mask` set. The path may go through fields: `"meta.flags & 0x4"`.
134//! - `value = "path"`: a field of the element, or of the variant chosen by `only`; through
135//!   unions too. `MaybeUninit` and `ManuallyDrop` are unwrapped.
136//!
137//! ```
138//! # use debuggable::Debuggable;
139//! # use std::mem::ManuallyDrop;
140//! enum Entry<T> { Vacant(usize), Occupied(T) }
141//!
142//! #[derive(Debuggable)]
143//! #[debuggable(summary = "{len} items")]
144//! pub struct Slab<T> {
145//!     #[debuggable(items, only = "Occupied", value = "0")]
146//!     entries: Vec<Entry<T>>,
147//!     #[debuggable(hide)]
148//!     len: usize,
149//! }
150//!
151//! union SlotUnion<T> { value: ManuallyDrop<T>, next_free: u32 }
152//! struct Slot<T> { u: SlotUnion<T>, version: u32 } // odd version = occupied
153//!
154//! #[derive(Debuggable)]
155//! #[debuggable(summary = "{num_elems} items")]
156//! pub struct SlotMap<V> {
157//!     #[debuggable(items, only = "version & 1", value = "u.value")]
158//!     slots: Vec<Slot<V>>,
159//!     #[debuggable(hide)]
160//!     num_elems: u32,
161//! }
162//! ```
163//!
164//! A slab holding "a" in slot 1 and "d" in slot 3 then shows as
165//! `2 items = {[1] = "a", [3] = "d"}`. Names inside the element type can't be checked at
166//! compile time; a misspelled variant shows `<only: no variant ...>` in the debugger.
167//!
168//! ## `items = "path"` on the type: inline-or-heap collections
169//!
170//! For types whose elements live in one of several places, such as small-vector types that
171//! move from an inline buffer to the heap. On the struct or enum, each
172//! `#[debuggable(items = "path", len = "path")]` names one place the elements can be. The
173//! first one whose paths exist in the current value is shown. A path segment can name an enum
174//! variant, and then only leads somewhere while that variant is active. Paths start at the
175//! type: on an enum, at a variant.
176//!
177//! ```
178//! # use debuggable::Debuggable;
179//! # use std::{marker::PhantomData, mem::MaybeUninit, ptr::NonNull};
180//! # pub trait Array { type Item; }
181//! # impl<T, const N: usize> Array for [T; N] { type Item = T; }
182//! enum SmallVecData<A: Array> {
183//!     Inline(MaybeUninit<A>),
184//!     Heap { ptr: NonNull<A::Item>, len: usize },
185//! }
186//!
187//! #[derive(Debuggable)]
188//! #[debuggable(summary = "{#} items")]
189//! #[debuggable(items = "data.Inline.0", len = "capacity")]  // inline: `capacity` is the length
190//! #[debuggable(items = "data.Heap.ptr", len = "data.Heap.len")]
191//! pub struct SmallVec<A: Array> {
192//!     #[debuggable(hide)]
193//!     capacity: usize,
194//!     #[debuggable(hide)]
195//!     data: SmallVecData<A>,
196//!     _marker: PhantomData<A::Item>,
197//! }
198//!
199//! #[derive(Debuggable)]
200//! #[debuggable(items = "Inline.0", len = "Inline.1")]
201//! #[debuggable(items = "Heap.0")]
202//! pub enum TinyVec<T> {
203//!     #[debuggable(summary = "{#} items (inline)")]
204//!     Inline(#[debuggable(hide)] [T; 4], #[debuggable(hide)] u16),
205//!     #[debuggable(summary = "{#} items")]
206//!     Heap(#[debuggable(hide)] Vec<T>),
207//! }
208//! ```
209//!
210//! Either way, three elements show as `3 items = {[0] = 1, [1] = 2, [2] = 3}`. The first
211//! segment of each path is checked at compile time; deeper ones can't be, and if no place
212//! matches, the debugger shows `<items: no alternative matched>`.
213//!
214//! ## `text`: show bytes as a string
215//!
216//! On any number of fields. The field's bytes are shown as a string literal, as UTF-8:
217//! `"hello"`. Escapes follow Rust's `{:?}` for strings (`\n`, `\"`, `\0`), and bytes that
218//! aren't valid UTF-8 show as `\xNN`, so nothing is lost. Takes the same sources as `items`,
219//! with elements of one byte: `[u8; N]`, `[MaybeUninit<u8>; N]`, `Vec<u8>`, or a pointer with
220//! `len`. With `hide`, the field gets no row of its own but a summary can still show it.
221//!
222//! ```
223//! # use debuggable::Debuggable;
224//! # use std::mem::MaybeUninit;
225//! #[derive(Debuggable)]
226//! #[debuggable(summary = "{buf}")]
227//! pub struct InlineString<const N: usize> {
228//!     #[debuggable(text, len = "len", hide)]
229//!     buf: [MaybeUninit<u8>; N],
230//!     #[debuggable(hide)]
231//!     len: u8,
232//! }
233//! ```
234//!
235//! An `InlineString` holding "hello" then shows as just `"hello"`, like a `String`. At most
236//! 1,024 bytes are read; LLDB leaves out trailing NUL bytes.
237//!
238//! # Compile errors
239//!
240//! Every mistake is a compile error pointing at the attribute, with suggestions for typos:
241//!
242//! ```compile_fail
243//! # use debuggable::Debuggable;
244//! #[derive(Debuggable)]
245//! #[debuggable(summary = "{lenght} items")] // did you mean `length`?
246//! pub struct Stack { length: usize }
247//! ```
248//!
249//! # Generics, enums, and dependencies
250//!
251//! Generic types work for every instantiation. Enums work with any layout, including
252//! niche-optimised ones (for example, an enum whose data variant holds a `String`). Types from
253//! dependency crates work in the final binary like your own.
254//!
255//! # Cost, and opting out
256//!
257//! About 300 bytes per derived type in the final binary, plus a 5 KB GDB runtime once per
258//! Linux binary: data in a section that only debuggers read. On Windows nothing is emitted
259//! (not supported yet). Build with `RUSTFLAGS="--cfg debuggable_disable"` to emit nothing
260//! anywhere.
261//!
262//! # Limitations
263//!
264//! - Types defined inside function bodies are not matched by the debugger
265//!   (`cargo debuggable doctor <binary>` points them out).
266//! - Summaries refer to fields by name only: no expressions or format specs.
267//! - Windows (Natvis) is not supported yet.
268//!
269//! See the [repository](https://github.com/tommantonclery/debuggable) for the compatibility table and
270//! troubleshooting.
271#![no_std]
272
273/// Derives debugger visualizers for a struct or an enum. See the [crate docs](crate).
274pub use debuggable_derive::Debuggable;
275
276#[doc(hidden)]
277pub mod __private;