Skip to content

Commit 175a4ea

Browse files
Add support for a new attribute #[debugger_visualizer] to support embedding debugger visualizers into a generated PDB.
Cleanup `DebuggerVisualizerFile` type and other minor cleanup of queries. Merge the queries for debugger visualizers into a single query. Revert move of `resolve_path` to `rustc_builtin_macros`. Update dependencies in Cargo.toml for `rustc_passes`. Respond to PR comments. Load visualizer files into opaque bytes `Vec<u8>`. Debugger visualizers for dynamically linked crates should not be embedded in the current crate. Update the unstable book with the new feature. Add the tracking issue for the debugger_visualizer feature. Respond to PR comments and minor cleanups.
1 parent e1df625 commit 175a4ea

29 files changed

+554
-76
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4183,6 +4183,7 @@ dependencies = [
41834183
"rustc_attr",
41844184
"rustc_data_structures",
41854185
"rustc_errors",
4186+
"rustc_expand",
41864187
"rustc_feature",
41874188
"rustc_hir",
41884189
"rustc_index",

compiler/rustc_builtin_macros/src/source_util.rs

+4-43
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,15 @@ use rustc_ast::ptr::P;
33
use rustc_ast::token;
44
use rustc_ast::tokenstream::TokenStream;
55
use rustc_ast_pretty::pprust;
6-
use rustc_errors::PResult;
76
use rustc_expand::base::{self, *};
87
use rustc_expand::module::DirOwnership;
98
use rustc_parse::parser::{ForceCollect, Parser};
109
use rustc_parse::{self, new_parser_from_file};
1110
use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
1211
use rustc_span::symbol::Symbol;
13-
use rustc_span::{self, FileName, Pos, Span};
12+
use rustc_span::{self, Pos, Span};
1413

1514
use smallvec::SmallVec;
16-
use std::path::PathBuf;
1715
use std::rc::Rc;
1816

1917
// These macros all relate to the file system; they either return
@@ -104,7 +102,7 @@ pub fn expand_include<'cx>(
104102
return DummyResult::any(sp);
105103
};
106104
// The file will be added to the code map by the parser
107-
let file = match resolve_path(cx, file.as_str(), sp) {
105+
let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
108106
Ok(f) => f,
109107
Err(mut err) => {
110108
err.emit();
@@ -176,7 +174,7 @@ pub fn expand_include_str(
176174
let Some(file) = get_single_str_from_tts(cx, sp, tts, "include_str!") else {
177175
return DummyResult::any(sp);
178176
};
179-
let file = match resolve_path(cx, file.as_str(), sp) {
177+
let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
180178
Ok(f) => f,
181179
Err(mut err) => {
182180
err.emit();
@@ -210,7 +208,7 @@ pub fn expand_include_bytes(
210208
let Some(file) = get_single_str_from_tts(cx, sp, tts, "include_bytes!") else {
211209
return DummyResult::any(sp);
212210
};
213-
let file = match resolve_path(cx, file.as_str(), sp) {
211+
let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
214212
Ok(f) => f,
215213
Err(mut err) => {
216214
err.emit();
@@ -225,40 +223,3 @@ pub fn expand_include_bytes(
225223
}
226224
}
227225
}
228-
229-
/// Resolves a `path` mentioned inside Rust code, returning an absolute path.
230-
///
231-
/// This unifies the logic used for resolving `include_X!`.
232-
fn resolve_path<'a>(
233-
cx: &mut ExtCtxt<'a>,
234-
path: impl Into<PathBuf>,
235-
span: Span,
236-
) -> PResult<'a, PathBuf> {
237-
let path = path.into();
238-
239-
// Relative paths are resolved relative to the file in which they are found
240-
// after macro expansion (that is, they are unhygienic).
241-
if !path.is_absolute() {
242-
let callsite = span.source_callsite();
243-
let mut result = match cx.source_map().span_to_filename(callsite) {
244-
FileName::Real(name) => name
245-
.into_local_path()
246-
.expect("attempting to resolve a file path in an external file"),
247-
FileName::DocTest(path, _) => path,
248-
other => {
249-
return Err(cx.struct_span_err(
250-
span,
251-
&format!(
252-
"cannot resolve relative path in non-file source `{}`",
253-
cx.source_map().filename_for_diagnostics(&other)
254-
),
255-
));
256-
}
257-
};
258-
result.pop();
259-
result.push(path);
260-
Ok(result)
261-
} else {
262-
Ok(path)
263-
}
264-
}

compiler/rustc_codegen_ssa/src/back/link.rs

+49-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_data_structures::memmap::Mmap;
55
use rustc_data_structures::temp_dir::MaybeTempDir;
66
use rustc_errors::{ErrorGuaranteed, Handler};
77
use rustc_fs_util::fix_windows_verbatim_for_gcc;
8-
use rustc_hir::def_id::CrateNum;
8+
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
99
use rustc_middle::middle::dependency_format::Linkage;
1010
use rustc_middle::middle::exported_symbols::SymbolExportKind;
1111
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
@@ -2099,8 +2099,14 @@ fn add_order_independent_options(
20992099
// Pass optimization flags down to the linker.
21002100
cmd.optimize();
21012101

2102+
let debugger_visualizer_paths = if sess.target.is_like_msvc {
2103+
collect_debugger_visualizers(tmpdir, sess, &codegen_results.crate_info)
2104+
} else {
2105+
Vec::new()
2106+
};
2107+
21022108
// Pass debuginfo and strip flags down to the linker.
2103-
cmd.debuginfo(strip_value(sess));
2109+
cmd.debuginfo(strip_value(sess), &debugger_visualizer_paths);
21042110

21052111
// We want to prevent the compiler from accidentally leaking in any system libraries,
21062112
// so by default we tell linkers not to link to any default libraries.
@@ -2119,6 +2125,47 @@ fn add_order_independent_options(
21192125
add_rpath_args(cmd, sess, codegen_results, out_filename);
21202126
}
21212127

2128+
// Write the debugger visualizer files for each crate to the temp directory and gather the file paths.
2129+
fn collect_debugger_visualizers(
2130+
tmpdir: &Path,
2131+
sess: &Session,
2132+
crate_info: &CrateInfo,
2133+
) -> Vec<PathBuf> {
2134+
let mut visualizer_paths = Vec::new();
2135+
let debugger_visualizers = &crate_info.debugger_visualizers;
2136+
let mut index = 0;
2137+
2138+
for (&cnum, visualizers) in debugger_visualizers {
2139+
let crate_name = if cnum == LOCAL_CRATE {
2140+
crate_info.local_crate_name.as_str()
2141+
} else {
2142+
crate_info.crate_name[&cnum].as_str()
2143+
};
2144+
2145+
for visualizer in visualizers {
2146+
let visualizer_out_file = tmpdir.join(format!("{}-{}.natvis", crate_name, index));
2147+
2148+
match fs::write(&visualizer_out_file, &visualizer.src) {
2149+
Ok(()) => {
2150+
visualizer_paths.push(visualizer_out_file.clone());
2151+
index += 1;
2152+
}
2153+
Err(error) => {
2154+
sess.warn(
2155+
format!(
2156+
"Unable to write debugger visualizer file `{}`: {} ",
2157+
visualizer_out_file.display(),
2158+
error
2159+
)
2160+
.as_str(),
2161+
);
2162+
}
2163+
};
2164+
}
2165+
}
2166+
visualizer_paths
2167+
}
2168+
21222169
/// # Native library linking
21232170
///
21242171
/// User-supplied library search paths (-L on the command line). These are the same paths used to

compiler/rustc_codegen_ssa/src/back/linker.rs

+15-8
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ pub trait Linker {
183183
fn optimize(&mut self);
184184
fn pgo_gen(&mut self);
185185
fn control_flow_guard(&mut self);
186-
fn debuginfo(&mut self, strip: Strip);
186+
fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]);
187187
fn no_crt_objects(&mut self);
188188
fn no_default_libraries(&mut self);
189189
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]);
@@ -611,7 +611,7 @@ impl<'a> Linker for GccLinker<'a> {
611611

612612
fn control_flow_guard(&mut self) {}
613613

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

918-
fn debuginfo(&mut self, strip: Strip) {
918+
fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]) {
919919
match strip {
920920
Strip::None => {
921921
// This will cause the Microsoft linker to generate a PDB file
@@ -942,6 +942,13 @@ impl<'a> Linker for MsvcLinker<'a> {
942942
}
943943
}
944944
}
945+
946+
// This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file
947+
for path in debugger_visualizers {
948+
let mut arg = OsString::from("/NATVIS:");
949+
arg.push(path);
950+
self.cmd.arg(arg);
951+
}
945952
}
946953
Strip::Debuginfo | Strip::Symbols => {
947954
self.cmd.arg("/DEBUG:NONE");
@@ -1124,7 +1131,7 @@ impl<'a> Linker for EmLinker<'a> {
11241131

11251132
fn control_flow_guard(&mut self) {}
11261133

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

13161323
fn pgo_gen(&mut self) {}
13171324

1318-
fn debuginfo(&mut self, strip: Strip) {
1325+
fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
13191326
match strip {
13201327
Strip::None => {}
13211328
Strip::Debuginfo => {
@@ -1450,7 +1457,7 @@ impl<'a> Linker for L4Bender<'a> {
14501457

14511458
fn pgo_gen(&mut self) {}
14521459

1453-
fn debuginfo(&mut self, strip: Strip) {
1460+
fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
14541461
match strip {
14551462
Strip::None => {}
14561463
Strip::Debuginfo => {
@@ -1600,7 +1607,7 @@ impl<'a> Linker for PtxLinker<'a> {
16001607
self.cmd.arg("-L").arg(path);
16011608
}
16021609

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

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

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

compiler/rustc_codegen_ssa/src/base.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,13 @@ impl CrateInfo {
847847
missing_lang_items: Default::default(),
848848
dependency_formats: tcx.dependency_formats(()).clone(),
849849
windows_subsystem,
850+
debugger_visualizers: Default::default(),
850851
};
852+
let debugger_visualizers = tcx.debugger_visualizers(LOCAL_CRATE).clone();
853+
if !debugger_visualizers.is_empty() {
854+
info.debugger_visualizers.insert(LOCAL_CRATE, debugger_visualizers);
855+
}
856+
851857
let lang_items = tcx.lang_items();
852858

853859
let crates = tcx.crates(());
@@ -862,7 +868,9 @@ impl CrateInfo {
862868
info.native_libraries
863869
.insert(cnum, tcx.native_libraries(cnum).iter().map(Into::into).collect());
864870
info.crate_name.insert(cnum, tcx.crate_name(cnum));
865-
info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum).clone());
871+
872+
let used_crate_source = tcx.used_crate_source(cnum);
873+
info.used_crate_source.insert(cnum, used_crate_source.clone());
866874
if tcx.is_compiler_builtins(cnum) {
867875
info.compiler_builtins = Some(cnum);
868876
}
@@ -883,6 +891,14 @@ impl CrateInfo {
883891
let missing =
884892
missing.iter().cloned().filter(|&l| lang_items::required(tcx, l)).collect();
885893
info.missing_lang_items.insert(cnum, missing);
894+
895+
// Only include debugger visualizer files from crates that will be statically linked.
896+
if used_crate_source.rlib.is_some() || used_crate_source.rmeta.is_some() {
897+
let debugger_visualizers = tcx.debugger_visualizers(cnum).clone();
898+
if !debugger_visualizers.is_empty() {
899+
info.debugger_visualizers.insert(cnum, debugger_visualizers);
900+
}
901+
}
886902
}
887903

888904
info

compiler/rustc_codegen_ssa/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT
3636
use rustc_session::cstore::{self, CrateSource};
3737
use rustc_session::utils::NativeLibKind;
3838
use rustc_span::symbol::Symbol;
39+
use rustc_span::DebuggerVisualizerFile;
3940
use std::path::{Path, PathBuf};
4041

4142
pub mod back;
@@ -157,6 +158,7 @@ pub struct CrateInfo {
157158
pub missing_lang_items: FxHashMap<CrateNum, Vec<LangItem>>,
158159
pub dependency_formats: Lrc<Dependencies>,
159160
pub windows_subsystem: Option<String>,
161+
pub debugger_visualizers: FxHashMap<CrateNum, Vec<DebuggerVisualizerFile>>,
160162
}
161163

162164
#[derive(Encodable, Decodable)]

compiler/rustc_expand/src/base.rs

+39-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind};
1010
use rustc_attr::{self as attr, Deprecation, Stability};
1111
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1212
use rustc_data_structures::sync::{self, Lrc};
13-
use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
13+
use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult};
1414
use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
1515
use rustc_lint_defs::BuiltinLintDiagnostics;
1616
use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS};
@@ -20,7 +20,7 @@ use rustc_span::edition::Edition;
2020
use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId};
2121
use rustc_span::source_map::SourceMap;
2222
use rustc_span::symbol::{kw, sym, Ident, Symbol};
23-
use rustc_span::{Span, DUMMY_SP};
23+
use rustc_span::{FileName, Span, DUMMY_SP};
2424
use smallvec::{smallvec, SmallVec};
2525

2626
use std::default::Default;
@@ -1136,6 +1136,43 @@ impl<'a> ExtCtxt<'a> {
11361136
}
11371137
}
11381138

1139+
/// Resolves a `path` mentioned inside Rust code, returning an absolute path.
1140+
///
1141+
/// This unifies the logic used for resolving `include_X!`.
1142+
pub fn resolve_path(
1143+
parse_sess: &ParseSess,
1144+
path: impl Into<PathBuf>,
1145+
span: Span,
1146+
) -> PResult<'_, PathBuf> {
1147+
let path = path.into();
1148+
1149+
// Relative paths are resolved relative to the file in which they are found
1150+
// after macro expansion (that is, they are unhygienic).
1151+
if !path.is_absolute() {
1152+
let callsite = span.source_callsite();
1153+
let mut result = match parse_sess.source_map().span_to_filename(callsite) {
1154+
FileName::Real(name) => name
1155+
.into_local_path()
1156+
.expect("attempting to resolve a file path in an external file"),
1157+
FileName::DocTest(path, _) => path,
1158+
other => {
1159+
return Err(parse_sess.span_diagnostic.struct_span_err(
1160+
span,
1161+
&format!(
1162+
"cannot resolve relative path in non-file source `{}`",
1163+
parse_sess.source_map().filename_for_diagnostics(&other)
1164+
),
1165+
));
1166+
}
1167+
};
1168+
result.pop();
1169+
result.push(path);
1170+
Ok(result)
1171+
} else {
1172+
Ok(path)
1173+
}
1174+
}
1175+
11391176
/// Extracts a string literal from the macro expanded version of `expr`,
11401177
/// returning a diagnostic error of `err_msg` if `expr` is not a string literal.
11411178
/// The returned bool indicates whether an applicable suggestion has already been

compiler/rustc_feature/src/active.rs

+2
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,8 @@ declare_features! (
358358
(active, custom_inner_attributes, "1.30.0", Some(54726), None),
359359
/// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`.
360360
(active, custom_test_frameworks, "1.30.0", Some(50297), None),
361+
/// Allows using `#[debugger_visualizer]`.
362+
(active, debugger_visualizer, "1.62.0", Some(95939), None),
361363
/// Allows declarative macros 2.0 (`macro`).
362364
(active, decl_macro, "1.17.0", Some(39412), None),
363365
/// Allows rustc to inject a default alloc_error_handler

compiler/rustc_feature/src/builtin_attrs.rs

+6
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
379379
// Unstable attributes:
380380
// ==========================================================================
381381

382+
// RFC #3191: #[debugger_visualizer] support
383+
gated!(
384+
debugger_visualizer, Normal, template!(List: r#"natvis_file = "...""#),
385+
DuplicatesOk, experimental!(debugger_visualizer)
386+
),
387+
382388
// Linking:
383389
gated!(naked, Normal, template!(Word), WarnFollowing, naked_functions, experimental!(naked)),
384390
gated!(

0 commit comments

Comments
 (0)