Skip to content

Commit 700ba81

Browse files
committed
Teach rustc to write the backtrace and query stack to disk.
* On non-`-dev` builds, always hide the backtrace and query stack. * Introduce `RUSTC_BACKTRACE_FORCE` where `1` will always print the backtrace and `0` will always hide it.
1 parent 0b90256 commit 700ba81

File tree

8 files changed

+159
-36
lines changed

8 files changed

+159
-36
lines changed

Cargo.lock

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3915,6 +3915,7 @@ dependencies = [
39153915
name = "rustc_driver"
39163916
version = "0.0.0"
39173917
dependencies = [
3918+
"backtrace",
39183919
"libc",
39193920
"rustc_ast",
39203921
"rustc_ast_pretty",
@@ -3934,11 +3935,14 @@ dependencies = [
39343935
"rustc_middle",
39353936
"rustc_parse",
39363937
"rustc_plugin_impl",
3938+
"rustc_query_impl",
3939+
"rustc_query_system",
39373940
"rustc_save_analysis",
39383941
"rustc_session",
39393942
"rustc_span",
39403943
"rustc_target",
39413944
"serde_json",
3945+
"time 0.3.17",
39423946
"tracing",
39433947
"winapi",
39443948
]

compiler/rustc_driver/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ edition = "2021"
77
crate-type = ["dylib"]
88

99
[dependencies]
10-
tracing = { version = "0.1.35" }
10+
backtrace = "0.3.66"
1111
serde_json = "1.0.59"
12+
time = { version = "0.3.17", features = ["std", "formatting"] }
13+
tracing = { version = "0.1.35" }
1214
rustc_log = { path = "../rustc_log" }
1315
rustc_middle = { path = "../rustc_middle" }
1416
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
@@ -30,6 +32,8 @@ rustc_error_codes = { path = "../rustc_error_codes" }
3032
rustc_interface = { path = "../rustc_interface" }
3133
rustc_ast = { path = "../rustc_ast" }
3234
rustc_span = { path = "../rustc_span" }
35+
rustc_query_system = { path = "../rustc_query_system" }
36+
rustc_query_impl = { path = "../rustc_query_impl" }
3337
rustc_hir_analysis = { path = "../rustc_hir_analysis" }
3438

3539
[target.'cfg(unix)'.dependencies]

compiler/rustc_driver/src/lib.rs

Lines changed: 145 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#![feature(is_terminal)]
99
#![feature(once_cell)]
1010
#![feature(decl_macro)]
11+
#![feature(panic_info_message)]
1112
#![recursion_limit = "256"]
1213
#![allow(rustc::potential_query_instability)]
1314
#![deny(rustc::untranslatable_diagnostic)]
@@ -30,6 +31,8 @@ use rustc_interface::util::{self, collect_crate_types, get_codegen_backend};
3031
use rustc_interface::{interface, Queries};
3132
use rustc_lint::LintStore;
3233
use rustc_metadata::locator;
34+
use rustc_query_impl::QueryCtxt;
35+
use rustc_query_system::query::QueryContext;
3336
use rustc_save_analysis as save;
3437
use rustc_save_analysis::DumpHandler;
3538
use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS};
@@ -45,7 +48,6 @@ use rustc_target::json::ToJson;
4548

4649
use std::borrow::Cow;
4750
use std::cmp::max;
48-
use std::env;
4951
use std::ffi::OsString;
5052
use std::fs;
5153
use std::io::{self, IsTerminal, Read, Write};
@@ -55,6 +57,7 @@ use std::process::{self, Command, Stdio};
5557
use std::str;
5658
use std::sync::LazyLock;
5759
use std::time::Instant;
60+
use std::{backtrace, env};
5861

5962
pub mod args;
6063
pub mod pretty;
@@ -1181,6 +1184,102 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
11811184
}
11821185
}
11831186

