Skip to content

Teach rustc to write the ICE backtrace and query stack to disk #106691

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

Closed
wants to merge 6 commits into from
Closed
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
77 changes: 75 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
@@ -611,6 +611,16 @@ dependencies = [
"tracing-tree",
]

[[package]]
name = "chrono"
version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00"
dependencies = [
"num",
"time 0.1.43",
]

[[package]]
name = "chrono"
version = "0.4.19"
@@ -625,6 +635,15 @@ dependencies = [
"winapi",
]

[[package]]
name = "ci_info"
version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62a62f39080c8c83e899dff6abd46c4fac05c1cf8dafece96ad8238e79addbf8"
dependencies = [
"envmnt",
]

[[package]]
name = "clap"
version = "3.2.20"
@@ -1414,6 +1433,16 @@ dependencies = [
"termcolor",
]

[[package]]
name = "envmnt"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d73999a2b8871e74c8b8bc23759ee9f3d85011b24fafc91a4b3b5c8cc8185501"
dependencies = [
"fsio",
"indexmap",
]

[[package]]
name = "errno"
version = "0.2.8"
@@ -1619,6 +1648,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674"

[[package]]
name = "fsio"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dad0ce30be0cc441b325c5d705c8b613a0ca0d92b6a8953d41bd236dc09a36d0"
dependencies = [
"dunce",
]

[[package]]
name = "futf"
version = "0.1.5"
@@ -2535,7 +2573,7 @@ checksum = "23f3e133c6d515528745ffd3b9f0c7d975ae039f0b6abb099f2168daa2afb4f9"
dependencies = [
"ammonia",
"anyhow",
"chrono",
"chrono 0.4.19",
"clap 3.2.20",
"clap_complete",
"elasticlunr-rs",
@@ -2703,6 +2741,17 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"

[[package]]
name = "num"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e"
dependencies = [
"num-integer",
"num-iter",
"num-traits",
]

[[package]]
name = "num-integer"
version = "0.1.43"
@@ -2713,6 +2762,17 @@ dependencies = [
"num-traits",
]

[[package]]
name = "num-iter"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]

