Skip to content

Commit 5212c75

Browse files
committed
Add -Zerror-metrics=PATH to save diagnostic metadata to disk
1 parent 2b78d92 commit 5212c75

File tree

4 files changed

+103
-23
lines changed

4 files changed

+103
-23
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Session.vim
1919
*.iml
2020
.vscode
2121
.project
22+
.vim/
2223
.favorites.json
2324
.settings/
2425
.vs/

compiler/rustc_driver_impl/src/lib.rs

+29-8
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ use rustc_metadata::creader::MetadataLoader;
5151
use rustc_metadata::locator;
5252
use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
5353
use rustc_session::config::{
54-
nightly_options, ErrorOutputType, Input, OutFileName, OutputType, CG_OPTIONS, Z_OPTIONS,
54+
nightly_options, ErrorOutputType, Input, OutFileName, OutputType, UnstableOptions, CG_OPTIONS,
55+
Z_OPTIONS,
5556
};
5657
use rustc_session::getopts::{self, Matches};
5758
use rustc_session::lint::{Lint, LintId};
@@ -301,6 +302,8 @@ fn run_compiler(
301302
let Some(matches) = handle_options(&default_early_dcx, &args) else { return Ok(()) };
302303

303304
let sopts = config::build_session_options(&mut default_early_dcx, &matches);
305+
// fully initialize ice path static once unstable options are available as context
306+
let ice_file = ice_path_with_config(Some(&sopts.unstable_opts)).clone();
304307

305308
if let Some(ref code) = matches.opt_str("explain") {
306309
handle_explain(&default_early_dcx, diagnostics_registry(), code, sopts.color);
@@ -315,7 +318,7 @@ fn run_compiler(
315318
input: Input::File(PathBuf::new()),
316319
output_file: ofile,
317320
output_dir: odir,
318-
ice_file: ice_path().clone(),
321+
ice_file,
319322
file_loader,
320323
locale_resources: DEFAULT_LOCALE_RESOURCES,
321324
lint_caps: Default::default(),
@@ -1306,25 +1309,43 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
13061309

13071310
static ICE_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();
13081311

1312+
// This function should only be called from the ICE hook.
1313+
//
1314+
// The intended behavior is that `run_compiler` will invoke `ice_path_with_config` early in the
1315+
// initialization process to properly initialize the ICE_PATH static based on parsed CLI flags.
1316+
//
1317+
// Subsequent calls to either function will then return the proper ICE path as configured by
1318+
// the environment and cli flags
13091319
fn ice_path() -> &'static Option<PathBuf> {
1320+
ice_path_with_config(None)
1321+
}
1322+
1323+
fn ice_path_with_config(config: Option<&UnstableOptions>) -> &'static Option<PathBuf> {
1324+
if ICE_PATH.get().is_some() && config.is_some() && cfg!(debug_assertions) {
1325+
tracing::warn!(
1326+
"ICE_PATH has already been initialized -- files may be emitted at unintended paths"
1327+
)
1328+
}
1329+
13101330
ICE_PATH.get_or_init(|| {
13111331
if !rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() {
13121332
return None;
13131333
}
1314-
if let Some(s) = std::env::var_os("RUST_BACKTRACE")
1315-
&& s == "0"
1316-
{
1317-
return None;
1318-
}
13191334
let mut path = match std::env::var_os("RUSTC_ICE") {
13201335
Some(s) => {
13211336
if s == "0" {
13221337
// Explicitly opting out of writing ICEs to disk.
13231338
return None;
13241339
}
1340+
if let Some(unstable_opts) = config && unstable_opts.metrics_dir.is_some() {
1341+
tracing::warn!("ignoring -Zerror-metrics in favor of RUSTC_ICE for destination of ICE report files");
1342+
}
13251343
PathBuf::from(s)
13261344
}
1327-
None => std::env::current_dir().unwrap_or_default(),
1345+
None => config
1346+
.and_then(|unstable_opts| unstable_opts.metrics_dir.to_owned())
1347+
.or_else(|| std::env::current_dir().ok())
1348+
.unwrap_or_default(),
13281349
};
13291350
let now: OffsetDateTime = SystemTime::now().into();
13301351
let file_now = now

compiler/rustc_session/src/options.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1827,6 +1827,8 @@ options! {
18271827
the same values as the target option of the same name"),
18281828
meta_stats: bool = (false, parse_bool, [UNTRACKED],
18291829
"gather metadata statistics (default: no)"),
1830+
metrics_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
1831+
"stores metrics about the errors being emitted by rustc to disk"),
18301832
mir_emit_retag: bool = (false, parse_bool, [TRACKED],
18311833
"emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
18321834
(default: no)"),

tests/run-make/dump-ice-to-disk/rmake.rs

+71-15
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@
44
// or full.
55
// - Check that disabling ICE logging results in zero files created.
66
// - Check that the ICE files contain some of the expected strings.
7+
// - exercise the -Zmetrics-dir nightly flag
8+
// - verify what happens when both the nightly flag and env variable are set
9+
// - test the RUST_BACKTRACE=0 behavior against the file creation
10+
711
// See https://github.com/rust-lang/rust/pull/108714
812

913
use run_make_support::{cwd, has_extension, has_prefix, rfs, rustc, shallow_find_files};
1014

1115
fn main() {
1216
rustc().input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail();
1317
let default = get_text_from_ice(".").lines().count();
14-
clear_ice_files();
1518

19+
clear_ice_files();
1620
rustc().env("RUSTC_ICE", cwd()).input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail();
1721
let ice_text = get_text_from_ice(cwd());
1822
let default_set = ice_text.lines().count();
@@ -25,7 +29,28 @@ fn main() {
2529
ice_files.first().and_then(|f| f.file_name()).and_then(|n| n.to_str()).unwrap();
2630
// Ensure that the ICE dump path doesn't contain `:`, because they cause problems on Windows.
2731
assert!(!ice_file_name.contains(":"), "{ice_file_name}");
32+
assert_eq!(default, default_set);
33+
assert!(default > 0);
34+
// Some of the expected strings in an ICE file should appear.
35+
assert!(content.contains("thread 'rustc' panicked at"));
36+
assert!(content.contains("stack backtrace:"));
37+
38+
test_backtrace_short(default);
39+
test_backtrace_full(default);
40+
test_backtrace_disabled(default);
41+
42+
clear_ice_files();
43+
// The ICE dump is explicitly disabled. Therefore, this should produce no files.
44+
rustc().env("RUSTC_ICE", "0").input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail();
45+
let ice_files = shallow_find_files(cwd(), |path| {
46+
has_prefix(path, "rustc-ice") && has_extension(path, "txt")
47+
});
48+
assert!(ice_files.is_empty()); // There should be 0 ICE files.
49+
50+
metrics_dir(default);
51+
}
2852

53+
fn test_backtrace_short(baseline: usize) {
2954
clear_ice_files();
3055
rustc()
3156
.env("RUSTC_ICE", cwd())
@@ -34,6 +59,11 @@ fn main() {
3459
.arg("-Ztreat-err-as-bug=1")
3560
.run_fail();
3661
let short = get_text_from_ice(cwd()).lines().count();
62+
// backtrace length in dump shouldn't be changed by RUST_BACKTRACE
63+
assert_eq!(short, baseline);
64+
}
65+
66+
fn test_backtrace_full(baseline: usize) {
3767
clear_ice_files();
3868
rustc()
3969
.env("RUSTC_ICE", cwd())
@@ -42,23 +72,49 @@ fn main() {
4272
.arg("-Ztreat-err-as-bug=1")
4373
.run_fail();
4474
let full = get_text_from_ice(cwd()).lines().count();
75+
// backtrace length in dump shouldn't be changed by RUST_BACKTRACE
76+
assert_eq!(full, baseline);
77+
}
78+
79+
fn test_backtrace_disabled(baseline: usize) {
4580
clear_ice_files();
81+
rustc()
82+
.env("RUSTC_ICE", cwd())
83+
.input("lib.rs")
84+
.env("RUST_BACKTRACE", "0")
85+
.arg("-Ztreat-err-as-bug=1")
86+
.run_fail();
87+
let disabled = get_text_from_ice(cwd()).lines().count();
88+
// backtrace length in dump shouldn't be changed by RUST_BACKTRACE
89+
assert_eq!(disabled, baseline);
90+
}
4691

47-
// The ICE dump is explicitly disabled. Therefore, this should produce no files.
48-
rustc().env("RUSTC_ICE", "0").input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail();
49-
let ice_files = shallow_find_files(cwd(), |path| {
50-
has_prefix(path, "rustc-ice") && has_extension(path, "txt")
51-
});
52-
assert!(ice_files.is_empty()); // There should be 0 ICE files.
92+
fn metrics_dir(baseline: usize) {
93+
test_flag_only(baseline);
94+
test_flag_and_env(baseline);
95+
}
5396

54-
// The line count should not change.
55-
assert_eq!(short, default_set);
56-
assert_eq!(short, default);
57-
assert_eq!(full, default_set);
58-
assert!(default > 0);
59-
// Some of the expected strings in an ICE file should appear.
60-
assert!(content.contains("thread 'rustc' panicked at"));
61-
assert!(content.contains("stack backtrace:"));
97+
fn test_flag_only(baseline: usize) {
98+
clear_ice_files();
99+
let metrics_arg = format!("-Zmetrics-dir={}", cwd().display());
100+
rustc().input("lib.rs").arg("-Ztreat-err-as-bug=1").arg(metrics_arg).run_fail();
101+
let output = get_text_from_ice(cwd()).lines().count();
102+
assert_eq!(output, baseline);
103+
}
104+
105+
fn test_flag_and_env(baseline: usize) {
106+
clear_ice_files();
107+
let metrics_arg = format!("-Zmetrics-dir={}", cwd().display());
108+
let real_dir = cwd().join("actually_put_ice_here");
109+
rfs::create_dir(real_dir.clone());
110+
rustc()
111+
.input("lib.rs")
112+
.env("RUSTC_ICE", real_dir.clone())
113+
.arg("-Ztreat-err-as-bug=1")
114+
.arg(metrics_arg)
115+
.run_fail();
116+
let output = get_text_from_ice(real_dir).lines().count();
117+
assert_eq!(output, baseline);
62118
}
63119

64120
fn clear_ice_files() {

0 commit comments

Comments
 (0)