未验证 提交 239287f0 编写于 作者: G Guillaume Gomez 提交者: GitHub

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
......@@ -5,11 +5,14 @@
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
}
......@@ -5,7 +5,7 @@
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 @@
/// 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 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
}
......
......@@ -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 @@ fn control_flow_guard(&mut self) {
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 @@ fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]) {
}
// 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);
......
......@@ -31,11 +31,13 @@
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 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> 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 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> 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
......
......@@ -36,6 +36,7 @@
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)]
......
......@@ -399,7 +399,7 @@ pub struct 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)
),
......
......@@ -8,6 +8,7 @@
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 @@ fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) -> bool {
}
}
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();
}
......
......@@ -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();
}
}
}
}
......
......@@ -1203,6 +1203,7 @@ fn hash_len(&self) -> usize {
#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
pub enum DebuggerVisualizerType {
Natvis,
GdbPrettyPrinter,
}
/// A single debugger visualizer file.
......
......@@ -722,6 +722,7 @@
fundamental,
future,
future_trait,
gdb_script_file,
ge,
gen_future,
gen_kill,
......
......@@ -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.
<?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>
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)
// 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 }
}
}
<?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>
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)
<?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>
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)
// 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() {
()
}
// 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() {
()
}
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
</AutoVisualizer>
#![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() {}
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
......
// 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() {}
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
#![feature(debugger_visualizer)]
#[debugger_visualizer(natvis_file = "../foo.natvis")] //~ ERROR attribute should be applied to a module
fn main() {}
......@@ -929,6 +929,16 @@ fn run_debuginfo_gdb_test_no_opt(&self) {
"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"\\")
));
}
}
_ => {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册