Rollup merge of #97028 - ridwanabdillahi:pretty-printer, r=michaelwoerister

Add support for embedding pretty printers via `#[debugger_visualizer]` attribute

Initial support for [RFC 3191](https://github.com/rust-lang/rfcs/pull/3191) in PR https://github.com/rust-lang/rust/pull/91779 was scoped to supporting embedding NatVis files using a new attribute. This PR implements the pretty printer support as stated in the RFC mentioned above.

This change includes embedding pretty printers in the `.debug_gdb_scripts` just as the pretty printers for rustc are embedded today. Also added additional tests for embedded pretty printers. Additionally cleaned up error checking so all error checking is done up front regardless of the current target.

RFC: https://github.com/rust-lang/rfcs/pull/3191
This commit is contained in:
Guillaume Gomez 2022-05-29 01:12:30 +02:00 committed by GitHub
commit 239287f013
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 462 additions and 200 deletions

View file

@ -14,6 +14,7 @@ to embed a debugger visualizer file into the PDB/ELF generated by `rustc`.
``` rust,ignore (partial-example)
#![feature(debugger_visualizer)]
#![debugger_visualizer(natvis_file = "foo.natvis")]
#![debugger_visualizer(gdb_script_file = "foo.py")]
struct Foo {
}
@ -22,4 +23,5 @@ struct Foo {
## Limitations
Currently, this feature only supports embedding Natvis files on `-windows-msvc`
targets when using the MSVC linker via the `natvis_file` meta item.
targets via the `natvis_file` meta item. `-windows-gnu` targets are not currently
supported.

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="dependency_with_embedded_visualizers::Person">
<DisplayString>{name} is {age} years old.</DisplayString>
<Expand>
<Item Name="[name]">name</Item>
<Item Name="[age]">age</Item>
</Expand>
</Type>
</AutoVisualizer>

View file

@ -0,0 +1,23 @@
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 "dependency_with_embedded_visualizers::Person" == lookup_tag:
return PersonPrinter(val)
return None
gdb.current_objfile().pretty_printers.append(lookup)

View file

@ -0,0 +1,19 @@
// compile-flags:-g
// ignore-lldb
// no-prefer-dynamic
#![feature(debugger_visualizer)]
#![debugger_visualizer(natvis_file = "dependency-with-embedded-visualizers.natvis")]
#![debugger_visualizer(gdb_script_file = "dependency-with-embedded-visualizers.py")]
#![crate_type = "rlib"]
pub struct Person {
name: String,
age: i32,
}
impl Person {
pub fn new(name: String, age: i32) -> Person {
Person { name: name, age: age }
}
}

View file

@ -1,18 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="msvc_embedded_natvis::Point">
<Type Name="embedded_visualizer::point::Point">
<DisplayString>({x}, {y})</DisplayString>
<Expand>
<Item Name="[x]">x</Item>
<Item Name="[y]">y</Item>
</Expand>
</Type>
<Type Name="msvc_embedded_natvis::Line">
<DisplayString>({a}, {b})</DisplayString>
<Expand>
<Item Name="[a]">a</Item>
<Item Name="[b]">b</Item>
</Expand>
</Type>
</AutoVisualizer>

View file

@ -0,0 +1,23 @@
import gdb
class PointPrinter:
"Print a Point"
def __init__(self, val):
self.val = val
self.x = int(val["x"])
self.y = int(val["y"])
def to_string(self):
return "({}, {})".format(self.x, self.y)
def lookup(val):
lookup_tag = val.type.tag
if lookup_tag is None:
return None
if "embedded_visualizer::point::Point" == lookup_tag:
return PointPrinter(val)
return None
gdb.current_objfile().pretty_printers.append(lookup)

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="embedded_visualizer::Line">
<DisplayString>({a}, {b})</DisplayString>
<Expand>
<Item Name="[a]">a</Item>
<Item Name="[b]">b</Item>
</Expand>
</Type>
</AutoVisualizer>

View file

@ -0,0 +1,23 @@
import gdb
class LinePrinter:
"Print a Line"
def __init__(self, val):
self.val = val
self.a = val["a"]
self.b = val["b"]
def to_string(self):
return "({}, {})".format(self.a, self.b)
def lookup(val):
lookup_tag = val.type.tag
if lookup_tag is None:
return None
if "embedded_visualizer::Line" == lookup_tag:
return LinePrinter(val)
return None
gdb.current_objfile().pretty_printers.append(lookup)

View file

@ -0,0 +1,112 @@
// compile-flags:-g
// min-gdb-version: 8.1
// ignore-lldb
// === CDB TESTS ==================================================================================
// cdb-command: g
// The .nvlist command in cdb does not always have a deterministic output
// for the order that NatVis files are displayed.
// cdb-command: .nvlist
// cdb-check: [...].exe (embedded NatVis "[...]embedded_visualizer-0.natvis")
// cdb-command: .nvlist
// cdb-check: [...].exe (embedded NatVis "[...]embedded_visualizer-1.natvis")
// cdb-command: .nvlist
// cdb-check: [...].exe (embedded NatVis "[...]embedded_visualizer-2.natvis")
// cdb-command: dx point_a
// cdb-check:point_a : (0, 0) [Type: embedded_visualizer::point::Point]
// cdb-check: [<Raw View>] [Type: embedded_visualizer::point::Point]
// cdb-check: [x] : 0 [Type: int]
// cdb-check: [y] : 0 [Type: int]
// cdb-command: dx point_b
// cdb-check:point_b : (5, 8) [Type: embedded_visualizer::point::Point]
// cdb-check: [<Raw View>] [Type: embedded_visualizer::point::Point]
// cdb-check: [x] : 5 [Type: int]
// cdb-check: [y] : 8 [Type: int]
// cdb-command: dx line
// cdb-check:line : ((0, 0), (5, 8)) [Type: embedded_visualizer::Line]
// cdb-check: [<Raw View>] [Type: embedded_visualizer::Line]
// cdb-check: [a] : (0, 0) [Type: embedded_visualizer::point::Point]
// cdb-check: [b] : (5, 8) [Type: embedded_visualizer::point::Point]
// cdb-command: dx person
// cdb-check:person : "Person A" is 10 years old. [Type: dependency_with_embedded_visualizers::Person]
// cdb-check: [<Raw View>] [Type: dependency_with_embedded_visualizers::Person]
// cdb-check: [name] : "Person A" [Type: alloc::string::String]
// cdb-check: [age] : 10 [Type: int]
// === GDB TESTS ===================================================================================
// gdb-command: run
// gdb-command: info auto-load python-scripts
// gdb-check:Yes pretty-printer-embedded_visualizer-0
// gdb-check:Yes pretty-printer-embedded_visualizer-1
// gdb-command: print point_a
// gdb-check:$1 = (0, 0)
// gdb-command: print point_b
// gdb-check:$2 = (5, 8)
// gdb-command: print line
// gdb-check:$3 = ((0, 0), (5, 8))
// gdb-command: print person
// gdb-check:$4 = "Person A" is 10 years old.
#![allow(unused_variables)]
#![feature(debugger_visualizer)]
#![debugger_visualizer(natvis_file = "embedded-visualizer.natvis")]
#![debugger_visualizer(gdb_script_file = "embedded-visualizer.py")]
// aux-build: dependency-with-embedded-visualizers.rs
extern crate dependency_with_embedded_visualizers;
use dependency_with_embedded_visualizers::Person;
#[debugger_visualizer(natvis_file = "embedded-visualizer-point.natvis")]
#[debugger_visualizer(gdb_script_file = "embedded-visualizer-point.py")]
mod point {
pub struct Point {
x: i32,
y: i32,
}
impl Point {
pub fn new(x: i32, y: i32) -> Point {
Point { x: x, y: y }
}
}
}
use point::Point;
pub struct Line {
a: Point,
b: Point,
}
impl Line {
pub fn new(a: Point, b: Point) -> Line {
Line { a: a, b: b }
}
}
fn main() {
let point_a = Point::new(0, 0);
let point_b = Point::new(5, 8);
let line = Line::new(point_a, point_b);
let name = String::from("Person A");
let person = Person::new(name, 10);
zzz(); // #break
}
fn zzz() {
()
}

View file

@ -1,64 +0,0 @@
// only-cdb
// compile-flags:-g
// === CDB TESTS ==================================================================================
// cdb-command: g
// cdb-command: .nvlist
// cdb-check: [...].exe (embedded NatVis "[...]msvc_embedded_natvis-0.natvis")
// cdb-command: dx point_a
// cdb-check:point_a : (0, 0) [Type: msvc_embedded_natvis::Point]
// cdb-check: [<Raw View>] [Type: msvc_embedded_natvis::Point]
// cdb-check: [x] : 0 [Type: int]
// cdb-check: [y] : 0 [Type: int]
// cdb-command: dx point_b
// cdb-check:point_b : (5, 8) [Type: msvc_embedded_natvis::Point]
// cdb-check: [<Raw View>] [Type: msvc_embedded_natvis::Point]
// cdb-check: [x] : 5 [Type: int]
// cdb-check: [y] : 8 [Type: int]
// cdb-command: dx line
// cdb-check:line : ((0, 0), (5, 8)) [Type: msvc_embedded_natvis::Line]
// cdb-check: [<Raw View>] [Type: msvc_embedded_natvis::Line]
// cdb-check: [a] : (0, 0) [Type: msvc_embedded_natvis::Point]
// cdb-check: [b] : (5, 8) [Type: msvc_embedded_natvis::Point]
#![feature(debugger_visualizer)]
#![debugger_visualizer(natvis_file = "msvc-embedded-natvis.natvis")]
pub struct Point {
x: i32,
y: i32,
}
impl Point {
pub fn new(x: i32, y: i32) -> Point {
Point { x: x, y: y }
}
}
pub struct Line {
a: Point,
b: Point,
}
impl Line {
pub fn new(a: Point, b: Point) -> Line {
Line { a: a, b: b }
}
}
fn main() {
let point_a = Point::new(0, 0);
let point_b = Point::new(5, 8);
let line = Line::new(point_a, point_b);
zzz(); // #break
}
fn zzz() {
()
}

View file

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
</AutoVisualizer>

View file

@ -1,3 +1,3 @@
#![debugger_visualizer(natvis_file = "../foo.natvis")] //~ ERROR the `#[debugger_visualizer]` attribute is an experimental feature
#![debugger_visualizer(natvis_file = "auxiliary/debugger-visualizer.natvis")] //~ ERROR the `#[debugger_visualizer]` attribute is an experimental feature
fn main() {}

View file

@ -1,8 +1,8 @@
error[E0658]: the `#[debugger_visualizer]` attribute is an experimental feature
--> $DIR/feature-gate-debugger-visualizer.rs:1:1
|
LL | #![debugger_visualizer(natvis_file = "../foo.natvis")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | #![debugger_visualizer(natvis_file = "auxiliary/debugger-visualizer.natvis")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #95939 <https://github.com/rust-lang/rust/issues/95939> for more information
= help: add `#![feature(debugger_visualizer)]` to the crate attributes to enable

View file

@ -1,4 +1,7 @@
// normalize-stderr-test: "foo.random:.*\(" -> "foo.random: $$FILE_NOT_FOUND_MSG ("
// normalize-stderr-test: "os error \d+" -> "os error $$FILE_NOT_FOUND_CODE"
#![feature(debugger_visualizer)]
#![debugger_visualizer(random_file = "../foo.random")] //~ ERROR invalid argument
#![debugger_visualizer(natvis_file = "../foo.random")] //~ ERROR
fn main() {}

View file

@ -1,10 +1,18 @@
error: invalid argument
--> $DIR/invalid-debugger-visualizer-option.rs:2:1
--> $DIR/invalid-debugger-visualizer-option.rs:5:24
|
LL | #![debugger_visualizer(random_file = "../foo.random")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: expected: `natvis_file = "..."`
= note: OR
= note: expected: `gdb_script_file = "..."`
error: aborting due to previous error
error: couldn't read $DIR/../foo.random: $FILE_NOT_FOUND_MSG (os error $FILE_NOT_FOUND_CODE)
--> $DIR/invalid-debugger-visualizer-option.rs:6:24
|
LL | #![debugger_visualizer(natvis_file = "../foo.random")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors

View file

@ -1,5 +1,4 @@
#![feature(debugger_visualizer)]
#[debugger_visualizer(natvis_file = "../foo.natvis")] //~ ERROR attribute should be applied to a module
fn main() {}

View file

@ -929,6 +929,16 @@ impl<'test> TestCx<'test> {
"add-auto-load-safe-path {}\n",
rust_pp_module_abs_path.replace(r"\", r"\\")
));
let output_base_dir = self.output_base_dir().to_str().unwrap().to_owned();
// Add the directory containing the output binary to
// include embedded pretty printers to GDB's script
// auto loading safe path
script_str.push_str(&format!(
"add-auto-load-safe-path {}\n",
output_base_dir.replace(r"\", r"\\")
));
}
}
_ => {