1187+
struct IceError;
1188+
impl From<time::error::InvalidFormatDescription> for IceError {
1189+
fn from(_: time::error::InvalidFormatDescription) -> IceError {
1190+
IceError
1191+
}
1192+
}
1193+
impl From<time::error::Format> for IceError {
1194+
fn from(_: time::error::Format) -> IceError {
1195+
IceError
1196+
}
1197+
}
1198+
impl From<std::io::Error> for IceError {
1199+
fn from(_: std::io::Error) -> IceError {
1200+
IceError
1201+
}
1202+
}
1203+
1204+
fn write_ice_to_disk(info: &panic::PanicInfo<'_>) -> Result<String, IceError> {
1205+
let capture = backtrace::Backtrace::force_capture();
1206+
let now = time::OffsetDateTime::now_utc();
1207+
let format = time::format_description::parse("[year]-[month]-[day]_[hour]:[minute]:[second]")?;
1208+
let file_now = now.format(&format)?;
1209+
let format = time::format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]")?;
1210+
let now = now.format(&format)?;
1211+
let path = format!("rustc-ice-context-{file_now}.txt");
1212+
let mut file = std::fs::File::create(&path)?;
1213+
writeln!(
1214+
file,
1215+
"rustc {}{} running on {} at {now}",
1216+
util::version_str!().unwrap_or("unknown_version"),
1217+
match (option_env!("CFG_VER_HASH"), option_env!("CFG_VER_DATE")) {
1218+
(Some(hash), Some(date)) => format!(" ({hash} - {date})"),
1219+
(Some(val), None) | (None, Some(val)) => format!(" ({val})"),
1220+
(None, None) => String::new(),
1221+
},
1222+
config::host_triple(),
1223+
)?;
1224+
1225+
if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
1226+
writeln!(file, "compiler flags:")?;
1227+
for flag in flags {
1228+
writeln!(file, " {flag}")?;
1229+
}
1230+
if excluded_cargo_defaults {
1231+
writeln!(file, "some of the compiler flags provided by cargo are hidden")?;
1232+
}
1233+
}
1234+
writeln!(file, "")?;
1235+
match (info.message(), info.location()) {
1236+
(Some(message), Some(location)) => {
1237+
writeln!(file, "panicked at {location}:\n{message}")?;
1238+
}
1239+
(None, Some(location)) => {
1240+
writeln!(file, "panicked at {location}")?;
1241+
}
1242+
(Some(message), None) => {
1243+
writeln!(file, "panicked\n{message}")?;
1244+
}
1245+
(None, None) => {
1246+
writeln!(file, "panicked")?;
1247+
}
1248+
}
1249+
1250+
writeln!(file, "")?;
1251+
writeln!(file, "{}", capture)?;
1252+
1253+
// Be careful relying on global state here: this code is called from
1254+
// a panic hook, which means that the global `Handler` may be in a weird
1255+
// state if it was responsible for triggering the panic.
1256+
// This the same as `interface::try_print_query_stack` but writing to file.
1257+
rustc_middle::ty::tls::with_context_opt(|icx| {
1258+
let Some(icx) = icx else { return Err(IceError); };
1259+
let qcx = QueryCtxt::from_tcx(icx.tcx);
1260+
let query_map = qcx.try_collect_active_jobs();
1261+
let mut i = 0;
1262+
let mut current_query = icx.query;
1263+
writeln!(file, "")?;
1264+
writeln!(file, "query stack during panic:")?;
1265+
while let Some(query) = current_query {
1266+
let Some(query_info) = query_map.as_ref().and_then(|map| map.get(&query)) else {
1267+
break;
1268+
};
1269+
writeln!(
1270+
file,
1271+
"#{} [{:?}] {}",
1272+
i, query_info.query.dep_kind, query_info.query.description
1273+
)?;
1274+
current_query = query_info.job.parent;
1275+
i += 1;
1276+
}
1277+
writeln!(file, "end of query stack")?;
1278+
Ok(())
1279+
})?;
1280+
Ok(path)
1281+
}
1282+
11841283
static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
11851284
LazyLock::new(|| {
11861285
let hook = panic::take_hook();
@@ -1196,17 +1295,25 @@ static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send +
11961295
}
11971296
};
11981297

