Skip to content

Commit

Permalink
Updates and copy-edits for debugger_visualizer.
Browse files Browse the repository at this point in the history
  • Loading branch information
ehuss committed May 6, 2023
1 parent 4dee364 commit 39a0be6
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 74 deletions.
2 changes: 1 addition & 1 deletion src/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ The following is an index of all built-in attributes.
- [`non_exhaustive`] — Indicate that a type will have more fields/variants
added in future.
- Debugger
- [`debugger_visualizer`] — Embeds a file that specifies debugger output for a type
- [`debugger_visualizer`] — Embeds a file that specifies debugger output for a type.

[Doc comments]: comments.md#doc-comments
[ECMA-334]: https://www.ecma-international.org/publications/standards/Ecma-334.htm
Expand Down
113 changes: 40 additions & 73 deletions src/attributes/debugger.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,36 @@
# Debugger attributes

The following [attributes] are used for enhancing the debugging experience when using third-party debuggers like GDB or LLDB.
The following [attributes] are used for enhancing the debugging experience when using third-party debuggers like GDB or WinDbg.

## The `debugger_visualizer` attribute

The `debugger_visualizer` attribute can be used to embed a debugger visualizer file into the debug information generated by `rustc`.
This enables an improved debugger experience for types outside of Rust's standard library.
The *`debugger_visualizer` attribute* can be used to embed a debugger visualizer file into the debug information.
This enables an improved debugger experience for displaying values in the debugger.
It uses the [_MetaListNameValueStr_] syntax to specify its inputs, and must be specified as a crate attribute.

### Using `debugger_visualizer` with Natvis

Natvis is an XML-based framework for Microsoft debuggers (such as Visual Studio and WinDbg) that uses declarative rules to customize the display of types.
A Natvis file is embedded using the `natvis-file` meta item.
For detailed information on the Natvis format, refer to Microsoft's [Natvis documentation].

<div class="warning">
Currently, this attribute only supports embedding Natvis files on `-windows-msvc` targets.
</div>
This attribute only supports embedding Natvis files on `-windows-msvc` targets.

Consider a crate with this directory structure:

```text
/Cargo.toml
/Rectangle.natvis
+-- src
+-- main.rs
```

Where `main.rs` contains:
The path to the Natvis file is specified with the `natvis_file` key, which is a path relative to the crate source file:

<!-- ignore: requires external files, and msvc -->
```rust ignore
#![debugger_visualizer(natvis_file = "../Rectangle.natvis")]
mod fancy_rect {
pub struct FancyRect {
pub x: f32,
pub y: f32,
pub dx: f32,
pub dy: f32,
}

impl FancyRect {
pub fn new(x: f32, y: f32, dx: f32, dy: f32) -> Self {
FancyRect { x, y, dx, dy }
}
}
}
#![debugger_visualizer(natvis_file = "Rectangle.natvis")]

use fancy_rect::FancyRect;
struct FancyRect {
x: f32,
y: f32,
dx: f32,
dy: f32,
}

fn main() {
let fancy_rect = FancyRect::new(10.0, 10.0, 5.0, 5.0);
()
let fancy_rect = FancyRect { x: 10.0, y: 10.0, dx: 5.0, dy: 5.0 };
println!("set breakpoint here");
}
```

Expand All @@ -58,7 +39,7 @@ and `Rectangle.natvis` contains:
```xml
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="Rectangle::FancyRect">
<Type Name="foo::FancyRect">
<DisplayString>({x},{y}) + ({dx}, {dy})</DisplayString>
<Expand>
<Synthetic Name="LowerLeft">
Expand All @@ -82,55 +63,38 @@ When viewed under WinDbg, the `fancy_rect` variable would be shown as follows:

```text
> Variables:
> fancy_rect: (10, 10) + (5, 5)
> LowerLeft: (10, 10)
> UpperLeft: (10, 15)
> UpperRight: (15, 15)
> LowerRight: (15, 10)
> fancy_rect: (10.0, 10.0) + (5.0, 5.0)
> LowerLeft: (10.0, 10.0)
> UpperLeft: (10.0, 15.0)
> UpperRight: (15.0, 15.0)
> LowerRight: (15.0, 10.0)
```

### Using `debugger_visualizer` with GDB

GDB supports the use of a structured Python script, called a *pretty printer*, that describes how a type should be visualized in the debugger view.
These scripts are embedded using the `gdb_script_file` meta item.
For detailed information on pretty printers, refer to GDB's [pretty print documentation].
For detailed information on pretty printers, refer to GDB's [pretty printing documentation].

Embedded pretty printers are not automatically loaded when debugging a binary under GDB.
There are two ways to enable auto-loading embedded pretty printers:
1. Launch GDB with extra arguments to explicitly add a directory or binary to the auto-load safe path: `gdb -iex "set auto-load safe-path path/to/binary" path/to/binary` (For more information, see GDB's [auto-loading documentation])
1. Launch GDB with extra arguments to explicitly add a directory or binary to the auto-load safe path: `gdb -iex "add-auto-load-safe-path safe-path path/to/binary" path/to/binary`
For more information, see GDB's [auto-loading documentation].
1. Create a file named `gdbinit` under `$HOME/.config/gdb` (you may need to create the directory if it doesn't already exist). Add the following line to that file: `add-auto-load-safe-path path/to/binary`.

Consider a crate called `PersonPrinter` with this directory structure:

```text
/Cargo.toml
/printer.py
+-- src
+-- main.rs
```

Where `main.rs` contains:
These scripts are embedded using the `gdb_script_file` key, which is a path relative to the crate source file.

<!-- ignore: requires external files -->
```rust ignore
#![debugger_visualizer(gdb_script_file = "../printer.py")]
mod person {
pub struct Person {
pub name: String,
pub age: i32,
}

impl Person {
pub fn new(name: String, age: i32) -> Self {
Person { name, age }
}
}
}
#![debugger_visualizer(gdb_script_file = "printer.py")]

use person::Person;
struct Person {
name: String,
age: i32,
}

fn main() {
let bob = Person::new(String::from("Bob"), 10);
()
let bob = Person { name: String::from("Bob"), age: 10 };
println!("set breakpoint here");
}
```

Expand All @@ -154,21 +118,24 @@ def lookup(val):
lookup_tag = val.type.tag
if lookup_tag is None:
return None
if "PersonPrinter::person::Person" == lookup_tag:
if "foo::Person" == lookup_tag:
return PersonPrinter(val)

return None

gdb.current_objfile().pretty_printers.append(lookup)
```

When the crate's debug executable is passed into GDB, `print bob` will display:
When the crate's debug executable is passed into GDB[^rust-gdb], `print bob` will display:

```text
"Bob" is 10 years old.
```

[^rust-gdb]: Note: This assumes you are using the `rust-gdb` script which configures pretty-printers for standard library types like `String`.

[auto-loading documentation]: https://sourceware.org/gdb/onlinedocs/gdb/Auto_002dloading-safe-path.html
[attributes]: ../attributes.md
[Natvis documentation]: https://docs.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects
[pretty print documentation]: https://sourceware.org/gdb/onlinedocs/gdb/Pretty-Printing.html
[pretty printing documentation]: https://sourceware.org/gdb/onlinedocs/gdb/Pretty-Printing.html
[_MetaListNameValueStr_]: ../attributes.md#meta-item-attribute-syntax

0 comments on commit 39a0be6

Please sign in to comment.