[[package]]
name = "num-traits"
version = "0.2.12"
@@ -3915,9 +3975,13 @@ dependencies = [
name = "rustc_driver"
version = "0.0.0"
dependencies = [
"backtrace",
"chrono 0.2.25",
"ci_info",
"libc",
"rustc_ast",
"rustc_ast_pretty",
"rustc_codegen_llvm",
"rustc_codegen_ssa",
"rustc_data_structures",
"rustc_error_codes",
@@ -3934,12 +3998,15 @@ dependencies = [
"rustc_middle",
"rustc_parse",
"rustc_plugin_impl",
"rustc_query_impl",
"rustc_query_system",
"rustc_save_analysis",
"rustc_session",
"rustc_span",
"rustc_target",
"serde_json",
"tracing",
"urlqstring",
"winapi",
]

@@ -5248,7 +5315,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3c02f6eb7e7b4100c272f685a9ccaccaab302324e8c7ec3e2ee72340fb29ff3"
dependencies = [
"chrono",
"chrono 0.4.19",
"log",
"nom",
"serde",
@@ -6054,6 +6121,12 @@ dependencies = [
"serde",
]

[[package]]
name = "urlqstring"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25ef3473a06a065718d8ec7cd7acc6a35fc20f836dee7661ad3b64ea3cc2e0cc"

[[package]]
name = "utf-8"
version = "0.7.5"
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ extern crate tracing;
use back::write::{create_informational_target_machine, create_target_machine};

use errors::FailParsingTargetMachineConfigToTargetMachine;
pub use llvm_util::target_features;
pub use llvm_util::{get_version, target_features};
use rustc_ast::expand::allocator::AllocatorKind;
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
use rustc_codegen_ssa::back::write::{
9 changes: 8 additions & 1 deletion compiler/rustc_driver/Cargo.toml
Original file line number Diff line number Diff line change
@@ -7,8 +7,12 @@ edition = "2021"
crate-type = ["dylib"]

[dependencies]
tracing = { version = "0.1.35" }
backtrace = "0.3.66"
serde_json = "1.0.59"
chrono = "0.2"
ci_info = "0.14.9"
urlqstring = "*"
tracing = { version = "0.1.35" }
rustc_log = { path = "../rustc_log" }
rustc_middle = { path = "../rustc_middle" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
@@ -25,11 +29,14 @@ rustc_parse = { path = "../rustc_parse" }
rustc_plugin_impl = { path = "../rustc_plugin_impl" }
rustc_save_analysis = { path = "../rustc_save_analysis" }
rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
rustc_codegen_llvm = { path = "../rustc_codegen_llvm" }
rustc_session = { path = "../rustc_session" }
rustc_error_codes = { path = "../rustc_error_codes" }
rustc_interface = { path = "../rustc_interface" }
rustc_ast = { path = "../rustc_ast" }
rustc_span = { path = "../rustc_span" }
rustc_query_system = { path = "../rustc_query_system" }
rustc_query_impl = { path = "../rustc_query_impl" }
rustc_hir_analysis = { path = "../rustc_hir_analysis" }

[target.'cfg(unix)'.dependencies]
177 changes: 150 additions & 27 deletions compiler/rustc_driver/src/lib.rs
Original file line number Diff line number Diff line change
@@ -8,6 +8,8 @@
#![feature(is_terminal)]
#![feature(once_cell)]
#![feature(decl_macro)]
#![feature(panic_info_message)]
#![feature(backtrace_frames)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]
#![deny(rustc::untranslatable_diagnostic)]
@@ -30,6 +32,8 @@ use rustc_interface::util::{self, collect_crate_types, get_codegen_backend};
use rustc_interface::{interface, Queries};
use rustc_lint::LintStore;
use rustc_metadata::locator;
use rustc_query_impl::QueryCtxt;
use rustc_query_system::query::QueryContext;
use rustc_save_analysis as save;
use rustc_save_analysis::DumpHandler;
use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS};
@@ -45,7 +49,6 @@ use rustc_target::json::ToJson;

use std::borrow::Cow;
use std::cmp::max;
use std::env;
use std::ffi::OsString;
use std::fs;
use std::io::{self, IsTerminal, Read, Write};
@@ -55,6 +58,7 @@ use std::process::{self, Command, Stdio};
use std::str;
use std::sync::LazyLock;
use std::time::Instant;
use std::{backtrace, env};

pub mod args;
pub mod pretty;
@@ -72,7 +76,7 @@ pub const EXIT_SUCCESS: i32 = 0;
pub const EXIT_FAILURE: i32 = 1;

const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
?labels=C-bug%2CI-ICE%2CT-compiler&template=ice.yaml";

const ICE_REPORT_COMPILER_FLAGS: &[&str] = &["-Z", "-C", "--crate-type"];

@@ -1181,6 +1185,100 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
}
}

struct IceError;

impl From<std::io::Error> for IceError {
fn from(_: std::io::Error) -> IceError {
IceError
}
}

fn write_ice_to_disk(info: &panic::PanicInfo<'_>) -> Result<(String, String), IceError> {
let mut args = vec![];
let capture = backtrace::Backtrace::force_capture();
let now = chrono::UTC::now();
let file_now = now.format("%Y-%m-%d_%H:%M:%S");
let now = now.format("%Y-%m-%d %H:%M:%S");
let mut path = std::env::current_dir()?;
path.push(format!("rustc-ice-context-{file_now}.txt"));
let mut file = std::fs::File::create(&path)?;
let (llvm_major, llvm_minor, llvm_dot) = rustc_codegen_llvm::get_version();
let version = format!(
"rustc {}{} running on {} at {now} with LLVM {llvm_major}.{llvm_minor}.{llvm_dot}",
util::version_str!().unwrap_or("unknown_version"),
match (option_env!("CFG_VER_HASH"), option_env!("CFG_VER_DATE")) {
(Some(hash), Some(date)) => format!(" ({hash} - {date})"),
(Some(val), None) | (None, Some(val)) => format!(" ({val})"),
(None, None) => String::new(),
},
config::host_triple(),
);

writeln!(file, "{}", version)?;
args.push(("version", version.as_str()));

if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
writeln!(file, "compiler flags:")?;
for flag in &flags {
writeln!(file, " {flag}")?;
}
if excluded_cargo_defaults {
writeln!(file, "some of the compiler flags provided by cargo are hidden")?;
}
}
writeln!(file, "")?;
match (info.message(), info.location()) {
(Some(message), Some(location)) => {
writeln!(file, "panicked at {location}:\n{message}")?;
}
(None, Some(location)) => {
writeln!(file, "panicked at {location}")?;
}
(Some(message), None) => {
writeln!(file, "panicked\n{message}")?;
}
(None, None) => {
writeln!(file, "panicked")?;
}
}

writeln!(file, "")?;
let capture = format!("{capture}");
writeln!(file, "{capture}")?;

// Be careful relying on global state here: this code is called from
// a panic hook, which means that the global `Handler` may be in a weird
// state if it was responsible for triggering the panic.
// This the same as `interface::try_print_query_stack` but writing to file.
rustc_middle::ty::tls::with_context_opt(|icx| {
let Some(icx) = icx else { return Err(IceError); };
let qcx = QueryCtxt::from_tcx(icx.tcx);
let query_map = qcx.try_collect_active_jobs();
let mut i = 0;
let mut current_query = icx.query;
writeln!(file, "")?;
writeln!(file, "query stack during panic:")?;
while let Some(query) = current_query {
let Some(query_info) = query_map.as_ref().and_then(|map| map.get(&query)) else {
break;
};
writeln!(
file,
"#{} [{:?}] {}",
i, query_info.query.dep_kind, query_info.query.description
)?;
current_query = query_info.job.parent;
i += 1;
}
writeln!(file, "end of query stack")?;
Ok(())
})?;
Ok((
path.display().to_string(),
format!("{BUG_REPORT_URL}&{}", urlqstring::QueryParams::from(args).stringify()),
))
}

static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
LazyLock::new(|| {
let hook = panic::take_hook();
@@ -1196,17 +1294,26 @@ static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send +
}
};

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

// Separate the output with an empty line
eprintln!();
}

