Skip to content

Update debugger_visualizer to use the attribute template #1922

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 125 additions & 106 deletions src/attributes/debugger.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,27 @@ r[attributes.debugger.debugger_visualizer]
## The `debugger_visualizer` attribute

r[attributes.debugger.debugger_visualizer.intro]
The *`debugger_visualizer` attribute* can be used to embed a debugger visualizer file into the debug information.
The *`debugger_visualizer` [attribute][attributes]* 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.

> [!EXAMPLE]
> <!-- ignore: requires external files-->
> ```rust,ignore
> #![debugger_visualizer(natvis_file = "Example.natvis")]
> #![debugger_visualizer(gdb_script_file = "example.py")]
> ```

r[attributes.debugger.debugger_visualizer.syntax]
It uses the [MetaListNameValueStr] syntax to specify its inputs, and must be specified as a crate attribute.
The `debugger_visualizer` attribute uses the [MetaListNameValueStr] syntax to specify its inputs. One of the following keys must be specified:

- [`natvis_file`][attributes.debugger.debugger_visualizer.natvis]
- [`gdb_script_file`][attributes.debugger.debugger_visualizer.gdb]

r[attributes.debugger.debugger_visualizer.allowed-positions]
The `debugger_visualizer` attribute may only be specified on a [module] or crate root.

r[attributes.debugger.debugger_visualizer.duplicates]
Duplicate instances of the `debugger_visualizer` attribute will load all of the specified visualizers.

r[attributes.debugger.debugger_visualizer.natvis]
### Using `debugger_visualizer` with Natvis
Expand All @@ -24,60 +40,61 @@ r[attributes.debugger.debugger_visualizer.natvis.msvc]
This attribute only supports embedding Natvis files on `-windows-msvc` targets.

r[attributes.debugger.debugger_visualizer.natvis.path]
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")]

struct FancyRect {
x: f32,
y: f32,
dx: f32,
dy: f32,
}

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

and `Rectangle.natvis` contains:

```xml
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="foo::FancyRect">
<DisplayString>({x},{y}) + ({dx}, {dy})</DisplayString>
<Expand>
<Synthetic Name="LowerLeft">
<DisplayString>({x}, {y})</DisplayString>
</Synthetic>
<Synthetic Name="UpperLeft">
<DisplayString>({x}, {y + dy})</DisplayString>
</Synthetic>
<Synthetic Name="UpperRight">
<DisplayString>({x + dx}, {y + dy})</DisplayString>
</Synthetic>
<Synthetic Name="LowerRight">
<DisplayString>({x + dx}, {y})</DisplayString>
</Synthetic>
</Expand>
</Type>
</AutoVisualizer>
```

When viewed under WinDbg, the `fancy_rect` variable would be shown as follows:

```text
> Variables:
> 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)
```
The path to the Natvis file is specified with the `natvis_file` key, which is a path relative to the crate source file.

> [!EXAMPLE]
> <!-- ignore: requires external files, and msvc -->
> ```rust ignore
> #![debugger_visualizer(natvis_file = "Rectangle.natvis")]
>
> struct FancyRect {
> x: f32,
> y: f32,
> dx: f32,
> dy: f32,
> }
>
> fn main() {
> let fancy_rect = FancyRect { x: 10.0, y: 10.0, dx: 5.0, dy: 5.0 };
> println!("set breakpoint here");
> }
> ```
>
> and `Rectangle.natvis` contains:
>
> ```xml
> <?xml version="1.0" encoding="utf-8"?>
> <AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
> <Type Name="foo::FancyRect">
> <DisplayString>({x},{y}) + ({dx}, {dy})</DisplayString>
> <Expand>
> <Synthetic Name="LowerLeft">
> <DisplayString>({x}, {y})</DisplayString>
> </Synthetic>
> <Synthetic Name="UpperLeft">
> <DisplayString>({x}, {y + dy})</DisplayString>
> </Synthetic>
> <Synthetic Name="UpperRight">
> <DisplayString>({x + dx}, {y + dy})</DisplayString>
> </Synthetic>
> <Synthetic Name="LowerRight">
> <DisplayString>({x + dx}, {y})</DisplayString>
> </Synthetic>
> </Expand>
> </Type>
> </AutoVisualizer>
> ```
>
> When viewed under WinDbg, the `fancy_rect` variable would be shown as follows:
>
> ```text
> > Variables:
> > 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)
> ```

r[attributes.debugger.debugger_visualizer.gdb]
### Using `debugger_visualizer` with GDB
Expand All @@ -95,56 +112,57 @@ There are two ways to enable auto-loading embedded pretty printers:
r[attributes.debugger.debugger_visualizer.gdb.path]
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")]

struct Person {
name: String,
age: i32,
}

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

and `printer.py` contains:

```python
import gdb

class PersonPrinter:
"Print a Person"

def __init__(self, val):
self.val = val
self.name = val["name"]
self.age = int(val["age"])

def to_string(self):
return "{} is {} years old.".format(self.name, self.age)

def lookup(val):
lookup_tag = val.type.tag
if lookup_tag is None:
return None
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[^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`.
> [!EXAMPLE]
> <!-- ignore: requires external files -->
> ```rust ignore
> #![debugger_visualizer(gdb_script_file = "printer.py")]
>
> struct Person {
> name: String,
> age: i32,
> }
>
> fn main() {
> let bob = Person { name: String::from("Bob"), age: 10 };
> println!("set breakpoint here");
> }
> ```
>
> and `printer.py` contains:
>
> ```python
> import gdb
>
> class PersonPrinter:
> "Print a Person"
>
> def __init__(self, val):
> self.val = val
> self.name = val["name"]
> self.age = int(val["age"])
>
> def to_string(self):
> return "{} is {} years old.".format(self.name, self.age)
>
> def lookup(val):
> lookup_tag = val.type.tag
> if lookup_tag is None:
> return None
> 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[^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
Expand Down Expand Up @@ -184,3 +202,4 @@ macro_rules! example {
```

[attribute]: ../attributes.md
[module]: ../items/modules.md