Skip to content

Commit 239287f

Browse files
authoredMay 28, 2022
Rollup merge of #97028 - ridwanabdillahi:pretty-printer, r=michaelwoerister
Add support for embedding pretty printers via `#[debugger_visualizer]` attribute Initial support for [RFC 3191](rust-lang/rfcs#3191) in PR #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: rust-lang/rfcs#3191
·
1.89.01.63.0
2 parents 376163a + 7ac62ce commit 239287f

27 files changed

+460
-198
lines changed
 

‎compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ use crate::llvm;
55
use crate::builder::Builder;
66
use crate::common::CodegenCx;
77
use crate::value::Value;
8+
use rustc_codegen_ssa::base::collect_debugger_visualizers_transitive;
89
use rustc_codegen_ssa::traits::*;
10+
use rustc_hir::def_id::LOCAL_CRATE;
911
use rustc_middle::bug;
10-
use rustc_session::config::DebugInfo;
12+
use rustc_session::config::{CrateType, DebugInfo};
1113

1214
use rustc_span::symbol::sym;
15+
use rustc_span::DebuggerVisualizerType;
1316

1417
/// Inserts a side-effect free instruction sequence that makes sure that the
1518
/// .debug_gdb_scripts global is referenced, so it isn't removed by the linker.
@@ -37,9 +40,33 @@ pub fn get_or_insert_gdb_debug_scripts_section_global<'ll>(cx: &CodegenCx<'ll, '
3740

3841
section_var.unwrap_or_else(|| {
3942
let section_name = b".debug_gdb_scripts\0";
40-
let section_contents = b"\x01gdb_load_rust_pretty_printers.py\0";
43+
let mut section_contents = Vec::new();
44+
45+
// Add the pretty printers for the standard library first.
46+
section_contents.extend_from_slice(b"\x01gdb_load_rust_pretty_printers.py\0");
47+
48+
// Next, add the pretty printers that were specified via the `#[debugger_visualizer]` attribute.
49+
let visualizers = collect_debugger_visualizers_transitive(
50+
cx.tcx,
51+
DebuggerVisualizerType::GdbPrettyPrinter,
52+
);
53+
let crate_name = cx.tcx.crate_name(LOCAL_CRATE);
54+
for (index, visualizer) in visualizers.iter().enumerate() {
55+
// The initial byte `4` instructs GDB that the following pretty printer
56+
// is defined inline as opposed to in a standalone file.
57+
section_contents.extend_from_slice(b"\x04");
58+
let vis_name = format!("pretty-printer-{}-{}\n", crate_name.as_str(), index);
59+
section_contents.extend_from_slice(vis_name.as_bytes());
60+
section_contents.extend_from_slice(&visualizer.src);
61+
62+
// The final byte `0` tells GDB that the pretty printer has been
63+
// fully defined and can continue searching for additional
64+
// pretty printers.
65+
section_contents.extend_from_slice(b"\0");
66+
}
4167

4268
unsafe {
69+
let section_contents = section_contents.as_slice();
4370
let llvm_type = cx.type_array(cx.type_i8(), section_contents.len() as u64);
4471

4572
let section_var = cx
@@ -62,7 +89,32 @@ pub fn needs_gdb_debug_scripts_section(cx: &CodegenCx<'_, '_>) -> bool {
6289
let omit_gdb_pretty_printer_section =
6390
cx.tcx.sess.contains_name(cx.tcx.hir().krate_attrs(), sym::omit_gdb_pretty_printer_section);
6491

92+
// To ensure the section `__rustc_debug_gdb_scripts_section__` will not create
93+
// ODR violations at link time, this section will not be emitted for rlibs since
94+
// each rlib could produce a different set of visualizers that would be embedded
95+
// in the `.debug_gdb_scripts` section. For that reason, we make sure that the
96+
// section is only emitted for leaf crates.
97+
let embed_visualizers = cx.sess().crate_types().iter().any(|&crate_type| match crate_type {
98+
CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Staticlib => {
99+
// These are crate types for which we will embed pretty printers since they
100+
// are treated as leaf crates.
101+
true
102+
}
103+
CrateType::ProcMacro => {
104+
// We could embed pretty printers for proc macro crates too but it does not
105+
// seem like a good default, since this is a rare use case and we don't
106+
// want to slow down the common case.
107+
false
108+
}
109+
CrateType::Rlib => {
110+
// As per the above description, embedding pretty printers for rlibs could
111+
// lead to ODR violations so we skip this crate type as well.
112+
false
113+
}
114+
});
115+
65116
!omit_gdb_pretty_printer_section
66117
&& cx.sess().opts.debuginfo != DebugInfo::None
67118
&& cx.sess().target.emit_debug_gdb_scripts
119+
&& embed_visualizers
68120
}

‎compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 34 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_data_structures::memmap::Mmap;
55
use rustc_data_structures::temp_dir::MaybeTempDir;
66
use rustc_errors::{ErrorGuaranteed, Handler};
77
use rustc_fs_util::fix_windows_verbatim_for_gcc;
8-
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
8+
use rustc_hir::def_id::CrateNum;
99
use rustc_middle::middle::dependency_format::Linkage;
1010
use rustc_middle::middle::exported_symbols::SymbolExportKind;
1111
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
@@ -18,6 +18,7 @@ use rustc_session::utils::NativeLibKind;
1818
/// need out of the shared crate context before we get rid of it.
1919
use rustc_session::{filesearch, Session};
2020
use rustc_span::symbol::Symbol;
21+
use rustc_span::DebuggerVisualizerFile;
2122
use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback};
2223
use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo};
2324
use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Target};
@@ -37,6 +38,7 @@ use regex::Regex;
3738
use tempfile::Builder as TempFileBuilder;
3839

3940
use std::borrow::Borrow;
41+
use std::collections::BTreeSet;
4042
use std::ffi::OsString;
4143
use std::fs::{File, OpenOptions};
4244
use std::io::{BufWriter, Write};
@@ -2099,14 +2101,16 @@ fn add_order_independent_options(
20992101
// Pass optimization flags down to the linker.
21002102
cmd.optimize();
21012103

2102-
let debugger_visualizer_paths = if sess.target.is_like_msvc {
2103-
collect_debugger_visualizers(tmpdir, sess, &codegen_results.crate_info)
2104-
} else {
2105-
Vec::new()
2106-
};
2104+
// Gather the set of NatVis files, if any, and write them out to a temp directory.
2105+
let natvis_visualizers = collect_natvis_visualizers(
2106+
tmpdir,
2107+
sess,
2108+
&codegen_results.crate_info.local_crate_name,
2109+
&codegen_results.crate_info.natvis_debugger_visualizers,
2110+
);
21072111

2108-
// Pass debuginfo and strip flags down to the linker.
2109-
cmd.debuginfo(strip_value(sess), &debugger_visualizer_paths);
2112+
// Pass debuginfo, NatVis debugger visualizers and strip flags down to the linker.
2113+
cmd.debuginfo(strip_value(sess), &natvis_visualizers);
21102114

21112115
// We want to prevent the compiler from accidentally leaking in any system libraries,
21122116
// so by default we tell linkers not to link to any default libraries.
@@ -2125,43 +2129,33 @@ fn add_order_independent_options(
21252129
add_rpath_args(cmd, sess, codegen_results, out_filename);
21262130
}
21272131

2128-
// Write the debugger visualizer files for each crate to the temp directory and gather the file paths.
2129-
fn collect_debugger_visualizers(
2132+
// Write the NatVis debugger visualizer files for each crate to the temp directory and gather the file paths.
2133+
fn collect_natvis_visualizers(
21302134
tmpdir: &Path,
21312135
sess: &Session,
2132-
crate_info: &CrateInfo,
2136+
crate_name: &Symbol,
2137+
natvis_debugger_visualizers: &BTreeSet<DebuggerVisualizerFile>,
21332138
) -> Vec<PathBuf> {
2134-
let mut visualizer_paths = Vec::new();
2135-
let debugger_visualizers = &crate_info.debugger_visualizers;
2136-
let mut index = 0;
2139+
let mut visualizer_paths = Vec::with_capacity(natvis_debugger_visualizers.len());
21372140

2138-
for (&cnum, visualizers) in debugger_visualizers {
2139-
let crate_name = if cnum == LOCAL_CRATE {
2140-
crate_info.local_crate_name.as_str()
2141-
} else {
2142-
crate_info.crate_name[&cnum].as_str()
2143-
};
2141+
for (index, visualizer) in natvis_debugger_visualizers.iter().enumerate() {
2142+
let visualizer_out_file = tmpdir.join(format!("{}-{}.natvis", crate_name.as_str(), index));
21442143

2145-
for visualizer in visualizers {
2146-
let visualizer_out_file = tmpdir.join(format!("{}-{}.natvis", crate_name, index));
2147-
2148-
match fs::write(&visualizer_out_file, &visualizer.src) {
2149-
Ok(()) => {
2150-
visualizer_paths.push(visualizer_out_file.clone());
2151-
index += 1;
2152-
}
2153-
Err(error) => {
2154-
sess.warn(
2155-
format!(
2156-
"Unable to write debugger visualizer file `{}`: {} ",
2157-
visualizer_out_file.display(),
2158-
error
2159-
)
2160-
.as_str(),
2161-
);
2162-
}
2163-
};
2164-
}
2144+
match fs::write(&visualizer_out_file, &visualizer.src) {
2145+
Ok(()) => {
2146+
visualizer_paths.push(visualizer_out_file);
2147+
}
2148+
Err(error) => {
2149+
sess.warn(
2150+
format!(
2151+
"Unable to write debugger visualizer file `{}`: {} ",
2152+
visualizer_out_file.display(),
2153+
error
2154+
)
2155+
.as_str(),
2156+
);
2157+
}
2158+
};
21652159
}
21662160
visualizer_paths
21672161
}

‎compiler/rustc_codegen_ssa/src/back/linker.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ pub trait Linker {
183183
fn optimize(&mut self);
184184
fn pgo_gen(&mut self);
185185
fn control_flow_guard(&mut self);
186-
fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]);
186+
fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]);
187187
fn no_crt_objects(&mut self);
188188
fn no_default_libraries(&mut self);
189189
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]);
@@ -915,7 +915,7 @@ impl<'a> Linker for MsvcLinker<'a> {
915915
self.cmd.arg("/guard:cf");
916916
}
917917