// Print the ICE message
report_ice(info, BUG_REPORT_URL);
report_ice(info, BUG_REPORT_URL, written_ice.ok());
}));
hook
});
@@ -1217,7 +1324,11 @@ static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send +
///
/// When `install_ice_hook` is called, this function will be called as the panic
/// hook.
pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
pub fn report_ice(
info: &panic::PanicInfo<'_>,
bug_report_url: &str,
reported_ice: Option<(String, String)>,
) {
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
@@ -1238,39 +1349,51 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
if !info.payload().is::<rustc_errors::ExplicitBug>()
&& !info.payload().is::<rustc_errors::DelayedBugPanic>()
{
let mut d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
let mut d = rustc_errors::Diagnostic::new(
rustc_errors::Level::Bug,
"the compiler unexpectedly panicked. this is a bug.",
);
handler.emit_diagnostic(&mut d);
}

let mut xs: Vec<Cow<'static, str>> = vec![
"the compiler unexpectedly panicked. this is a bug.".into(),
format!("we would appreciate a bug report: {bug_report_url}").into(),
format!(
"rustc {} running on {}",
util::version_str!().unwrap_or("unknown_version"),
config::host_triple()
)
.into(),
];

if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
xs.push(format!("compiler flags: {}", flags.join(" ")).into());

if excluded_cargo_defaults {
xs.push("some of the compiler flags provided by cargo are hidden".into());
let xs: Vec<Cow<'static, str>> = if let Some((path, custom_url)) = &reported_ice {
let link = format!("\x1b]8;;{custom_url}\x07{bug_report_url}\x1b]8;;\x07");
vec![
format!("all necessary context about this bug was written to `{path}`").into(),
format!("we would appreciate a bug report with this context at {link}").into(),
]
} else {
let mut xs = vec![
format!("we would appreciate a bug report at <{bug_report_url}>").into(),
format!(
"rustc {} running on {}",
util::version_str!().unwrap_or("unknown_version"),
config::host_triple()
)
.into(),
];
if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
xs.push(format!("compiler flags: {}", flags.join(" ")).into());

if excluded_cargo_defaults {
xs.push("some of the compiler flags provided by cargo are hidden".into());
}
}
}
xs
};

for note in &xs {
handler.note_without_error(note.as_ref());
}

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

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

interface::try_print_query_stack(&handler, num_frames);
interface::try_print_query_stack(&handler, num_frames);
}

#[cfg(windows)]
unsafe {
1 change: 1 addition & 0 deletions src/tools/tidy/src/deps.rs
Original file line number Diff line number Diff line change
@@ -96,6 +96,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"arrayvec",
"atty",
"autocfg",
"backtrace",
"bitflags",
"block-buffer",
"bumpalo", // Included in Cargo's dep graph but only activated on wasm32-*-unknown.
3 changes: 2 additions & 1 deletion tests/ui/chalkify/bugs/async.rs
Original file line number Diff line number Diff line change
@@ -6,7 +6,8 @@
// failure-status:101
// normalize-stderr-test "note: .*" -> ""
// normalize-stderr-test "thread 'rustc' .*" -> ""
// normalize-stderr-test " .*\n" -> ""
// normalize-stderr-test " ? ?\d\d?\d?:.*\n" -> ""
// normalize-stderr-test " .*\n" -> ""
// normalize-stderr-test "DefId([^)]*)" -> "..."

fn main() -> () {}
44 changes: 31 additions & 13 deletions tests/ui/chalkify/bugs/async.stderr
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
error[E0277]: `[async fn body@$DIR/async.rs:14:29: 16:2]` is not a future
LL |LL | |LL | | }


