Skip to content

Commit 6ff62b2

Browse files
Rollup merge of rust-lang#108714 - estebank:ice_dump, r=oli-obk
On nightly, dump ICE backtraces to disk Implement rust-lang/compiler-team#578. When an ICE is encountered on nightly releases, the new rustc panic handler will also write the contents of the backtrace to disk. If any `delay_span_bug`s are encountered, their backtrace is also added to the file. The platform and rustc version will also be collected. <img width="1032" alt="Screenshot 2023-03-03 at 2 13 25 PM" src="https://user-images.githubusercontent.com/1606434/222842420-8e039740-4042-4563-b31d-599677171acf.png"> The current behavior will *always* write to disk on nightly builds, regardless of whether the backtrace is printed to the terminal, unless the environment variable `RUSTC_ICE_DISK_DUMP` is set to `0`. This is a compromise and can be changed.
2 parents fd68a6d + 6ea7d7d commit 6ff62b2

File tree

48 files changed

+362
-58
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+362
-58
lines changed

Cargo.lock

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3212,6 +3212,7 @@ dependencies = [
32123212
"rustc_trait_selection",
32133213
"rustc_ty_utils",
32143214
"serde_json",
3215+
"time",
32153216
"tracing",
32163217
"windows",
32173218
]
@@ -4815,6 +4816,33 @@ dependencies = [
48154816
name = "tier-check"
48164817
version = "0.1.0"
48174818

4819+
[[package]]
4820+
name = "time"
4821+
version = "0.3.22"
4822+
source = "registry+https://github.com/rust-lang/crates.io-index"
4823+
checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd"
4824+
dependencies = [
4825+
"itoa",
4826+
"serde",
4827+
"time-core",
4828+
"time-macros",
4829+
]
4830+
4831+
[[package]]
4832+
name = "time-core"
4833+
version = "0.1.1"
4834+
source = "registry+https://github.com/rust-lang/crates.io-index"
4835+
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
4836+
4837+
[[package]]
4838+
name = "time-macros"
4839+
version = "0.2.9"
4840+
source = "registry+https://github.com/rust-lang/crates.io-index"
4841+
checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b"
4842+
dependencies = [
4843+
"time-core",
4844+
]
4845+
48184846
[[package]]
48194847
name = "tinystr"
48204848
version = "0.7.1"

compiler/rustc_codegen_ssa/src/back/write.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {
362362

363363
impl<B: WriteBackendMethods> CodegenContext<B> {
364364
pub fn create_diag_handler(&self) -> Handler {
365-
Handler::with_emitter(true, None, Box::new(self.diag_emitter.clone()))
365+
Handler::with_emitter(true, None, Box::new(self.diag_emitter.clone()), None)
366366
}
367367

368368
pub fn config(&self, kind: ModuleKind) -> &ModuleConfig {

compiler/rustc_driver_impl/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition = "2021"
66
[lib]
77

88
[dependencies]
9+
time = { version = "0.3", default-features = false, features = ["formatting", ] }
910
tracing = { version = "0.1.35" }
1011
serde_json = "1.0.59"
1112
rustc_log = { path = "../rustc_log" }

compiler/rustc_driver_impl/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url}
33
driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden
44
55
driver_impl_ice_flags = compiler flags: {$flags}
6+
driver_impl_ice_path = please attach the file at `{$path}` to your bug report
7+
driver_impl_ice_path_error = the ICE couldn't be written to `{$path}`: {$error}
8+
driver_impl_ice_path_error_env = the environment variable `RUSTC_ICE` is set to `{$env_var}`
69
driver_impl_ice_version = rustc {$version} running on {$triple}
10+
711
driver_impl_rlink_empty_version_number = The input does not contain version number
812
913
driver_impl_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}`

compiler/rustc_driver_impl/src/lib.rs

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
88
#![feature(lazy_cell)]
99
#![feature(decl_macro)]
10+
#![feature(ice_to_disk)]
11+
#![feature(let_chains)]
1012
#![recursion_limit = "256"]
1113
#![allow(rustc::potential_query_instability)]
1214
#![deny(rustc::untranslatable_diagnostic)]
@@ -57,8 +59,11 @@ use std::panic::{self, catch_unwind};
5759
use std::path::PathBuf;
5860
use std::process::{self, Command, Stdio};
5961
use std::str;
62+
use std::sync::atomic::{AtomicBool, Ordering};
6063
use std::sync::OnceLock;
61-
use std::time::Instant;
64+
use std::time::{Instant, SystemTime};
65+
use time::format_description::well_known::Rfc3339;
66+
use time::OffsetDateTime;
6267

6368
#[allow(unused_macros)]
6469
macro do_not_use_print($($t:tt)*) {
@@ -297,6 +302,7 @@ fn run_compiler(
297302
input: Input::File(PathBuf::new()),
298303
output_file: ofile,
299304
output_dir: odir,
305+
ice_file: ice_path().clone(),
300306
file_loader,
301307
locale_resources: DEFAULT_LOCALE_RESOURCES,
302308
lint_caps: Default::default(),
@@ -1295,9 +1301,29 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
12951301
}
12961302
}
12971303

1298-
/// Stores the default panic hook, from before [`install_ice_hook`] was called.
1299-
static DEFAULT_HOOK: OnceLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
1300-
OnceLock::new();
1304+
pub static ICE_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();
1305+
1306+
pub fn ice_path() -> &'static Option<PathBuf> {
1307+
ICE_PATH.get_or_init(|| {
1308+
if !rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() {
1309+
return None;
1310+
}
1311+
if let Ok("0") = std::env::var("RUST_BACKTRACE").as_deref() {
1312+
return None;
1313+
}
1314+
let mut path = match std::env::var("RUSTC_ICE").as_deref() {
1315+
// Explicitly opting out of writing ICEs to disk.
1316+
Ok("0") => return None,
1317+
Ok(s) => PathBuf::from(s),
1318+
Err(_) => std::env::current_dir().unwrap_or_default(),
1319+
};
1320+
let now: OffsetDateTime = SystemTime::now().into();
1321+
let file_now = now.format(&Rfc3339).unwrap_or(String::new());
1322+
let pid = std::process::id();
1323+
path.push(format!("rustc-ice-{file_now}-{pid}.txt"));
1324+
Some(path)
1325+
})
1326+
}
13011327

13021328
/// Installs a panic hook that will print the ICE message on unexpected panics.
13031329
///
@@ -1321,8 +1347,6 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler))
13211347
std::env::set_var("RUST_BACKTRACE", "full");
13221348
}
13231349

1324-
let default_hook = DEFAULT_HOOK.get_or_init(panic::take_hook);
1325-
13261350
panic::set_hook(Box::new(move |info| {
13271351
// If the error was caused by a broken pipe then this is not a bug.
13281352
// Write the error and return immediately. See #98700.
@@ -1339,7 +1363,7 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler))
13391363
// Invoke the default handler, which prints the actual panic message and optionally a backtrace
13401364
// Don't do this for delayed bugs, which already emit their own more useful backtrace.
13411365
if !info.payload().is::<rustc_errors::DelayedBugPanic>() {
1342-
(*default_hook)(info);
1366+
std::panic_hook_with_disk_dump(info, ice_path().as_deref());
13431367

13441368
// Separate the output with an empty line
13451369
eprintln!();
@@ -1371,7 +1395,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info:
13711395
false,
13721396
TerminalUrl::No,
13731397
));
1374-
let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
1398+
let handler = rustc_errors::Handler::with_emitter(true, None, emitter, None);
13751399

13761400
// a .span_bug or .bug call has already printed what
13771401
// it wants to print.
@@ -1382,10 +1406,40 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info:
13821406
}
13831407

13841408
handler.emit_note(session_diagnostics::IceBugReport { bug_report_url });
1385-
handler.emit_note(session_diagnostics::IceVersion {
1386-
version: util::version_str!().unwrap_or("unknown_version"),
1387-
triple: config::host_triple(),
1388-
});
1409+
1410+
let version = util::version_str!().unwrap_or("unknown_version");
1411+
let triple = config::host_triple();
1412+
1413+
static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
1414+
1415+
let file = if let Some(path) = ice_path().as_ref() {
1416+
// Create the ICE dump target file.
1417+
match crate::fs::File::options().create(true).append(true).open(&path) {
1418+
Ok(mut file) => {
1419+
handler
1420+
.emit_note(session_diagnostics::IcePath { path: path.display().to_string() });
1421+
if FIRST_PANIC.swap(false, Ordering::SeqCst) {
1422+
let _ = write!(file, "\n\nrustc version: {version}\nplatform: {triple}");
1423+
}
1424+
Some(file)
1425+
}
1426+
Err(err) => {
1427+
// The path ICE couldn't be written to disk, provide feedback to the user as to why.
1428+
handler.emit_warning(session_diagnostics::IcePathError {
1429+
path: path.display().to_string(),
1430+
error: err.to_string(),
1431+
env_var: std::env::var("RUSTC_ICE")
1432+
.ok()
1433+
.map(|env_var| session_diagnostics::IcePathErrorEnv { env_var }),
1434+
});
1435+
handler.emit_note(session_diagnostics::IceVersion { version, triple });
1436+
None
1437+
}
1438+
}
1439+
} else {
1440+
handler.emit_note(session_diagnostics::IceVersion { version, triple });
1441+
None
1442+
};
13891443

13901444
if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
13911445
handler.emit_note(session_diagnostics::IceFlags { flags: flags.join(" ") });
@@ -1399,7 +1453,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info:
13991453

14001454
let num_frames = if backtrace { None } else { Some(2) };
14011455

1402-
interface::try_print_query_stack(&handler, num_frames);
1456+
interface::try_print_query_stack(&handler, num_frames, file);
14031457

14041458
// We don't trust this callback not to panic itself, so run it at the end after we're sure we've
14051459
// printed all the relevant info.

compiler/rustc_driver_impl/src/session_diagnostics.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_macros::Diagnostic;
1+
use rustc_macros::{Diagnostic, Subdiagnostic};
22

33
#[derive(Diagnostic)]
44
#[diag(driver_impl_rlink_unable_to_read)]
@@ -56,6 +56,27 @@ pub(crate) struct IceVersion<'a> {
5656
pub triple: &'a str,
5757
}
5858

59+
#[derive(Diagnostic)]
60+
#[diag(driver_impl_ice_path)]
61+
pub(crate) struct IcePath {
62+
pub path: String,
63+
}
64+
65+
#[derive(Diagnostic)]
66+
#[diag(driver_impl_ice_path_error)]
67+
pub(crate) struct IcePathError {
68+
pub path: String,
69+
pub error: String,
70+
#[subdiagnostic]
71+
pub env_var: Option<IcePathErrorEnv>,
72+
}
73+
74+
#[derive(Subdiagnostic)]
75+
#[note(driver_impl_ice_path_error_env)]
76+
pub(crate) struct IcePathErrorEnv {
77+
pub env_var: String,
78+
}
79+
5980
#[derive(Diagnostic)]
6081
#[diag(driver_impl_ice_flags)]
6182
pub(crate) struct IceFlags {

compiler/rustc_error_messages/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,13 @@ impl DiagnosticMessage {
355355
}
356356
}
357357
}
358+
359+
pub fn as_str(&self) -> Option<&str> {
360+
match self {
361+
DiagnosticMessage::Eager(s) | DiagnosticMessage::Str(s) => Some(s),
362+
DiagnosticMessage::FluentIdentifier(_, _) => None,
363+
}
364+
}
358365
}
359366

360367
impl From<String> for DiagnosticMessage {

compiler/rustc_errors/src/json/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
6464
);
6565

6666
let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1));
67-
let handler = Handler::with_emitter(true, None, Box::new(je));
67+
let handler = Handler::with_emitter(true, None, Box::new(je), None);
6868
handler.span_err(span, "foo");
6969

7070
let bytes = output.lock().unwrap();

compiler/rustc_errors/src/lib.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ use std::borrow::Cow;
4747
use std::error::Report;
4848
use std::fmt;
4949
use std::hash::Hash;
50+
use std::io::Write;
5051
use std::num::NonZeroUsize;
5152
use std::panic;
52-
use std::path::Path;
53+
use std::path::{Path, PathBuf};
5354

5455
use termcolor::{Color, ColorSpec};
5556

@@ -461,6 +462,10 @@ struct HandlerInner {
461462
///
462463
/// [RFC-2383]: https://rust-lang.github.io/rfcs/2383-lint-reasons.html
463464
fulfilled_expectations: FxHashSet<LintExpectationId>,
465+
466+
/// The file where the ICE information is stored. This allows delayed_span_bug backtraces to be
467+
/// stored along side the main panic backtrace.
468+
ice_file: Option<PathBuf>,
464469
}
465470

466471
/// A key denoting where from a diagnostic was stashed.
@@ -550,13 +555,15 @@ impl Handler {
550555
sm: Option<Lrc<SourceMap>>,
551556
fluent_bundle: Option<Lrc<FluentBundle>>,
552557
fallback_bundle: LazyFallbackBundle,
558+
ice_file: Option<PathBuf>,
553559
) -> Self {
554560
Self::with_tty_emitter_and_flags(
555561
color_config,
556562
sm,
557563
fluent_bundle,
558564
fallback_bundle,
559565
HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() },
566+
ice_file,
560567
)
561568
}
562569

@@ -566,6 +573,7 @@ impl Handler {
566573
fluent_bundle: Option<Lrc<FluentBundle>>,
567574
fallback_bundle: LazyFallbackBundle,
568575
flags: HandlerFlags,
576+
ice_file: Option<PathBuf>,
569577
) -> Self {
570578
let emitter = Box::new(EmitterWriter::stderr(
571579
color_config,
@@ -579,23 +587,26 @@ impl Handler {
579587
flags.track_diagnostics,
580588
TerminalUrl::No,
581589
));
582-
Self::with_emitter_and_flags(emitter, flags)
590+
Self::with_emitter_and_flags(emitter, flags, ice_file)
583591
}
584592

585593
pub fn with_emitter(
586594
can_emit_warnings: bool,
587595
treat_err_as_bug: Option<NonZeroUsize>,
588596
emitter: Box<dyn Emitter + sync::Send>,
597+
ice_file: Option<PathBuf>,
589598
) -> Self {
590599
Handler::with_emitter_and_flags(
591600
emitter,
592601
HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() },
602+
ice_file,
593603
)
594604
}
595605

596606
pub fn with_emitter_and_flags(
597607
emitter: Box<dyn Emitter + sync::Send>,
598608
flags: HandlerFlags,
609+
ice_file: Option<PathBuf>,
599610
) -> Self {
600611
Self {
601612
flags,
@@ -618,6 +629,7 @@ impl Handler {
618629
check_unstable_expect_diagnostics: false,
619630
unstable_expect_diagnostics: Vec::new(),
620631
fulfilled_expectations: Default::default(),
632+
ice_file,
621633
}),
622634
}
623635
}
@@ -1657,8 +1669,21 @@ impl HandlerInner {
16571669
explanation: impl Into<DiagnosticMessage> + Copy,
16581670
) {
16591671
let mut no_bugs = true;
1672+
// If backtraces are enabled, also print the query stack
1673+
let backtrace = std::env::var_os("RUST_BACKTRACE").map_or(true, |x| &x != "0");
16601674
for bug in bugs {
1661-
let mut bug = bug.decorate();
1675+
if let Some(file) = self.ice_file.as_ref()
1676+
&& let Ok(mut out) = std::fs::File::options().append(true).open(file)
1677+
{
1678+
let _ = write!(
1679+
&mut out,
1680+
"\n\ndelayed span bug: {}\n{}",
1681+
bug.inner.styled_message().iter().filter_map(|(msg, _)| msg.as_str()).collect::<String>(),
1682+
&bug.note
1683+
);
1684+
}
1685+
let mut bug =
1686+
if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner };
16621687

16631688
if no_bugs {
16641689
// Put the overall explanation before the `DelayedBug`s, to

compiler/rustc_expand/src/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &
161161
false,
162162
TerminalUrl::No,
163163
);
164-
let handler = Handler::with_emitter(true, None, Box::new(emitter));
164+
let handler = Handler::with_emitter(true, None, Box::new(emitter), None);
165165
#[allow(rustc::untranslatable_diagnostic)]
166166
handler.span_err(msp, "foo");
167167

0 commit comments

Comments
 (0)