918-
fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]) {
918+
fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]) {
919919
match strip {
920920
Strip::None => {
921921
// This will cause the Microsoft linker to generate a PDB file
@@ -944,7 +944,7 @@ impl<'a> Linker for MsvcLinker<'a> {
944944
}
945945

946946
// This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file
947-
for path in debugger_visualizers {
947+
for path in natvis_debugger_visualizers {
948948
let mut arg = OsString::from("/NATVIS:");
949949
arg.push(path);
950950
self.cmd.arg(arg);

‎compiler/rustc_codegen_ssa/src/base.rs

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
3131
use rustc_middle::ty::query::Providers;
3232
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
3333
use rustc_session::cgu_reuse_tracker::CguReuse;
34-
use rustc_session::config::{self, EntryFnType, OutputType};
34+
use rustc_session::config::{self, CrateType, EntryFnType, OutputType};
3535
use rustc_session::Session;
3636
use rustc_span::symbol::sym;
37+
use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType};
3738
use rustc_target::abi::{Align, VariantIdx};
3839

40+
use std::collections::BTreeSet;
3941
use std::convert::TryFrom;
4042
use std::ops::{Deref, DerefMut};
4143
use std::time::{Duration, Instant};
@@ -487,6 +489,29 @@ fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
487489
}
488490
}
489491

492+
/// This function returns all of the debugger visualizers specified for the
493+
/// current crate as well as all upstream crates transitively that match the
494+
/// `visualizer_type` specified.
495+
pub fn collect_debugger_visualizers_transitive(
496+
tcx: TyCtxt<'_>,
497+
visualizer_type: DebuggerVisualizerType,
498+
) -> BTreeSet<DebuggerVisualizerFile> {
499+
tcx.debugger_visualizers(LOCAL_CRATE)
500+
.iter()
501+
.chain(
502+
tcx.crates(())
503+
.iter()
504+
.filter(|&cnum| {
505+
let used_crate_source = tcx.used_crate_source(*cnum);
506+
used_crate_source.rlib.is_some() || used_crate_source.rmeta.is_some()
507+
})
508+
.flat_map(|&cnum| tcx.debugger_visualizers(cnum)),
509+
)
510+
.filter(|visualizer| visualizer.visualizer_type == visualizer_type)
511+
.cloned()
512+
.collect::<BTreeSet<_>>()
513+
}
514+
490515
pub fn codegen_crate<B: ExtraBackendMethods>(
491516
backend: B,
492517
tcx: TyCtxt<'_>,
@@ -838,13 +863,8 @@ impl CrateInfo {
838863
missing_lang_items: Default::default(),
839864
dependency_formats: tcx.dependency_formats(()).clone(),
840865
windows_subsystem,
841-
debugger_visualizers: Default::default(),
866+
natvis_debugger_visualizers: Default::default(),
842867
};
843-
let debugger_visualizers = tcx.debugger_visualizers(LOCAL_CRATE).clone();
844-
if !debugger_visualizers.is_empty() {
845-
info.debugger_visualizers.insert(LOCAL_CRATE, debugger_visualizers);
846-
}
847-
848868
let lang_items = tcx.lang_items();
849869

850870
let crates = tcx.crates(());
@@ -882,14 +902,29 @@ impl CrateInfo {
882902
let missing =
883903
missing.iter().cloned().filter(|&l| lang_items::required(tcx, l)).collect();
884904
info.missing_lang_items.insert(cnum, missing);
905+
}
885906

886-
// Only include debugger visualizer files from crates that will be statically linked.
887-
if used_crate_source.rlib.is_some() || used_crate_source.rmeta.is_some() {
888-
let debugger_visualizers = tcx.debugger_visualizers(cnum).clone();
889-
if !debugger_visualizers.is_empty() {
890-
info.debugger_visualizers.insert(cnum, debugger_visualizers);
891-
}
907+
let embed_visualizers = tcx.sess.crate_types().iter().any(|&crate_type| match crate_type {
908+
CrateType::Executable | CrateType::Dylib | CrateType::Cdylib => {
909+
// These are crate types for which we invoke the linker and can embed
910+
// NatVis visualizers.
911+
true
912+
}
913+
CrateType::ProcMacro => {
914+
// We could embed NatVis for proc macro crates too (to improve the debugging
915+
// experience for them) but it does not seem like a good default, since
916+
// this is a rare use case and we don't want to slow down the common case.
917+
false
892918
}
919+
CrateType::Staticlib | CrateType::Rlib => {
920+
// We don't invoke the linker for these, so we don't need to collect the NatVis for them.
921+
false
922+
}
923+
});
924+
925+
if tcx.sess.target.is_like_msvc && embed_visualizers {
926+
info.natvis_debugger_visualizers =
927+
collect_debugger_visualizers_transitive(tcx, DebuggerVisualizerType::Natvis);
893928
}
894929

895930
info

‎compiler/rustc_codegen_ssa/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use rustc_session::cstore::{self, CrateSource};
3636
use rustc_session::utils::NativeLibKind;
3737
use rustc_span::symbol::Symbol;
3838
use rustc_span::DebuggerVisualizerFile;
39+
use std::collections::BTreeSet;
3940
use std::path::{Path, PathBuf};
4041

4142
pub mod back;
@@ -157,7 +158,7 @@ pub struct CrateInfo {
157158
pub missing_lang_items: FxHashMap<CrateNum, Vec<LangItem>>,
158159
pub dependency_formats: Lrc<Dependencies>,
159160
pub windows_subsystem: Option<String>,
160-
pub debugger_visualizers: FxHashMap<CrateNum, Vec<DebuggerVisualizerFile>>,
161+
pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>,
161162
}
162163

163164
#[derive(Encodable, Decodable)]

‎compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
399399

400400
// RFC #3191: #[debugger_visualizer] support
401401
gated!(
402-
debugger_visualizer, Normal, template!(List: r#"natvis_file = "...""#),
402+
debugger_visualizer, Normal, template!(List: r#"natvis_file = "...", gdb_script_file = "...""#),
403403
DuplicatesOk, experimental!(debugger_visualizer)
404404
),
405405

‎compiler/rustc_passes/src/check_attr.rs

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_ast::tokenstream::DelimSpan;
88
use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MacArgs, MetaItemKind, NestedMetaItem};
99
use rustc_data_structures::fx::FxHashMap;
1010
use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
11+
use rustc_expand::base::resolve_path;
1112
use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
1213
use rustc_hir as hir;
1314
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
@@ -1982,49 +1983,64 @@ impl CheckAttrVisitor<'_> {
19821983
}
19831984
}
19841985

1985-
let hints = match attr.meta_item_list() {
1986-
Some(meta_item_list) => meta_item_list,
1987-
None => {
1988-
self.emit_debugger_visualizer_err(attr);
1989-
return false;
1990-
}
1986+
let Some(hints) = attr.meta_item_list() else {
1987+
self.emit_debugger_visualizer_err(attr.span);
1988+
return false;
19911989
};
19921990

19931991
let hint = match hints.len() {
19941992
1 => &hints[0],
19951993
_ => {
1996-
self.emit_debugger_visualizer_err(attr);
1994+
self.emit_debugger_visualizer_err(attr.span);
19971995
return false;
19981996
}
19991997
};
20001998

2001-
if !hint.has_name(sym::natvis_file) {
2002-
self.emit_debugger_visualizer_err(attr);
1999+
let Some(meta_item) = hint.meta_item() else {
2000+
self.emit_debugger_visualizer_err(attr.span);
20032001
return false;
2004-
}
2002+
};
20052003

2006-
let meta_item = match hint.meta_item() {
2007-
Some(meta_item) => meta_item,
2008-
None => {
2009-
self.emit_debugger_visualizer_err(attr);
2004+
let visualizer_path = match (meta_item.name_or_empty(), meta_item.value_str()) {
2005+
(sym::natvis_file, Some(value)) => value,
2006+
(sym::gdb_script_file, Some(value)) => value,
2007+
(_, _) => {
2008+
self.emit_debugger_visualizer_err(meta_item.span);
20102009
return false;
20112010
}
20122011
};
20132012

2014-
match (meta_item.name_or_empty(), meta_item.value_str()) {
2015-
(sym::natvis_file, Some(_)) => true,
2016-
(_, _) => {
2017-
self.emit_debugger_visualizer_err(attr);
2013+
let file =
2014+
match resolve_path(&self.tcx.sess.parse_sess, visualizer_path.as_str(), attr.span) {
2015+
Ok(file) => file,
2016+
Err(mut err) => {
2017+
err.emit();
2018+
return false;
2019+
}
2020+
};
2021+
2022+
match std::fs::File::open(&file) {
2023+
Ok(_) => true,
2024+
Err(err) => {
2025+
self.tcx
2026+
.sess
2027+
.struct_span_err(
2028+
meta_item.span,
2029+
&format!("couldn't read {}: {}", file.display(), err),
2030+
)
2031+
.emit();
20182032
false
20192033
}
20202034
}
20212035
}
20222036

2023-
fn emit_debugger_visualizer_err(&self, attr: &Attribute) {
2037+
fn emit_debugger_visualizer_err(&self, span: Span) {
20242038
self.tcx
20252039
.sess
2026-
.struct_span_err(attr.span, "invalid argument")
2040+
.struct_span_err(span, "invalid argument")
20272041
.note(r#"expected: `natvis_file = "..."`"#)
2042+
.note(r#"OR"#)
2043+
.note(r#"expected: `gdb_script_file = "..."`"#)
20282044
.emit();
20292045
}
20302046

‎compiler/rustc_passes/src/debugger_visualizer.rs

Lines changed: 25 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@ fn check_for_debugger_visualizer<'tcx>(
2121
let attrs = tcx.hir().attrs(hir_id);
2222
for attr in attrs {
2323
if attr.has_name(sym::debugger_visualizer) {
24-
let list = match attr.meta_item_list() {
25-
Some(list) => list,
26-
_ => continue,
24+
let Some(list) = attr.meta_item_list() else {
25+
continue
2726
};
2827

2928
let meta_item = match list.len() {
@@ -34,45 +33,35 @@ fn check_for_debugger_visualizer<'tcx>(
3433
_ => continue,
3534
};
3635

37-
let file = match (meta_item.name_or_empty(), meta_item.value_str()) {
38-
(sym::natvis_file, Some(value)) => {
36+
let visualizer_type = match meta_item.name_or_empty() {
37+
sym::natvis_file => DebuggerVisualizerType::Natvis,
38+
sym::gdb_script_file => DebuggerVisualizerType::GdbPrettyPrinter,
39+
_ => continue,
40+
};
41+
42+
let file = match meta_item.value_str() {
43+
Some(value) => {
3944
match resolve_path(&tcx.sess.parse_sess, value.as_str(), attr.span) {
4045
Ok(file) => file,
41-
Err(mut err) => {
42-
err.emit();
43-
continue;
44-
}
46+
_ => continue,
4547
}
4648
}
47-
(_, _) => continue,
49+
None => continue,
4850
};
4951

50-
if file.is_file() {
51-
let contents = match std::fs::read(&file) {
52-
Ok(contents) => contents,
53-
Err(err) => {
54-
tcx.sess
55-
.struct_span_err(
56-
attr.span,
57-
&format!(
58-
"Unable to read contents of file `{}`. {}",
59-
file.display(),
60-
err
61-
),
62-
)
63-
.emit();
64-
continue;
65-
}
66-
};
67-
68-
debugger_visualizers.insert(DebuggerVisualizerFile::new(
69-
Arc::from(contents),
70-
DebuggerVisualizerType::Natvis,
71-
));
72-
} else {
73-
tcx.sess
74-
.struct_span_err(attr.span, &format!("{} is not a valid file", file.display()))
75-
.emit();
52+
match std::fs::read(&file) {
53+
Ok(contents) => {
54+
debugger_visualizers
55+
.insert(DebuggerVisualizerFile::new(Arc::from(contents), visualizer_type));
56+
}
57+
Err(err) => {
58+
tcx.sess
59+
.struct_span_err(
60+
meta_item.span,
61+
&format!("couldn't read {}: {}", file.display(), err),
62+
)
63+
.emit();
64+
}
7665
}
7766
}
7867
}

‎compiler/rustc_span/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,7 @@ impl SourceFileHash {
12031203
#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
12041204
pub enum DebuggerVisualizerType {
12051205
Natvis,
1206+
GdbPrettyPrinter,
12061207
}
12071208

12081209
/// A single debugger visualizer file.

‎compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,7 @@ symbols! {
722722
fundamental,
723723
future,
724724
future_trait,
725+
gdb_script_file,
725726
ge,
726727
gen_future,
727728
gen_kill,

‎src/doc/unstable-book/src/language-features/debugger-visualizer.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ to embed a debugger visualizer file into the PDB/ELF generated by `rustc`.
1414
``` rust,ignore (partial-example)
1515
#![feature(debugger_visualizer)]
1616
#![debugger_visualizer(natvis_file = "foo.natvis")]
17+
#![debugger_visualizer(gdb_script_file = "foo.py")]
1718
struct Foo {
1819
1920
}
@@ -22,4 +23,5 @@ struct Foo {
2223
## Limitations
2324

2425
Currently, this feature only supports embedding Natvis files on `-windows-msvc`
25-
targets when using the MSVC linker via the `natvis_file` meta item.
26+
targets via the `natvis_file` meta item. `-windows-gnu` targets are not currently
27+
supported.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
3+
<Type Name="dependency_with_embedded_visualizers::Person">
4+
<DisplayString>{name} is {age} years old.</DisplayString>
5+
<Expand>
6+
<Item Name="[name]">name</Item>
7+
<Item Name="[age]">age</Item>
8+
</Expand>
9+
</Type>
10+
</AutoVisualizer>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import gdb
2+
3+
class PersonPrinter:
4+
"Print a Person"
5+
6+
def __init__(self, val):
7+
self.val = val
8+
self.name = val["name"]
9+
self.age = int(val["age"])
10+
11+
def to_string(self):
12+
return "{} is {} years old.".format(self.name, self.age)
13+
14+
def lookup(val):
15+
lookup_tag = val.type.tag
16+
if lookup_tag is None:
17+
return None
18+
if "dependency_with_embedded_visualizers::Person" == lookup_tag:
19+
return PersonPrinter(val)
20+
21+
return None
22+
23+
gdb.current_objfile().pretty_printers.append(lookup)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// compile-flags:-g
2+
// ignore-lldb
3+
// no-prefer-dynamic
4+
5+
#![feature(debugger_visualizer)]
6+
#![debugger_visualizer(natvis_file = "dependency-with-embedded-visualizers.natvis")]
7+
#![debugger_visualizer(gdb_script_file = "dependency-with-embedded-visualizers.py")]
8+
#![crate_type = "rlib"]
9+
10+
pub struct Person {
11+
name: String,
12+
age: i32,
13+
}
14+
15+
impl Person {
16+
pub fn new(name: String, age: i32) -> Person {
17+
Person { name: name, age: age }
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
3-
<Type Name="msvc_embedded_natvis::Point">
3+
<Type Name="embedded_visualizer::point::Point">
44
<DisplayString>({x}, {y})</DisplayString>
55
<Expand>
66
<Item Name="[x]">x</Item>
77
<Item Name="[y]">y</Item>
88
</Expand>
99
</Type>
10-
11-
<Type Name="msvc_embedded_natvis::Line">
12-
<DisplayString>({a}, {b})</DisplayString>
13-
<Expand>
14-
<Item Name="[a]">a</Item>
15-
<Item Name="[b]">b</Item>
16-
</Expand>
17-
</Type>
1810
</AutoVisualizer>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import gdb
2+
3+
class PointPrinter:
4+
"Print a Point"
5+
6+
def __init__(self, val):
7+
self.val = val
8+
self.x = int(val["x"])
9+
self.y = int(val["y"])
10+
11+
def to_string(self):
12+
return "({}, {})".format(self.x, self.y)
13+
14+
def lookup(val):
15+
lookup_tag = val.type.tag
16+
if lookup_tag is None:
17+
return None
18+
if "embedded_visualizer::point::Point" == lookup_tag:
19+
return PointPrinter(val)
20+
21+
return None
22+
23+
gdb.current_objfile().pretty_printers.append(lookup)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
3+
<Type Name="embedded_visualizer::Line">
4+
<DisplayString>({a}, {b})</DisplayString>
5+
<Expand>
6+
<Item Name="[a]">a</Item>
7+
<Item Name="[b]">b</Item>
8+
</Expand>
9+
</Type>
10+
</AutoVisualizer>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import gdb
2+
3+
class LinePrinter:
4+
"Print a Line"
5+
6+
def __init__(self, val):
7+
self.val = val
8+
self.a = val["a"]
9+
self.b = val["b"]
10+
11+
def to_string(self):
12+
return "({}, {})".format(self.a, self.b)
13+
14+
def lookup(val):
15+
lookup_tag = val.type.tag
16+
if lookup_tag is None:
17+
return None
18+
if "embedded_visualizer::Line" == lookup_tag:
19+
return LinePrinter(val)
20+
21+
return None
22+
23+
gdb.current_objfile().pretty_printers.append(lookup)
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// compile-flags:-g
2+
// min-gdb-version: 8.1
3+
// ignore-lldb
4+
5+
// === CDB TESTS ==================================================================================
6+
7+
// cdb-command: g
8+
9+
// The .nvlist command in cdb does not always have a deterministic output
10+
// for the order that NatVis files are displayed.
11+
12+
// cdb-command: .nvlist
13+
// cdb-check: [...].exe (embedded NatVis "[...]embedded_visualizer-0.natvis")
14+
15+
// cdb-command: .nvlist
16+
// cdb-check: [...].exe (embedded NatVis "[...]embedded_visualizer-1.natvis")
17+
18+
// cdb-command: .nvlist
19+
// cdb-check: [...].exe (embedded NatVis "[...]embedded_visualizer-2.natvis")
20+
21+
// cdb-command: dx point_a
22+
// cdb-check:point_a : (0, 0) [Type: embedded_visualizer::point::Point]
23+
// cdb-check: [<Raw View>] [Type: embedded_visualizer::point::Point]
24+
// cdb-check: [x] : 0 [Type: int]
25+
// cdb-check: [y] : 0 [Type: int]
26+
27+
// cdb-command: dx point_b
28+
// cdb-check:point_b : (5, 8) [Type: embedded_visualizer::point::Point]
29+
// cdb-check: [<Raw View>] [Type: embedded_visualizer::point::Point]
30+
// cdb-check: [x] : 5 [Type: int]
31+
// cdb-check: [y] : 8 [Type: int]
32+
33+
// cdb-command: dx line
34+
// cdb-check:line : ((0, 0), (5, 8)) [Type: embedded_visualizer::Line]
35+
// cdb-check: [<Raw View>] [Type: embedded_visualizer::Line]
36+
// cdb-check: [a] : (0, 0) [Type: embedded_visualizer::point::Point]
37+
// cdb-check: [b] : (5, 8) [Type: embedded_visualizer::point::Point]
38+
39+
// cdb-command: dx person
40+
// cdb-check:person : "Person A" is 10 years old. [Type: dependency_with_embedded_visualizers::Person]
41+
// cdb-check: [<Raw View>] [Type: dependency_with_embedded_visualizers::Person]
42+
// cdb-check: [name] : "Person A" [Type: alloc::string::String]
43+
// cdb-check: [age] : 10 [Type: int]
44+
45+
// === GDB TESTS ===================================================================================
46+
47+
// gdb-command: run
48+
49+
// gdb-command: info auto-load python-scripts
50+
// gdb-check:Yes pretty-printer-embedded_visualizer-0
51+
// gdb-check:Yes pretty-printer-embedded_visualizer-1
52+
// gdb-command: print point_a
53+
// gdb-check:$1 = (0, 0)
54+
// gdb-command: print point_b
55+
// gdb-check:$2 = (5, 8)
56+
// gdb-command: print line
57+
// gdb-check:$3 = ((0, 0), (5, 8))
58+
// gdb-command: print person
59+
// gdb-check:$4 = "Person A" is 10 years old.
60+
61+
#![allow(unused_variables)]
62+
#![feature(debugger_visualizer)]
63+
#![debugger_visualizer(natvis_file = "embedded-visualizer.natvis")]
64+
#![debugger_visualizer(gdb_script_file = "embedded-visualizer.py")]
65+
66+
// aux-build: dependency-with-embedded-visualizers.rs
67+
extern crate dependency_with_embedded_visualizers;
68+
69+
use dependency_with_embedded_visualizers::Person;
70+
71+
#[debugger_visualizer(natvis_file = "embedded-visualizer-point.natvis")]
72+
#[debugger_visualizer(gdb_script_file = "embedded-visualizer-point.py")]
73+
mod point {
74+
pub struct Point {
75+
x: i32,
76+
y: i32,
77+
}
78+
79+
impl Point {
80+
pub fn new(x: i32, y: i32) -> Point {
81+
Point { x: x, y: y }
82+
}
83+
}
84+
}
85+
86+
use point::Point;
87+
88+
pub struct Line {
89+
a: Point,
90+
b: Point,
91+
}
92+
93+
impl Line {
94+
pub fn new(a: Point, b: Point) -> Line {
95+
Line { a: a, b: b }
96+
}
97+
}
98+
99+
fn main() {
100+
let point_a = Point::new(0, 0);
101+
let point_b = Point::new(5, 8);
102+
let line = Line::new(point_a, point_b);
103+
104+
let name = String::from("Person A");
105+
let person = Person::new(name, 10);
106+
107+
zzz(); // #break
108+
}
109+
110+
fn zzz() {
111+
()
112+
}

‎src/test/debuginfo/msvc-embedded-natvis.rs

Lines changed: 0 additions & 64 deletions
This file was deleted.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
3+
</AutoVisualizer>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
#![debugger_visualizer(natvis_file = "../foo.natvis")] //~ ERROR the `#[debugger_visualizer]` attribute is an experimental feature
1+
#![debugger_visualizer(natvis_file = "auxiliary/debugger-visualizer.natvis")] //~ ERROR the `#[debugger_visualizer]` attribute is an experimental feature
22

33
fn main() {}

‎src/test/ui/feature-gates/feature-gate-debugger-visualizer.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0658]: the `#[debugger_visualizer]` attribute is an experimental feature
22
--> $DIR/feature-gate-debugger-visualizer.rs:1:1
33
|
4-
LL | #![debugger_visualizer(natvis_file = "../foo.natvis")]
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
LL | #![debugger_visualizer(natvis_file = "auxiliary/debugger-visualizer.natvis")]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= note: see issue #95939 <https://github.com/rust-lang/rust/issues/95939> for more information
88
= help: add `#![feature(debugger_visualizer)]` to the crate attributes to enable
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
// normalize-stderr-test: "foo.random:.*\(" -> "foo.random: $$FILE_NOT_FOUND_MSG ("
2+
// normalize-stderr-test: "os error \d+" -> "os error $$FILE_NOT_FOUND_CODE"
3+
14
#![feature(debugger_visualizer)]
25
#![debugger_visualizer(random_file = "../foo.random")] //~ ERROR invalid argument
3-
6+
#![debugger_visualizer(natvis_file = "../foo.random")] //~ ERROR
47
fn main() {}
Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
error: invalid argument
2-
--> $DIR/invalid-debugger-visualizer-option.rs:2:1
2+
--> $DIR/invalid-debugger-visualizer-option.rs:5:24
33
|
44
LL | #![debugger_visualizer(random_file = "../foo.random")]
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= note: expected: `natvis_file = "..."`
8+
= note: OR
9+
= note: expected: `gdb_script_file = "..."`
810

9-
error: aborting due to previous error
11+
error: couldn't read $DIR/../foo.random: $FILE_NOT_FOUND_MSG (os error $FILE_NOT_FOUND_CODE)
12+
--> $DIR/invalid-debugger-visualizer-option.rs:6:24
13+
|
14+
LL | #![debugger_visualizer(natvis_file = "../foo.random")]
15+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
17+
error: aborting due to 2 previous errors
1018

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#![feature(debugger_visualizer)]
22

33
#[debugger_visualizer(natvis_file = "../foo.natvis")] //~ ERROR attribute should be applied to a module
4-
54
fn main() {}

‎src/tools/compiletest/src/runtest.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,16 @@ impl<'test> TestCx<'test> {
929929
"add-auto-load-safe-path {}\n",
930930
rust_pp_module_abs_path.replace(r"\", r"\\")
931931
));
932+
933+
let output_base_dir = self.output_base_dir().to_str().unwrap().to_owned();
934+
935+
// Add the directory containing the output binary to
936+
// include embedded pretty printers to GDB's script
937+
// auto loading safe path
938+
script_str.push_str(&format!(
939+
"add-auto-load-safe-path {}\n",
940+
output_base_dir.replace(r"\", r"\\")
941+
));
932942
}
933943
}
934944
_ => {

0 commit comments

Comments
 (0)
Please sign in to comment.