Skip to content

Commit

Permalink
Auto merge of #91779 - ridwanabdillahi:natvis, r=michaelwoerister
Browse files Browse the repository at this point in the history
Add a new Rust attribute to support embedding debugger visualizers

Implemented [this RFC](rust-lang/rfcs#3191) to add support for embedding debugger visualizers into a PDB.

Added a new attribute `#[debugger_visualizer]` and updated the `CrateMetadata` to store debugger visualizers for crate dependencies.

RFC: rust-lang/rfcs#3191
  • Loading branch information
bors committed May 5, 2022
2 parents 322a149 + 791bef5 commit a7d6768
Show file tree
Hide file tree
Showing 29 changed files with 554 additions and 76 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4184,6 +4184,7 @@ dependencies = [
"rustc_attr",
"rustc_data_structures",
"rustc_errors",
"rustc_expand",
"rustc_feature",
"rustc_hir",
"rustc_index",
Expand Down
47 changes: 4 additions & 43 deletions compiler/rustc_builtin_macros/src/source_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@ use rustc_ast::ptr::P;
use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast_pretty::pprust;
use rustc_errors::PResult;
use rustc_expand::base::{self, *};
use rustc_expand::module::DirOwnership;
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_parse::{self, new_parser_from_file};
use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
use rustc_span::symbol::Symbol;
use rustc_span::{self, FileName, Pos, Span};
use rustc_span::{self, Pos, Span};

use smallvec::SmallVec;
use std::path::PathBuf;
use std::rc::Rc;

// These macros all relate to the file system; they either return
Expand Down Expand Up @@ -104,7 +102,7 @@ pub fn expand_include<'cx>(
return DummyResult::any(sp);
};
// The file will be added to the code map by the parser
let file = match resolve_path(cx, file.as_str(), sp) {
let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
Ok(f) => f,
Err(mut err) => {
err.emit();
Expand Down Expand Up @@ -176,7 +174,7 @@ pub fn expand_include_str(
let Some(file) = get_single_str_from_tts(cx, sp, tts, "include_str!") else {
return DummyResult::any(sp);
};
let file = match resolve_path(cx, file.as_str(), sp) {
let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
Ok(f) => f,
Err(mut err) => {
err.emit();
Expand Down Expand Up @@ -210,7 +208,7 @@ pub fn expand_include_bytes(
let Some(file) = get_single_str_from_tts(cx, sp, tts, "include_bytes!") else {
return DummyResult::any(sp);
};
let file = match resolve_path(cx, file.as_str(), sp) {
let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
Ok(f) => f,
Err(mut err) => {
err.emit();
Expand All @@ -225,40 +223,3 @@ pub fn expand_include_bytes(
}
}
}

/// Resolves a `path` mentioned inside Rust code, returning an absolute path.
///
/// This unifies the logic used for resolving `include_X!`.
fn resolve_path<'a>(
cx: &mut ExtCtxt<'a>,
path: impl Into<PathBuf>,
span: Span,
) -> PResult<'a, PathBuf> {
let path = path.into();

// Relative paths are resolved relative to the file in which they are found
// after macro expansion (that is, they are unhygienic).
if !path.is_absolute() {
let callsite = span.source_callsite();
let mut result = match cx.source_map().span_to_filename(callsite) {
FileName::Real(name) => name
.into_local_path()
.expect("attempting to resolve a file path in an external file"),
FileName::DocTest(path, _) => path,
other => {
return Err(cx.struct_span_err(
span,
&format!(
"cannot resolve relative path in non-file source `{}`",
cx.source_map().filename_for_diagnostics(&other)
),
));
}
};
result.pop();
result.push(path);
Ok(result)
} else {
Ok(path)
}
}
51 changes: 49 additions & 2 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::SymbolExportKind;
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
Expand Down Expand Up @@ -2099,8 +2099,14 @@ 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()
};

// Pass debuginfo and strip flags down to the linker.
cmd.debuginfo(strip_value(sess));
cmd.debuginfo(strip_value(sess), &debugger_visualizer_paths);

// 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.
Expand All @@ -2119,6 +2125,47 @@ 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(
tmpdir: &Path,
sess: &Session,
crate_info: &CrateInfo,
) -> Vec<PathBuf> {
let mut visualizer_paths = Vec::new();
let debugger_visualizers = &crate_info.debugger_visualizers;
let mut index = 0;

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 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
}

/// # Native library linking
///
/// User-supplied library search paths (-L on the command line). These are the same paths used to
Expand Down
23 changes: 15 additions & 8 deletions compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
fn debuginfo(&mut self, strip: Strip, 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]);
Expand Down Expand Up @@ -611,7 +611,7 @@ impl<'a> Linker for GccLinker<'a> {

fn control_flow_guard(&mut self) {}

fn debuginfo(&mut self, strip: Strip) {
fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
// MacOS linker doesn't support stripping symbols directly anymore.
if self.sess.target.is_like_osx {
return;
Expand Down Expand Up @@ -915,7 +915,7 @@ impl<'a> Linker for MsvcLinker<'a> {
self.cmd.arg("/guard:cf");
}

fn debuginfo(&mut self, strip: Strip) {
fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]) {
match strip {
Strip::None => {
// This will cause the Microsoft linker to generate a PDB file
Expand All @@ -942,6 +942,13 @@ 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 {
let mut arg = OsString::from("/NATVIS:");
arg.push(path);
self.cmd.arg(arg);
}
}
Strip::Debuginfo | Strip::Symbols => {
self.cmd.arg("/DEBUG:NONE");
Expand Down Expand Up @@ -1124,7 +1131,7 @@ impl<'a> Linker for EmLinker<'a> {

fn control_flow_guard(&mut self) {}

fn debuginfo(&mut self, _strip: Strip) {
fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
// Preserve names or generate source maps depending on debug info
self.cmd.arg(match self.sess.opts.debuginfo {
DebugInfo::None => "-g0",
Expand Down Expand Up @@ -1315,7 +1322,7 @@ impl<'a> Linker for WasmLd<'a> {

fn pgo_gen(&mut self) {}

fn debuginfo(&mut self, strip: Strip) {
fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
match strip {
Strip::None => {}
Strip::Debuginfo => {
Expand Down Expand Up @@ -1450,7 +1457,7 @@ impl<'a> Linker for L4Bender<'a> {

fn pgo_gen(&mut self) {}

fn debuginfo(&mut self, strip: Strip) {
fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
match strip {
Strip::None => {}
Strip::Debuginfo => {
Expand Down Expand Up @@ -1600,7 +1607,7 @@ impl<'a> Linker for PtxLinker<'a> {
self.cmd.arg("-L").arg(path);
}

fn debuginfo(&mut self, _strip: Strip) {
fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
self.cmd.arg("--debug");
}

Expand Down Expand Up @@ -1699,7 +1706,7 @@ impl<'a> Linker for BpfLinker<'a> {
self.cmd.arg("-L").arg(path);
}

fn debuginfo(&mut self, _strip: Strip) {
fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
self.cmd.arg("--debug");
}

Expand Down
18 changes: 17 additions & 1 deletion compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -847,7 +847,13 @@ impl CrateInfo {
missing_lang_items: Default::default(),
dependency_formats: tcx.dependency_formats(()).clone(),
windows_subsystem,
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(());
Expand All @@ -862,7 +868,9 @@ impl CrateInfo {
info.native_libraries
.insert(cnum, tcx.native_libraries(cnum).iter().map(Into::into).collect());
info.crate_name.insert(cnum, tcx.crate_name(cnum));
info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum).clone());

let used_crate_source = tcx.used_crate_source(cnum);
info.used_crate_source.insert(cnum, used_crate_source.clone());
if tcx.is_compiler_builtins(cnum) {
info.compiler_builtins = Some(cnum);
}
Expand All @@ -883,6 +891,14 @@ 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);
}
}
}

info
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_codegen_ssa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT
use rustc_session::cstore::{self, CrateSource};
use rustc_session::utils::NativeLibKind;
use rustc_span::symbol::Symbol;
use rustc_span::DebuggerVisualizerFile;
use std::path::{Path, PathBuf};

pub mod back;
Expand Down Expand Up @@ -156,6 +157,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>>,
}

#[derive(Encodable, Decodable)]
Expand Down
41 changes: 39 additions & 2 deletions compiler/rustc_expand/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind};
use rustc_attr::{self as attr, Deprecation, Stability};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::{self, Lrc};
use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult};
use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
use rustc_lint_defs::BuiltinLintDiagnostics;
use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS};
Expand All @@ -20,7 +20,7 @@ use rustc_span::edition::Edition;
use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId};
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};
use rustc_span::{FileName, Span, DUMMY_SP};
use smallvec::{smallvec, SmallVec};

use std::default::Default;
Expand Down Expand Up @@ -1136,6 +1136,43 @@ impl<'a> ExtCtxt<'a> {
}
}

/// Resolves a `path` mentioned inside Rust code, returning an absolute path.
///
/// This unifies the logic used for resolving `include_X!`.
pub fn resolve_path(
parse_sess: &ParseSess,
path: impl Into<PathBuf>,
span: Span,
) -> PResult<'_, PathBuf> {
let path = path.into();

// Relative paths are resolved relative to the file in which they are found
// after macro expansion (that is, they are unhygienic).
if !path.is_absolute() {
let callsite = span.source_callsite();
let mut result = match parse_sess.source_map().span_to_filename(callsite) {
FileName::Real(name) => name
.into_local_path()
.expect("attempting to resolve a file path in an external file"),
FileName::DocTest(path, _) => path,
other => {
return Err(parse_sess.span_diagnostic.struct_span_err(
span,
&format!(
"cannot resolve relative path in non-file source `{}`",
parse_sess.source_map().filename_for_diagnostics(&other)
),
));
}
};
result.pop();
result.push(path);
Ok(result)
} else {
Ok(path)
}
}

/// Extracts a string literal from the macro expanded version of `expr`,
/// returning a diagnostic error of `err_msg` if `expr` is not a string literal.
/// The returned bool indicates whether an applicable suggestion has already been
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,8 @@ declare_features! (
(active, custom_inner_attributes, "1.30.0", Some(54726), None),
/// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`.
(active, custom_test_frameworks, "1.30.0", Some(50297), None),
/// Allows using `#[debugger_visualizer]`.
(active, debugger_visualizer, "1.62.0", Some(95939), None),
/// Allows declarative macros 2.0 (`macro`).
(active, decl_macro, "1.17.0", Some(39412), None),
/// Allows rustc to inject a default alloc_error_handler
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
// Unstable attributes:
// ==========================================================================

// RFC #3191: #[debugger_visualizer] support
gated!(
debugger_visualizer, Normal, template!(List: r#"natvis_file = "...""#),
DuplicatesOk, experimental!(debugger_visualizer)
),

// Linking:
gated!(naked, Normal, template!(Word), WarnFollowing, naked_functions, experimental!(naked)),
gated!(
Expand Down

0 comments on commit a7d6768

Please sign in to comment.