diff options
| author | Guillaume Gomez <guillaume1.gomez@gmail.com> | 2022-05-29 01:12:30 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-05-29 01:12:30 +0200 |
| commit | 239287f013b21d18c8ddd5bf5419629d43dca484 (patch) | |
| tree | 8370d6dac6dfdae06b82d23e097b46472e026673 | |
| parent | 376163a77cda823c4e1bdedd48039609069e6b58 (diff) | |
| parent | 7ac62ce75cef963017245287f0a7b140e09589d7 (diff) | |
| download | rust-239287f013b21d18c8ddd5bf5419629d43dca484.tar.gz rust-239287f013b21d18c8ddd5bf5419629d43dca484.zip | |
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
27 files changed, 460 insertions, 198 deletions
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index 31a09242c5a..5186aee57fb 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -5,11 +5,14 @@ use crate::llvm; use crate::builder::Builder; use crate::common::CodegenCx; use crate::value::Value; +use rustc_codegen_ssa::base::collect_debugger_visualizers_transitive; use rustc_codegen_ssa::traits::*; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; -use rustc_session::config::DebugInfo; +use rustc_session::config::{CrateType, DebugInfo}; use rustc_span::symbol::sym; +use rustc_span::DebuggerVisualizerType; /// Inserts a side-effect free instruction sequence that makes sure that the /// .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, ' section_var.unwrap_or_else(|| { let section_name = b".debug_gdb_scripts\0"; - let section_contents = b"\x01gdb_load_rust_pretty_printers.py\0"; + let mut section_contents = Vec::new(); + + // Add the pretty printers for the standard library first. + section_contents.extend_from_slice(b"\x01gdb_load_rust_pretty_printers.py\0"); + + // Next, add the pretty printers that were specified via the `#[debugger_visualizer]` attribute. + let visualizers = collect_debugger_visualizers_transitive( + cx.tcx, + DebuggerVisualizerType::GdbPrettyPrinter, + ); + let crate_name = cx.tcx.crate_name(LOCAL_CRATE); + for (index, visualizer) in visualizers.iter().enumerate() { + // The initial byte `4` instructs GDB that the following pretty printer + // is defined inline as opposed to in a standalone file. + section_contents.extend_from_slice(b"\x04"); + let vis_name = format!("pretty-printer-{}-{}\n", crate_name.as_str(), index); + section_contents.extend_from_slice(vis_name.as_bytes()); + section_contents.extend_from_slice(&visualizer.src); + + // The final byte `0` tells GDB that the pretty printer has been + // fully defined and can continue searching for additional + // pretty printers. + section_contents.extend_from_slice(b"\0"); + } unsafe { + let section_contents = section_contents.as_slice(); let llvm_type = cx.type_array(cx.type_i8(), section_contents.len() as u64); let section_var = cx @@ -62,7 +89,32 @@ pub fn needs_gdb_debug_scripts_section(cx: &CodegenCx<'_, '_>) -> bool { let omit_gdb_pretty_printer_section = cx.tcx.sess.contains_name(cx.tcx.hir().krate_attrs(), sym::omit_gdb_pretty_printer_section); + // To ensure the section `__rustc_debug_gdb_scripts_section__` will not create + // ODR violations at link time, this section will not be emitted for rlibs since + // each rlib could produce a different set of visualizers that would be embedded + // in the `.debug_gdb_scripts` section. For that reason, we make sure that the + // section is only emitted for leaf crates. + let embed_visualizers = cx.sess().crate_types().iter().any(|&crate_type| match crate_type { + CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Staticlib => { + // These are crate types for which we will embed pretty printers since they + // are treated as leaf crates. + true + } + CrateType::ProcMacro => { + // We could embed pretty printers for proc macro crates too but it does not + // seem like a good default, since this is a rare use case and we don't + // want to slow down the common case. + false + } + CrateType::Rlib => { + // As per the above description, embedding pretty printers for rlibs could + // lead to ODR violations so we skip this crate type as well. + false + } + }); + !omit_gdb_pretty_printer_section && cx.sess().opts.debuginfo != DebugInfo::None && cx.sess().target.emit_debug_gdb_scripts + && embed_visualizers } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 00f85852493..fc30679be03 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -5,7 +5,7 @@ use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{ErrorGuaranteed, Handler}; use rustc_fs_util::fix_windows_verbatim_for_gcc; -use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_hir::def_id::CrateNum; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip}; @@ -18,6 +18,7 @@ use rustc_session::utils::NativeLibKind; /// need out of the shared crate context before we get rid of it. use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; +use rustc_span::DebuggerVisualizerFile; use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo}; use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Target}; @@ -37,6 +38,7 @@ use regex::Regex; use tempfile::Builder as TempFileBuilder; use std::borrow::Borrow; +use std::collections::BTreeSet; use std::ffi::OsString; use std::fs::{File, OpenOptions}; use std::io::{BufWriter, Write}; @@ -2099,14 +2101,16 @@ fn add_order_independent_options( // Pass optimization flags down to the linker. cmd.optimize(); - let debugger_visualizer_paths = if sess.target.is_like_msvc { - collect_debugger_visualizers(tmpdir, sess, &codegen_results.crate_info) - } else { - Vec::new() - }; + // Gather the set of NatVis files, if any, and write them out to a temp directory. + let natvis_visualizers = collect_natvis_visualizers( + tmpdir, + sess, + &codegen_results.crate_info.local_crate_name, + &codegen_results.crate_info.natvis_debugger_visualizers, + ); - // Pass debuginfo and strip flags down to the linker. - cmd.debuginfo(strip_value(sess), &debugger_visualizer_paths); + // Pass debuginfo, NatVis debugger visualizers and strip flags down to the linker. + cmd.debuginfo(strip_value(sess), &natvis_visualizers); // We want to prevent the compiler from accidentally leaking in any system libraries, // so by default we tell linkers not to link to any default libraries. @@ -2125,43 +2129,33 @@ fn add_order_independent_options( add_rpath_args(cmd, sess, codegen_results, out_filename); } -// Write the debugger visualizer files for each crate to the temp directory and gather the file paths. -fn collect_debugger_visualizers( +// Write the NatVis debugger visualizer files for each crate to the temp directory and gather the file paths. +fn collect_natvis_visualizers( tmpdir: &Path, sess: &Session, - crate_info: &CrateInfo, + crate_name: &Symbol, + natvis_debugger_visualizers: &BTreeSet<DebuggerVisualizerFile>, ) -> Vec<PathBuf> { - let mut visualizer_paths = Vec::new(); - let debugger_visualizers = &crate_info.debugger_visualizers; - let mut index = 0; + let mut visualizer_paths = Vec::with_capacity(natvis_debugger_visualizers.len()); - for (&cnum, visualizers) in debugger_visualizers { - let crate_name = if cnum == LOCAL_CRATE { - crate_info.local_crate_name.as_str() - } else { - crate_info.crate_name[&cnum].as_str() - }; + for (index, visualizer) in natvis_debugger_visualizers.iter().enumerate() { + let visualizer_out_file = tmpdir.join(format!("{}-{}.natvis", crate_name.as_str(), index)); - for visualizer in visualizers { - let visualizer_out_file = tmpdir.join(format!("{}-{}.natvis", crate_name, index)); - - match fs::write(&visualizer_out_file, &visualizer.src) { - Ok(()) => { - visualizer_paths.push(visualizer_out_file.clone()); - index += 1; - } - Err(error) => { - sess.warn( - format!( - "Unable to write debugger visualizer file `{}`: {} ", - visualizer_out_file.display(), - error - ) - .as_str(), - ); - } - }; - } + match fs::write(&visualizer_out_file, &visualizer.src) { + Ok(()) => { + visualizer_paths.push(visualizer_out_file); + } + Err(error) => { + sess.warn( + format!( + "Unable to write debugger visualizer file `{}`: {} ", + visualizer_out_file.display(), + error + ) + .as_str(), + ); + } + }; } visualizer_paths } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 2a71377d2f1..e4236876463 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -183,7 +183,7 @@ pub trait Linker { fn optimize(&mut self); fn pgo_gen(&mut self); fn control_flow_guard(&mut self); - fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]); + fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]); fn no_crt_objects(&mut self); fn no_default_libraries(&mut self); fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]); @@ -915,7 +915,7 @@ impl<'a> Linker for MsvcLinker<'a> { self.cmd.arg("/guard:cf"); } - fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]) { + fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]) { match strip { Strip::None => { // This will cause the Microsoft linker to generate a PDB file @@ -944,7 +944,7 @@ impl<'a> Linker for MsvcLinker<'a> { } // This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file - for path in debugger_visualizers { + for path in natvis_debugger_visualizers { let mut arg = OsString::from("/NATVIS:"); arg.push(path); self.cmd.arg(arg); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index d11f1534153..420adec456f 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -31,11 +31,13 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::cgu_reuse_tracker::CguReuse; -use rustc_session::config::{self, EntryFnType, OutputType}; +use rustc_session::config::{self, CrateType, EntryFnType, OutputType}; use rustc_session::Session; use rustc_span::symbol::sym; +use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType}; use rustc_target::abi::{Align, VariantIdx}; +use std::collections::BTreeSet; use std::convert::TryFrom; use std::ops::{Deref, DerefMut}; use std::time::{Duration, Instant}; @@ -487,6 +489,29 @@ fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } +/// This function returns all of the debugger visualizers specified for the +/// current crate as well as all upstream crates transitively that match the +/// `visualizer_type` specified. +pub fn collect_debugger_visualizers_transitive( + tcx: TyCtxt<'_>, + visualizer_type: DebuggerVisualizerType, +) -> BTreeSet<DebuggerVisualizerFile> { + tcx.debugger_visualizers(LOCAL_CRATE) + .iter() + .chain( + tcx.crates(()) + .iter() + .filter(|&cnum| { + let used_crate_source = tcx.used_crate_source(*cnum); + used_crate_source.rlib.is_some() || used_crate_source.rmeta.is_some() + }) + .flat_map(|&cnum| tcx.debugger_visualizers(cnum)), + ) + .filter(|visualizer| visualizer.visualizer_type == visualizer_type) + .cloned() + .collect::<BTreeSet<_>>() +} + pub fn codegen_crate<B: ExtraBackendMethods>( backend: B, tcx: TyCtxt<'_>, @@ -838,13 +863,8 @@ impl CrateInfo { missing_lang_items: Default::default(), dependency_formats: tcx.dependency_formats(()).clone(), windows_subsystem, - debugger_visualizers: Default::default(), + natvis_debugger_visualizers: Default::default(), }; - let debugger_visualizers = tcx.debugger_visualizers(LOCAL_CRATE).clone(); - if !debugger_visualizers.is_empty() { - info.debugger_visualizers.insert(LOCAL_CRATE, debugger_visualizers); - } - let lang_items = tcx.lang_items(); let crates = tcx.crates(()); @@ -882,14 +902,29 @@ impl CrateInfo { let missing = missing.iter().cloned().filter(|&l| lang_items::required(tcx, l)).collect(); info.missing_lang_items.insert(cnum, missing); + } - // Only include debugger visualizer files from crates that will be statically linked. - if used_crate_source.rlib.is_some() || used_crate_source.rmeta.is_some() { - let debugger_visualizers = tcx.debugger_visualizers(cnum).clone(); - if !debugger_visualizers.is_empty() { - info.debugger_visualizers.insert(cnum, debugger_visualizers); - } + let embed_visualizers = tcx.sess.crate_types().iter().any(|&crate_type| match crate_type { + CrateType::Executable | CrateType::Dylib | CrateType::Cdylib => { + // These are crate types for which we invoke the linker and can embed + // NatVis visualizers. + true + } + CrateType::ProcMacro => { + // We could embed NatVis for proc macro crates too (to improve the debugging + // experience for them) but it does not seem like a good default, since + // this is a rare use case and we don't want to slow down the common case. + false } + CrateType::Staticlib | CrateType::Rlib => { + // We don't invoke the linker for these, so we don't need to collect the NatVis for them. + false + } + }); + + if tcx.sess.target.is_like_msvc && embed_visualizers { + info.natvis_debugger_visualizers = + collect_debugger_visualizers_transitive(tcx, DebuggerVisualizerType::Natvis); } info diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 9e1fe588c53..7fde700be39 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -36,6 +36,7 @@ use rustc_session::cstore::{self, CrateSource}; use rustc_session::utils::NativeLibKind; use rustc_span::symbol::Symbol; use rustc_span::DebuggerVisualizerFile; +use std::collections::BTreeSet; use std::path::{Path, PathBuf}; pub mod back; @@ -157,7 +158,7 @@ pub struct CrateInfo { pub missing_lang_items: FxHashMap<CrateNum, Vec<LangItem>>, pub dependency_formats: Lrc<Dependencies>, pub windows_subsystem: Option<String>, - pub debugger_visualizers: FxHashMap<CrateNum, Vec<DebuggerVisualizerFile>>, + pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>, } #[derive(Encodable, Decodable)] diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 097493e8985..e021e9603f3 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -399,7 +399,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // RFC #3191: #[debugger_visualizer] support gated!( - debugger_visualizer, Normal, template!(List: r#"natvis_file = "...""#), + debugger_visualizer, Normal, template!(List: r#"natvis_file = "...", gdb_script_file = "...""#), DuplicatesOk, experimental!(debugger_visualizer) ), diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 3d5da114ecf..3d38ff00eab 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -8,6 +8,7 @@ use rustc_ast::tokenstream::DelimSpan; use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MacArgs, MetaItemKind, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan}; +use rustc_expand::base::resolve_path; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; @@ -1982,49 +1983,64 @@ impl CheckAttrVisitor<'_> { } } - let hints = match attr.meta_item_list() { - Some(meta_item_list) => meta_item_list, - None => { - self.emit_debugger_visualizer_err(attr); - return false; - } + let Some(hints) = attr.meta_item_list() else { + self.emit_debugger_visualizer_err(attr.span); + return false; }; let hint = match hints.len() { 1 => &hints[0], _ => { - self.emit_debugger_visualizer_err(attr); + self.emit_debugger_visualizer_err(attr.span); return false; } }; - if !hint.has_name(sym::natvis_file) { - self.emit_debugger_visualizer_err(attr); + let Some(meta_item) = hint.meta_item() else { + self.emit_debugger_visualizer_err(attr.span); return false; - } + }; - let meta_item = match hint.meta_item() { - Some(meta_item) => meta_item, - None => { - self.emit_debugger_visualizer_err(attr); + let visualizer_path = match (meta_item.name_or_empty(), meta_item.value_str()) { + (sym::natvis_file, Some(value)) => value, + (sym::gdb_script_file, Some(value)) => value, + (_, _) => { + self.emit_debugger_visualizer_err(meta_item.span); return false; } }; - match (meta_item.name_or_empty(), meta_item.value_str()) { - (sym::natvis_file, Some(_)) => true, - (_, _) => { - self.emit_debugger_visualizer_err(attr); + let file = + match resolve_path(&self.tcx.sess.parse_sess, visualizer_path.as_str(), attr.span) { + Ok(file) => file, + Err(mut err) => { + err.emit(); + return false; + } + }; + + match std::fs::File::open(&file) { + Ok(_) => true, + Err(err) => { + self.tcx + .sess + .struct_span_err( + meta_item.span, + &format!("couldn't read {}: {}", file.display(), err), + ) + .emit(); false } } } - fn emit_debugger_visualizer_err(&self, attr: &Attribute) { + fn emit_debugger_visualizer_err(&self, span: Span) { self.tcx .sess - .struct_span_err(attr.span, "invalid argument") + .struct_span_err(span, "invalid argument") .note(r#"expected: `natvis_file = "..."`"#) + .note(r#"OR"#) + .note(r#"expected: `gdb_script_file = "..."`"#) .emit(); } diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index 8305830bc98..e08683fe23b 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -21,9 +21,8 @@ fn check_for_debugger_visualizer<'tcx>( let attrs = tcx.hir().attrs(hir_id); for attr in attrs { if attr.has_name(sym::debugger_visualizer) { - let list = match attr.meta_item_list() { - Some(list) => list, - _ => continue, + let Some(list) = attr.meta_item_list() else { + continue }; let meta_item = match list.len() { @@ -34,45 +33,35 @@ fn check_for_debugger_visualizer<'tcx>( _ => continue, }; - let file = match (meta_item.name_or_empty(), meta_item.value_str()) { - (sym::natvis_file, Some(value)) => { + let visualizer_type = match meta_item.name_or_empty() { + sym::natvis_file => DebuggerVisualizerType::Natvis, + sym::gdb_script_file => DebuggerVisualizerType::GdbPrettyPrinter, + _ => continue, + }; + + let file = match meta_item.value_str() { + Some(value) => { match resolve_path(&tcx.sess.parse_sess, value.as_str(), attr.span) { Ok(file) => file, - Err(mut err) => { - err.emit(); - continue; - } + _ => continue, } } - (_, _) => continue, + None => continue, }; - if file.is_file() { - let contents = match std::fs::read(&file) { - Ok(contents) => contents, - Err(err) => { - tcx.sess - .struct_span_err( - attr.span, - &format!( - "Unable to read contents of file `{}`. {}", - file.display(), - err - ), - ) - .emit(); - continue; - } - }; - - debugger_visualizers.insert(DebuggerVisualizerFile::new( - Arc::from(contents), - DebuggerVisualizerType::Natvis, - )); - } else { - tcx.sess - .struct_span_err(attr.span, &format!("{} is not a valid file", file.display())) - .emit(); + match std::fs::read(&file) { + Ok(contents) => { + debugger_visualizers + .insert(DebuggerVisualizerFile::new(Arc::from(contents), visualizer_type)); + } + Err(err) => { + tcx.sess + .struct_span_err( + meta_item.span, + &format!("couldn't read {}: {}", file.display(), err), + ) + .emit(); + } } } } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index f44eac63cef..adf5a744048 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -1203,6 +1203,7 @@ impl SourceFileHash { #[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] pub enum DebuggerVisualizerType { Natvis, + GdbPrettyPrinter, } /// A single debugger visualizer file. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 406e9a4113e..6989763f75a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -722,6 +722,7 @@ symbols! { fundamental, future, future_trait, + gdb_script_file, ge, gen_future, gen_kill, diff --git a/src/doc/unstable-book/src/language-features/debugger-visualizer.md b/src/doc/unstable-book/src/language-features/debugger-visualizer.md index 4ab482fffb9..c7a0414b676 100644 --- a/src/doc/unstable-book/src/language-features/debugger-visualizer.md +++ b/src/doc/unstable-book/src/language-features/debugger-visualizer.md @@ -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. diff --git a/src/test/debuginfo/auxiliary/dependency-with-embedded-visualizers.natvis b/src/test/debuginfo/auxiliary/dependency-with-embedded-visualizers.natvis new file mode 100644 index 00000000000..5900fcc01a6 --- /dev/null +++ b/src/test/debuginfo/auxiliary/dependency-with-embedded-visualizers.natvis @@ -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> diff --git a/src/test/debuginfo/auxiliary/dependency-with-embedded-visualizers.py b/src/test/debuginfo/auxiliary/dependency-with-embedded-visualizers.py new file mode 100644 index 00000000000..2635ed487c8 --- /dev/null +++ b/src/test/debuginfo/auxiliary/dependency-with-embedded-visualizers.py @@ -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) diff --git a/src/test/debuginfo/auxiliary/dependency-with-embedded-visualizers.rs b/src/test/debuginfo/auxiliary/dependency-with-embedded-visualizers.rs new file mode 100644 index 00000000000..327515b10af --- /dev/null +++ b/src/test/debuginfo/auxiliary/dependency-with-embedded-visualizers.rs @@ -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 } + } +} diff --git a/src/test/debuginfo/msvc-embedded-natvis.natvis b/src/test/debuginfo/embedded-visualizer-point.natvis index 201d014b520..d7bf6885dd4 100644 --- a/src/test/debuginfo/msvc-embedded-natvis.natvis +++ b/src/test/debuginfo/embedded-visualizer-point.natvis @@ -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> diff --git a/src/test/debuginfo/embedded-visualizer-point.py b/src/test/debuginfo/embedded-visualizer-point.py new file mode 100644 index 00000000000..d6b1af00785 --- /dev/null +++ b/src/test/debuginfo/embedded-visualizer-point.py @@ -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) diff --git a/src/test/debuginfo/embedded-visualizer.natvis b/src/test/debuginfo/embedded-visualizer.natvis new file mode 100644 index 00000000000..100437f90e5 --- /dev/null +++ b/src/test/debuginfo/embedded-visualizer.natvis @@ -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> diff --git a/src/test/debuginfo/embedded-visualizer.py b/src/test/debuginfo/embedded-visualizer.py new file mode 100644 index 00000000000..8e4fab61614 --- /dev/null +++ b/src/test/debuginfo/embedded-visualizer.py @@ -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) diff --git a/src/test/debuginfo/embedded-visualizer.rs b/src/test/debuginfo/embedded-visualizer.rs new file mode 100644 index 00000000000..0269015b466 --- /dev/null +++ b/src/test/debuginfo/embedded-visualizer.rs @@ -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() { + () +} diff --git a/src/test/debuginfo/msvc-embedded-natvis.rs b/src/test/debuginfo/msvc-embedded-natvis.rs deleted file mode 100644 index f714fb2ad17..00000000000 --- a/src/test/debuginfo/msvc-embedded-natvis.rs +++ /dev/null @@ -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() { - () -} diff --git a/src/test/ui/feature-gates/auxiliary/debugger-visualizer.natvis b/src/test/ui/feature-gates/auxiliary/debugger-visualizer.natvis new file mode 100644 index 00000000000..6eb47e3d85b --- /dev/null +++ b/src/test/ui/feature-gates/auxiliary/debugger-visualizer.natvis @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?> +<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010"> +</AutoVisualizer> diff --git a/src/test/ui/feature-gates/feature-gate-debugger-visualizer.rs b/src/test/ui/feature-gates/feature-gate-debugger-visualizer.rs index 3f9eb27a0d6..4c4dc450d18 100644 --- a/src/test/ui/feature-gates/feature-gate-debugger-visualizer.rs +++ b/src/test/ui/feature-gates/feature-gate-debugger-visualizer.rs @@ -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() {} diff --git a/src/test/ui/feature-gates/feature-gate-debugger-visualizer.stderr b/src/test/ui/feature-gates/feature-gate-debugger-visualizer.stderr index 721b2b185da..e9367fbc6c9 100644 --- a/src/test/ui/feature-gates/feature-gate-debugger-visualizer.stderr +++ b/src/test/ui/feature-gates/feature-gate-debugger-visualizer.stderr @@ -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 diff --git a/src/test/ui/invalid/invalid-debugger-visualizer-option.rs b/src/test/ui/invalid/invalid-debugger-visualizer-option.rs index 5332298f0ef..5645a30ccee 100644 --- a/src/test/ui/invalid/invalid-debugger-visualizer-option.rs +++ b/src/test/ui/invalid/invalid-debugger-visualizer-option.rs @@ -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() {} diff --git a/src/test/ui/invalid/invalid-debugger-visualizer-option.stderr b/src/test/ui/invalid/invalid-debugger-visualizer-option.stderr index 24ad9361fe3..afb8d16ee96 100644 --- a/src/test/ui/invalid/invalid-debugger-visualizer-option.stderr +++ b/src/test/ui/invalid/invalid-debugger-visualizer-option.stderr @@ -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 diff --git a/src/test/ui/invalid/invalid-debugger-visualizer-target.rs b/src/test/ui/invalid/invalid-debugger-visualizer-target.rs index 7668d092e61..f0aba6a75c4 100644 --- a/src/test/ui/invalid/invalid-debugger-visualizer-target.rs +++ b/src/test/ui/invalid/invalid-debugger-visualizer-target.rs @@ -1,5 +1,4 @@ #![feature(debugger_visualizer)] #[debugger_visualizer(natvis_file = "../foo.natvis")] //~ ERROR attribute should be applied to a module - fn main() {} diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 64b6df3567a..494c8d771b0 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -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"\\") + )); } } _ => { |
