Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new Rust attribute to support embedding debugger visualizers #91779

Merged
merged 2 commits into from
May 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4183,6 +4183,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 @@ -36,6 +36,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 @@ -157,6 +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>>,
}

#[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
Loading