Skip to content

Commit b6821ad

Browse files
Rollup merge of rust-lang#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 rust-lang#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
2 parents 59d2231 + 7ac62ce commit b6821ad

27 files changed

+460
-198
lines changed

compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs

+54-2
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

+34-40
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

+3-3
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

+48-13
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

+2-1
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

+1-1
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

0 commit comments

Comments
 (0)