error[E0277]: the size for values of type `<[async fn body@$DIR/async.rs:14:29: 16:2] as Future>::Output` cannot be known at compilation time
LL |LL | |LL | | }


error[E0277]: `[async fn body@$DIR/async.rs:14:29: 16:2]` is not a future
error[E0277]: `[async fn body@$DIR/async.rs:15:29: --> $DIR/async.rs:15:29
|
LL | async fn foo(x: u32) -> u32 {
| _____________________________-
LL | | x
LL | | }
| | ^
| | |
| |_`[async fn body@$DIR/async.rs:15:29: | required by a bound introduced by this call
|
= help: the trait `Future` is not implemented for `[async fn body@$DIR/async.rs:15:29: =

--> $SRC_DIR/core/src/future/mod.rs:LL:COL

error[E0277]: the size for values of type `<[async fn body@$DIR/async.rs:15:29: --> $DIR/async.rs:15:29
|
LL | async fn foo(x: u32) -> u32 {
| _____________________________^
LL | | x
LL | | }
| |_^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `<[async fn body@$DIR/async.rs:15:29:
--> $SRC_DIR/core/src/future/mod.rs:LL:COL

error[E0277]: `[async fn body@$DIR/async.rs:15:29: --> $DIR/async.rs:15:25
|
LL | async fn foo(x: u32) -> u32 {
| = help: the trait `Future` is not implemented for `[async fn body@$DIR/async.rs:15:29: =

error: internal compiler error: compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs:1114:25: projection clauses should be implied from elsewhere. obligation: `Obligation(predicate=Binder(ProjectionPredicate(AliasTy { substs: [[async fn body@$DIR/async.rs:14:29: 16:2]], def_id: ...), _use_mk_alias_ty_instead: () }, Term::Ty(u32)), []), depth=0)`
error: internal compiler error: compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs:1114:25: projection clauses should be implied from elsewhere. obligation: `Obligation(predicate=Binder(ProjectionPredicate(AliasTy { substs: [[async fn body@$DIR/async.rs:15:29: --> $DIR/async.rs:15:25
|
LL | async fn foo(x: u32) -> u32 {

|

stack backtrace:

@@ -21,8 +41,6 @@ stack backtrace:





query stack during panic:
#0 [typeck] type-checking `foo`
#1 [thir_body] building THIR for `foo`
4 changes: 1 addition & 3 deletions tests/ui/impl-trait/issues/issue-86800.stderr
Original file line number Diff line number Diff line change
@@ -9,9 +9,7 @@ LL | type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResu

stack backtrace:

error: internal compiler error: unexpected panic


error: internal compiler error: the compiler unexpectedly panicked. this is a bug.



2 changes: 1 addition & 1 deletion tests/ui/layout/valid_range_oob.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: internal compiler error: unexpected panic
error: internal compiler error: the compiler unexpectedly panicked. this is a bug.

query stack during panic:
#0 [layout_of] computing layout of `Foo`
4 changes: 1 addition & 3 deletions tests/ui/panics/default-backtrace-ice.stderr
Original file line number Diff line number Diff line change
@@ -4,9 +4,7 @@ LL | fn main() { missing_ident; }

stack backtrace:

error: internal compiler error: unexpected panic


error: internal compiler error: the compiler unexpectedly panicked. this is a bug.



2 changes: 1 addition & 1 deletion tests/ui/treat-err-as-bug/delay_span_bug.stderr
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ error: internal compiler error: delayed span bug triggered by #[rustc_error(dela
LL | fn main() {}
| ^^^^^^^^^

error: internal compiler error: unexpected panic
error: internal compiler error: the compiler unexpectedly panicked. this is a bug.

query stack during panic:
#0 [trigger_delay_span_bug] triggering a delay span bug
2 changes: 1 addition & 1 deletion tests/ui/treat-err-as-bug/err.stderr
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ error[E0080]: could not evaluate static initializer
LL | pub static C: u32 = 0 - 1;
| ^^^^^ attempt to compute `0_u32 - 1_u32`, which would overflow

error: internal compiler error: unexpected panic
error: internal compiler error: the compiler unexpectedly panicked. this is a bug.

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