Add support for embedding pretty printers via the #[debugger_visualizer]
attribute. Add tests for embedding pretty printers and update documentation.
Ensure all error checking for `#[debugger_visualizer]` is done up front and not when the `debugger_visualizer` query is run. Clean up potential ODR violations when embedding pretty printers into the `__rustc_debug_gdb_scripts_section__` section. Respond to PR comments and update documentation.
This commit is contained in:
parent
ee9726cb10
commit
60458b97e7
27 changed files with 455 additions and 200 deletions
|
@ -5,11 +5,14 @@ use crate::llvm;
|
||||||
use crate::builder::Builder;
|
use crate::builder::Builder;
|
||||||
use crate::common::CodegenCx;
|
use crate::common::CodegenCx;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
use rustc_codegen_ssa::base::collect_debugger_visualizers_transitive;
|
||||||
use rustc_codegen_ssa::traits::*;
|
use rustc_codegen_ssa::traits::*;
|
||||||
|
use rustc_hir::def_id::LOCAL_CRATE;
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
use rustc_session::config::DebugInfo;
|
use rustc_session::config::{CrateType, DebugInfo};
|
||||||
|
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
|
use rustc_span::DebuggerVisualizerType;
|
||||||
|
|
||||||
/// Inserts a side-effect free instruction sequence that makes sure that the
|
/// 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.
|
/// .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(|| {
|
section_var.unwrap_or_else(|| {
|
||||||
let section_name = b".debug_gdb_scripts\0";
|
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 file 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 {
|
unsafe {
|
||||||
|
let section_contents = section_contents.as_slice();
|
||||||
let llvm_type = cx.type_array(cx.type_i8(), section_contents.len() as u64);
|
let llvm_type = cx.type_array(cx.type_i8(), section_contents.len() as u64);
|
||||||
|
|
||||||
let section_var = cx
|
let section_var = cx
|
||||||
|
@ -62,7 +89,32 @@ pub fn needs_gdb_debug_scripts_section(cx: &CodegenCx<'_, '_>) -> bool {
|
||||||
let omit_gdb_pretty_printer_section =
|
let omit_gdb_pretty_printer_section =
|
||||||
cx.tcx.sess.contains_name(cx.tcx.hir().krate_attrs(), sym::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 visualizers for rlibs could
|
||||||
|
// lead to ODR violations so we skip this crate type as well.
|
||||||
|
false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
!omit_gdb_pretty_printer_section
|
!omit_gdb_pretty_printer_section
|
||||||
&& cx.sess().opts.debuginfo != DebugInfo::None
|
&& cx.sess().opts.debuginfo != DebugInfo::None
|
||||||
&& cx.sess().target.emit_debug_gdb_scripts
|
&& cx.sess().target.emit_debug_gdb_scripts
|
||||||
|
&& embed_visualizers
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use rustc_data_structures::memmap::Mmap;
|
||||||
use rustc_data_structures::temp_dir::MaybeTempDir;
|
use rustc_data_structures::temp_dir::MaybeTempDir;
|
||||||
use rustc_errors::{ErrorGuaranteed, Handler};
|
use rustc_errors::{ErrorGuaranteed, Handler};
|
||||||
use rustc_fs_util::fix_windows_verbatim_for_gcc;
|
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::dependency_format::Linkage;
|
||||||
use rustc_middle::middle::exported_symbols::SymbolExportKind;
|
use rustc_middle::middle::exported_symbols::SymbolExportKind;
|
||||||
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
|
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.
|
/// need out of the shared crate context before we get rid of it.
|
||||||
use rustc_session::{filesearch, Session};
|
use rustc_session::{filesearch, Session};
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
|
use rustc_span::DebuggerVisualizerFile;
|
||||||
use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback};
|
use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback};
|
||||||
use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo};
|
use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo};
|
||||||
use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Target};
|
use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Target};
|
||||||
|
@ -37,6 +38,7 @@ use regex::Regex;
|
||||||
use tempfile::Builder as TempFileBuilder;
|
use tempfile::Builder as TempFileBuilder;
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::{BufWriter, Write};
|
use std::io::{BufWriter, Write};
|
||||||
|
@ -2099,14 +2101,16 @@ fn add_order_independent_options(
|
||||||
// Pass optimization flags down to the linker.
|
// Pass optimization flags down to the linker.
|
||||||
cmd.optimize();
|
cmd.optimize();
|
||||||
|
|
||||||
let debugger_visualizer_paths = if sess.target.is_like_msvc {
|
// Gather the set of NatVis files, if any, and write them out to a temp directory.
|
||||||
collect_debugger_visualizers(tmpdir, sess, &codegen_results.crate_info)
|
let natvis_visualizers = collect_natvis_visualizers(
|
||||||
} else {
|
tmpdir,
|
||||||
Vec::new()
|
sess,
|
||||||
};
|
&codegen_results.crate_info.local_crate_name,
|
||||||
|
&codegen_results.crate_info.natvis_debugger_visualizers,
|
||||||
|
);
|
||||||
|
|
||||||
// Pass debuginfo and strip flags down to the linker.
|
// Pass debuginfo, NatVis debugger visualizers and strip flags down to the linker.
|
||||||
cmd.debuginfo(strip_value(sess), &debugger_visualizer_paths);
|
cmd.debuginfo(strip_value(sess), &natvis_visualizers);
|
||||||
|
|
||||||
// We want to prevent the compiler from accidentally leaking in any system libraries,
|
// 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.
|
// 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);
|
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.
|
// Write the NatVis debugger visualizer files for each crate to the temp directory and gather the file paths.
|
||||||
fn collect_debugger_visualizers(
|
fn collect_natvis_visualizers(
|
||||||
tmpdir: &Path,
|
tmpdir: &Path,
|
||||||
sess: &Session,
|
sess: &Session,
|
||||||
crate_info: &CrateInfo,
|
crate_name: &Symbol,
|
||||||
|
natvis_debugger_visualizers: &BTreeSet<DebuggerVisualizerFile>,
|
||||||
) -> Vec<PathBuf> {
|
) -> Vec<PathBuf> {
|
||||||
let mut visualizer_paths = Vec::new();
|
let mut visualizer_paths = Vec::with_capacity(natvis_debugger_visualizers.len());
|
||||||
let debugger_visualizers = &crate_info.debugger_visualizers;
|
|
||||||
let mut index = 0;
|
|
||||||
|
|
||||||
for (&cnum, visualizers) in debugger_visualizers {
|
for (index, visualizer) in natvis_debugger_visualizers.iter().enumerate() {
|
||||||
let crate_name = if cnum == LOCAL_CRATE {
|
let visualizer_out_file = tmpdir.join(format!("{}-{}.natvis", crate_name.as_str(), index));
|
||||||
crate_info.local_crate_name.as_str()
|
|
||||||
} else {
|
match fs::write(&visualizer_out_file, &visualizer.src) {
|
||||||
crate_info.crate_name[&cnum].as_str()
|
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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
visualizer_paths
|
visualizer_paths
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,7 +183,7 @@ pub trait Linker {
|
||||||
fn optimize(&mut self);
|
fn optimize(&mut self);
|
||||||
fn pgo_gen(&mut self);
|
fn pgo_gen(&mut self);
|
||||||
fn control_flow_guard(&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_crt_objects(&mut self);
|
||||||
fn no_default_libraries(&mut self);
|
fn no_default_libraries(&mut self);
|
||||||
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]);
|
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");
|
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 {
|
match strip {
|
||||||
Strip::None => {
|
Strip::None => {
|
||||||
// This will cause the Microsoft linker to generate a PDB file
|
// 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
|
// 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:");
|
let mut arg = OsString::from("/NATVIS:");
|
||||||
arg.push(path);
|
arg.push(path);
|
||||||
self.cmd.arg(arg);
|
self.cmd.arg(arg);
|
||||||
|
|
|
@ -31,11 +31,13 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
|
||||||
use rustc_middle::ty::query::Providers;
|
use rustc_middle::ty::query::Providers;
|
||||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||||
use rustc_session::cgu_reuse_tracker::CguReuse;
|
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_session::Session;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
|
use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType};
|
||||||
use rustc_target::abi::{Align, VariantIdx};
|
use rustc_target::abi::{Align, VariantIdx};
|
||||||
|
|
||||||
|
use std::collections::BTreeSet;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::time::{Duration, Instant};
|
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>(
|
pub fn codegen_crate<B: ExtraBackendMethods>(
|
||||||
backend: B,
|
backend: B,
|
||||||
tcx: TyCtxt<'_>,
|
tcx: TyCtxt<'_>,
|
||||||
|
@ -838,13 +863,8 @@ impl CrateInfo {
|
||||||
missing_lang_items: Default::default(),
|
missing_lang_items: Default::default(),
|
||||||
dependency_formats: tcx.dependency_formats(()).clone(),
|
dependency_formats: tcx.dependency_formats(()).clone(),
|
||||||
windows_subsystem,
|
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 lang_items = tcx.lang_items();
|
||||||
|
|
||||||
let crates = tcx.crates(());
|
let crates = tcx.crates(());
|
||||||
|
@ -882,14 +902,29 @@ impl CrateInfo {
|
||||||
let missing =
|
let missing =
|
||||||
missing.iter().cloned().filter(|&l| lang_items::required(tcx, l)).collect();
|
missing.iter().cloned().filter(|&l| lang_items::required(tcx, l)).collect();
|
||||||
info.missing_lang_items.insert(cnum, missing);
|
info.missing_lang_items.insert(cnum, missing);
|
||||||
|
}
|
||||||
|
|
||||||
// Only include debugger visualizer files from crates that will be statically linked.
|
let embed_visualizers = tcx.sess.crate_types().iter().any(|&crate_type| match crate_type {
|
||||||
if used_crate_source.rlib.is_some() || used_crate_source.rmeta.is_some() {
|
CrateType::Executable | CrateType::Dylib | CrateType::Cdylib => {
|
||||||
let debugger_visualizers = tcx.debugger_visualizers(cnum).clone();
|
// These are crate types for which we invoke the linker and can embed
|
||||||
if !debugger_visualizers.is_empty() {
|
// NatVis visualizers.
|
||||||
info.debugger_visualizers.insert(cnum, debugger_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
|
info
|
||||||
|
|
|
@ -36,6 +36,7 @@ use rustc_session::cstore::{self, CrateSource};
|
||||||
use rustc_session::utils::NativeLibKind;
|
use rustc_session::utils::NativeLibKind;
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
use rustc_span::DebuggerVisualizerFile;
|
use rustc_span::DebuggerVisualizerFile;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
pub mod back;
|
pub mod back;
|
||||||
|
@ -157,7 +158,7 @@ pub struct CrateInfo {
|
||||||
pub missing_lang_items: FxHashMap<CrateNum, Vec<LangItem>>,
|
pub missing_lang_items: FxHashMap<CrateNum, Vec<LangItem>>,
|
||||||
pub dependency_formats: Lrc<Dependencies>,
|
pub dependency_formats: Lrc<Dependencies>,
|
||||||
pub windows_subsystem: Option<String>,
|
pub windows_subsystem: Option<String>,
|
||||||
pub debugger_visualizers: FxHashMap<CrateNum, Vec<DebuggerVisualizerFile>>,
|
pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Encodable, Decodable)]
|
#[derive(Encodable, Decodable)]
|
||||||
|
|
|
@ -399,7 +399,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||||
|
|
||||||
// RFC #3191: #[debugger_visualizer] support
|
// RFC #3191: #[debugger_visualizer] support
|
||||||
gated!(
|
gated!(
|
||||||
debugger_visualizer, Normal, template!(List: r#"natvis_file = "...""#),
|
debugger_visualizer, Normal, template!(List: r#"natvis_file = "...", gdb_script_file = "...""#),
|
||||||
DuplicatesOk, experimental!(debugger_visualizer)
|
DuplicatesOk, experimental!(debugger_visualizer)
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ use rustc_ast::tokenstream::DelimSpan;
|
||||||
use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MacArgs, MetaItemKind, NestedMetaItem};
|
use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MacArgs, MetaItemKind, NestedMetaItem};
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
|
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_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
|
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
|
||||||
|
@ -1982,49 +1983,64 @@ impl CheckAttrVisitor<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let hints = match attr.meta_item_list() {
|
let Some(hints) = attr.meta_item_list() else {
|
||||||
Some(meta_item_list) => meta_item_list,
|
self.emit_debugger_visualizer_err(attr.span);
|
||||||
None => {
|
return false;
|
||||||
self.emit_debugger_visualizer_err(attr);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let hint = match hints.len() {
|
let hint = match hints.len() {
|
||||||
1 => &hints[0],
|
1 => &hints[0],
|
||||||
_ => {
|
_ => {
|
||||||
self.emit_debugger_visualizer_err(attr);
|
self.emit_debugger_visualizer_err(attr.span);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if !hint.has_name(sym::natvis_file) {
|
let Some(meta_item) = hint.meta_item() else {
|
||||||
self.emit_debugger_visualizer_err(attr);
|
self.emit_debugger_visualizer_err(attr.span);
|
||||||
return false;
|
return false;
|
||||||
}
|
};
|
||||||
|
|
||||||
let meta_item = match hint.meta_item() {
|
let visualizer_path = match (meta_item.name_or_empty(), meta_item.value_str()) {
|
||||||
Some(meta_item) => meta_item,
|
(sym::natvis_file, Some(value)) => value,
|
||||||
None => {
|
(sym::gdb_script_file, Some(value)) => value,
|
||||||
self.emit_debugger_visualizer_err(attr);
|
(_, _) => {
|
||||||
|
self.emit_debugger_visualizer_err(meta_item.span);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match (meta_item.name_or_empty(), meta_item.value_str()) {
|
let file =
|
||||||
(sym::natvis_file, Some(_)) => true,
|
match resolve_path(&self.tcx.sess.parse_sess, visualizer_path.as_str(), attr.span) {
|
||||||
(_, _) => {
|
Ok(file) => file,
|
||||||
self.emit_debugger_visualizer_err(attr);
|
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
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_debugger_visualizer_err(&self, attr: &Attribute) {
|
fn emit_debugger_visualizer_err(&self, span: Span) {
|
||||||
self.tcx
|
self.tcx
|
||||||
.sess
|
.sess
|
||||||
.struct_span_err(attr.span, "invalid argument")
|
.struct_span_err(span, "invalid argument")
|
||||||
.note(r#"expected: `natvis_file = "..."`"#)
|
.note(r#"expected: `natvis_file = "..."`"#)
|
||||||
|
.note(r#"OR"#)
|
||||||
|
.note(r#"expected: `gdb_script_file = "..."`"#)
|
||||||
.emit();
|
.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,8 @@ fn check_for_debugger_visualizer<'tcx>(
|
||||||
let attrs = tcx.hir().attrs(hir_id);
|
let attrs = tcx.hir().attrs(hir_id);
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
if attr.has_name(sym::debugger_visualizer) {
|
if attr.has_name(sym::debugger_visualizer) {
|
||||||
let list = match attr.meta_item_list() {
|
let Some(list) = attr.meta_item_list() else {
|
||||||
Some(list) => list,
|
continue
|
||||||
_ => continue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let meta_item = match list.len() {
|
let meta_item = match list.len() {
|
||||||
|
@ -34,45 +33,28 @@ fn check_for_debugger_visualizer<'tcx>(
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let file = match (meta_item.name_or_empty(), meta_item.value_str()) {
|
let visualizer_type = match meta_item.name_or_empty() {
|
||||||
(sym::natvis_file, Some(value)) => {
|
sym::natvis_file => DebuggerVisualizerType::Natvis,
|
||||||
match resolve_path(&tcx.sess.parse_sess, value.as_str(), attr.span) {
|
sym::gdb_script_file => DebuggerVisualizerType::GdbPrettyPrinter,
|
||||||
Ok(file) => file,
|
_ => continue,
|
||||||
Err(mut err) => {
|
|
||||||
err.emit();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(_, _) => continue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if file.is_file() {
|
let file = match meta_item.value_str() {
|
||||||
let contents = match std::fs::read(&file) {
|
Some(value) => {
|
||||||
Ok(contents) => contents,
|
match resolve_path(&tcx.sess.parse_sess, value.as_str(), attr.span) {
|
||||||
Err(err) => {
|
Ok(file) => file,
|
||||||
tcx.sess
|
_ => continue,
|
||||||
.struct_span_err(
|
|
||||||
attr.span,
|
|
||||||
&format!(
|
|
||||||
"Unable to read contents of file `{}`. {}",
|
|
||||||
file.display(),
|
|
||||||
err
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
debugger_visualizers.insert(DebuggerVisualizerFile::new(
|
match std::fs::read(&file) {
|
||||||
Arc::from(contents),
|
Ok(contents) => {
|
||||||
DebuggerVisualizerType::Natvis,
|
debugger_visualizers
|
||||||
));
|
.insert(DebuggerVisualizerFile::new(Arc::from(contents), visualizer_type));
|
||||||
} else {
|
}
|
||||||
tcx.sess
|
_ => {}
|
||||||
.struct_span_err(attr.span, &format!("{} is not a valid file", file.display()))
|
|
||||||
.emit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1203,6 +1203,7 @@ impl SourceFileHash {
|
||||||
#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
|
#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
|
||||||
pub enum DebuggerVisualizerType {
|
pub enum DebuggerVisualizerType {
|
||||||
Natvis,
|
Natvis,
|
||||||
|
GdbPrettyPrinter,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single debugger visualizer file.
|
/// A single debugger visualizer file.
|
||||||
|
|
|
@ -721,6 +721,7 @@ symbols! {
|
||||||
fundamental,
|
fundamental,
|
||||||
future,
|
future,
|
||||||
future_trait,
|
future_trait,
|
||||||
|
gdb_script_file,
|
||||||
ge,
|
ge,
|
||||||
gen_future,
|
gen_future,
|
||||||
gen_kill,
|
gen_kill,
|
||||||
|
|
|
@ -14,6 +14,7 @@ to embed a debugger visualizer file into the PDB/ELF generated by `rustc`.
|
||||||
``` rust,ignore (partial-example)
|
``` rust,ignore (partial-example)
|
||||||
#![feature(debugger_visualizer)]
|
#![feature(debugger_visualizer)]
|
||||||
#![debugger_visualizer(natvis_file = "foo.natvis")]
|
#![debugger_visualizer(natvis_file = "foo.natvis")]
|
||||||
|
#![debugger_visualizer(gdb_script_file = "foo.py")]
|
||||||
struct Foo {
|
struct Foo {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,4 +23,5 @@ struct Foo {
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
Currently, this feature only supports embedding Natvis files on `-windows-msvc`
|
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.
|
||||||
|
|
|
@ -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>
|
|
@ -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)
|
|
@ -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 }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,10 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
<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>
|
<DisplayString>({x}, {y})</DisplayString>
|
||||||
<Expand>
|
<Expand>
|
||||||
<Item Name="[x]">x</Item>
|
<Item Name="[x]">x</Item>
|
||||||
<Item Name="[y]">y</Item>
|
<Item Name="[y]">y</Item>
|
||||||
</Expand>
|
</Expand>
|
||||||
</Type>
|
</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>
|
</AutoVisualizer>
|
23
src/test/debuginfo/embedded-visualizer-point.py
Normal file
23
src/test/debuginfo/embedded-visualizer-point.py
Normal 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)
|
10
src/test/debuginfo/embedded-visualizer.natvis
Normal file
10
src/test/debuginfo/embedded-visualizer.natvis
Normal 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>
|
23
src/test/debuginfo/embedded-visualizer.py
Normal file
23
src/test/debuginfo/embedded-visualizer.py
Normal 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)
|
112
src/test/debuginfo/embedded-visualizer.rs
Normal file
112
src/test/debuginfo/embedded-visualizer.rs
Normal 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() {
|
||||||
|
()
|
||||||
|
}
|
|
@ -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() {
|
|
||||||
()
|
|
||||||
}
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
</AutoVisualizer>
|
|
@ -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() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
error[E0658]: the `#[debugger_visualizer]` attribute is an experimental feature
|
error[E0658]: the `#[debugger_visualizer]` attribute is an experimental feature
|
||||||
--> $DIR/feature-gate-debugger-visualizer.rs:1:1
|
--> $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
|
= 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
|
= help: add `#![feature(debugger_visualizer)]` to the crate attributes to enable
|
||||||
|
|
|
@ -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)]
|
#![feature(debugger_visualizer)]
|
||||||
#![debugger_visualizer(random_file = "../foo.random")] //~ ERROR invalid argument
|
#![debugger_visualizer(random_file = "../foo.random")] //~ ERROR invalid argument
|
||||||
|
#![debugger_visualizer(natvis_file = "../foo.random")] //~ ERROR
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
error: invalid argument
|
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")]
|
LL | #![debugger_visualizer(random_file = "../foo.random")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: expected: `natvis_file = "..."`
|
= 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
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#![feature(debugger_visualizer)]
|
#![feature(debugger_visualizer)]
|
||||||
|
|
||||||
#[debugger_visualizer(natvis_file = "../foo.natvis")] //~ ERROR attribute should be applied to a module
|
#[debugger_visualizer(natvis_file = "../foo.natvis")] //~ ERROR attribute should be applied to a module
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -929,6 +929,16 @@ impl<'test> TestCx<'test> {
|
||||||
"add-auto-load-safe-path {}\n",
|
"add-auto-load-safe-path {}\n",
|
||||||
rust_pp_module_abs_path.replace(r"\", r"\\")
|
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"\\")
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
Loading…
Add table
Reference in a new issue