1298+
let is_dev = util::version_str!().map_or_else(
1299+
|| std::env::var("RUSTC_BACKTRACE_FORCE").as_deref() != Ok("1"),
1300+
|v| {
1301+
v.ends_with("-dev")
1302+
&& std::env::var("RUSTC_BACKTRACE_FORCE").as_deref() != Ok("0")
1303+
},
1304+
);
1305+
let written_ice = if !is_dev { write_ice_to_disk(info) } else { Err(IceError) };
11991306
// Invoke the default handler, which prints the actual panic message and optionally a backtrace
12001307
// Don't do this for delayed bugs, which already emit their own more useful backtrace.
1201-
if !info.payload().is::<rustc_errors::DelayedBugPanic>() {
1308+
if !info.payload().is::<rustc_errors::DelayedBugPanic>() && written_ice.is_err() {
12021309
(*DEFAULT_HOOK)(info);
12031310

12041311
// Separate the output with an empty line
12051312
eprintln!();
12061313
}
12071314

12081315
// Print the ICE message
1209-
report_ice(info, BUG_REPORT_URL);
1316+
report_ice(info, BUG_REPORT_URL, written_ice.ok());
12101317
}));
12111318
hook
12121319
});
@@ -1217,7 +1324,7 @@ static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send +
12171324
///
12181325
/// When `install_ice_hook` is called, this function will be called as the panic
12191326
/// hook.
1220-
pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
1327+
pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, reported_ice: Option<String>) {
12211328
let fallback_bundle =
12221329
rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
12231330
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
@@ -1238,39 +1345,51 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
12381345
if !info.payload().is::<rustc_errors::ExplicitBug>()
12391346
&& !info.payload().is::<rustc_errors::DelayedBugPanic>()
12401347
{
1241-
let mut d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
1348+
let mut d = rustc_errors::Diagnostic::new(
1349+
rustc_errors::Level::Bug,
1350+
"the compiler unexpectedly panicked. this is a bug.",
1351+
);
12421352
handler.emit_diagnostic(&mut d);
12431353
}
12441354

1245-
let mut xs: Vec<Cow<'static, str>> = vec![
1246-
"the compiler unexpectedly panicked. this is a bug.".into(),
1247-
format!("we would appreciate a bug report: {bug_report_url}").into(),
1248-
format!(
1249-
"rustc {} running on {}",
1250-
util::version_str!().unwrap_or("unknown_version"),
1251-
config::host_triple()
1252-
)
1253-
.into(),
1254-
];
1255-
1256-
if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
1257-
xs.push(format!("compiler flags: {}", flags.join(" ")).into());
1258-
1259-
if excluded_cargo_defaults {
1260-
xs.push("some of the compiler flags provided by cargo are hidden".into());
1355+
let xs: Vec<Cow<'static, str>> = if let Some(path) = &reported_ice {
1356+
vec![
1357+
format!("all necessary context about this bug was written to `{path}`").into(),
1358+
format!("we would appreciate a bug report with this context at <{bug_report_url}>")
1359+
.into(),
1360+
]
1361+
} else {
1362+
let mut xs = vec![
1363+
format!("we would appreciate a bug report at <{bug_report_url}>").into(),
1364+
format!(
1365+
"rustc {} running on {}",
1366+
util::version_str!().unwrap_or("unknown_version"),
1367+
config::host_triple()
1368+
)
1369+
.into(),
1370+
];
1371+
if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
1372+
xs.push(format!("compiler flags: {}", flags.join(" ")).into());
1373+
1374+
if excluded_cargo_defaults {
1375+
xs.push("some of the compiler flags provided by cargo are hidden".into());
1376+
}
12611377
}
1262-
}
1378+
xs
1379+
};
12631380

12641381
for note in &xs {
12651382
handler.note_without_error(note.as_ref());
12661383
}
12671384

1268-
// If backtraces are enabled, also print the query stack
1269-
let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");
1385+
if reported_ice.is_none() {
1386+
// If backtraces are enabled, also print the query stack
1387+
let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");
12701388

1271-
let num_frames = if backtrace { None } else { Some(2) };
1389+
let num_frames = if backtrace { None } else { Some(2) };
12721390

1273-
interface::try_print_query_stack(&handler, num_frames);
1391+
interface::try_print_query_stack(&handler, num_frames);
1392+
}
12741393

12751394
#[cfg(windows)]
12761395
unsafe {

tests/ui/impl-trait/issues/issue-86800.stderr

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ LL | type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResu
99

1010
stack backtrace:
1111

12-
error: internal compiler error: unexpected panic
13-
14-
12+
error: internal compiler error: the compiler unexpectedly panicked. this is a bug.
1513

1614

1715

tests/ui/layout/valid_range_oob.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: internal compiler error: unexpected panic
1+
error: internal compiler error: the compiler unexpectedly panicked. this is a bug.
22

33
query stack during panic:
44
#0 [layout_of] computing layout of `Foo`

tests/ui/panics/default-backtrace-ice.stderr

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ LL | fn main() { missing_ident; }
44

55
stack backtrace:
66

7-
error: internal compiler error: unexpected panic
8-
9-
7+
error: internal compiler error: the compiler unexpectedly panicked. this is a bug.
108

119

1210

tests/ui/treat-err-as-bug/delay_span_bug.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error: internal compiler error: delayed span bug triggered by #[rustc_error(dela
44
LL | fn main() {}
55
| ^^^^^^^^^
66

7-
error: internal compiler error: unexpected panic
7+
error: internal compiler error: the compiler unexpectedly panicked. this is a bug.
88

99
query stack during panic:
1010
#0 [trigger_delay_span_bug] triggering a delay span bug

tests/ui/treat-err-as-bug/err.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0080]: could not evaluate static initializer
44
LL | pub static C: u32 = 0 - 1;
55
| ^^^^^ attempt to compute `0_u32 - 1_u32`, which would overflow
66

7-
error: internal compiler error: unexpected panic
7+
error: internal compiler error: the compiler unexpectedly panicked. this is a bug.
88

99
query stack during panic:
1010
#0 [eval_to_allocation_raw] const-evaluating + checking `C`

0 commit comments

Comments
 (0)