Skip to content
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

On nightly, dump ICE backtraces to disk #108714

Merged
merged 2 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
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
28 changes: 28 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3488,6 +3488,7 @@ dependencies = [
"rustc_trait_selection",
"rustc_ty_utils",
"serde_json",
"time",
"tracing",
"windows",
]
Expand Down Expand Up @@ -5142,6 +5143,33 @@ dependencies = [
name = "tier-check"
version = "0.1.0"

[[package]]
name = "time"
version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd"
dependencies = [
"itoa",
"serde",
"time-core",
"time-macros",
]

[[package]]
name = "time-core"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"

[[package]]
name = "time-macros"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b"
dependencies = [
"time-core",
]

[[package]]
name = "tinystr"
version = "0.7.1"
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {

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

pub fn config(&self, kind: ModuleKind) -> &ModuleConfig {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_driver_impl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition = "2021"
[lib]

[dependencies]
time = { version = "0.3", default-features = false, features = ["formatting", ] }
tracing = { version = "0.1.35" }
serde_json = "1.0.59"
rustc_log = { path = "../rustc_log" }
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_driver_impl/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url}
driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden

driver_impl_ice_flags = compiler flags: {$flags}
driver_impl_ice_path = please attach the file at `{$path}` to your bug report
driver_impl_ice_path_error = the ICE couldn't be written to `{$path}`: {$error}
driver_impl_ice_path_error_env = the environment variable `RUSTC_ICE` is set to `{$env_var}`
driver_impl_ice_version = rustc {$version} running on {$triple}

driver_impl_rlink_empty_version_number = The input does not contain version number

driver_impl_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}`
Expand Down
80 changes: 67 additions & 13 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(lazy_cell)]
#![feature(decl_macro)]
#![feature(ice_to_disk)]
#![feature(let_chains)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]
#![deny(rustc::untranslatable_diagnostic)]
Expand Down Expand Up @@ -57,8 +59,11 @@ use std::panic::{self, catch_unwind};
use std::path::PathBuf;
use std::process::{self, Command, Stdio};
use std::str;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::OnceLock;
use std::time::Instant;
use std::time::{Instant, SystemTime};
use time::format_description::well_known::Rfc3339;
use time::OffsetDateTime;

#[allow(unused_macros)]
macro do_not_use_print($($t:tt)*) {
Expand Down Expand Up @@ -294,6 +299,7 @@ fn run_compiler(
input: Input::File(PathBuf::new()),
output_file: ofile,
output_dir: odir,
ice_file: ice_path().clone(),
file_loader,
locale_resources: DEFAULT_LOCALE_RESOURCES,
lint_caps: Default::default(),
Expand Down Expand Up @@ -1292,9 +1298,29 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
}
}

/// Stores the default panic hook, from before [`install_ice_hook`] was called.
static DEFAULT_HOOK: OnceLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
OnceLock::new();
pub static ICE_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();

pub fn ice_path() -> &'static Option<PathBuf> {
ICE_PATH.get_or_init(|| {
if !rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() {
return None;
}
if let Ok("0") = std::env::var("RUST_BACKTRACE").as_deref() {
return None;
}
let mut path = match std::env::var("RUSTC_ICE").as_deref() {
// Explicitly opting out of writing ICEs to disk.
Ok("0") => return None,
Ok(s) => PathBuf::from(s),
Err(_) => std::env::current_dir().unwrap_or_default(),
};
let now: OffsetDateTime = SystemTime::now().into();
let file_now = now.format(&Rfc3339).unwrap_or(String::new());
let pid = std::process::id();
path.push(format!("rustc-ice-{file_now}-{pid}.txt"));
Some(path)
})
}

/// Installs a panic hook that will print the ICE message on unexpected panics.
///
Expand All @@ -1318,8 +1344,6 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler))
std::env::set_var("RUST_BACKTRACE", "full");
}

let default_hook = DEFAULT_HOOK.get_or_init(panic::take_hook);

panic::set_hook(Box::new(move |info| {
// If the error was caused by a broken pipe then this is not a bug.
// Write the error and return immediately. See #98700.
Expand All @@ -1336,7 +1360,7 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler))
// 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>() {
(*default_hook)(info);
std::panic_hook_with_disk_dump(info, ice_path().as_deref());

// Separate the output with an empty line
eprintln!();
Expand Down Expand Up @@ -1368,7 +1392,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info:
false,
TerminalUrl::No,
));
let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
let handler = rustc_errors::Handler::with_emitter(true, None, emitter, None);

// a .span_bug or .bug call has already printed what
// it wants to print.
Expand All @@ -1379,10 +1403,40 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info:
}

handler.emit_note(session_diagnostics::IceBugReport { bug_report_url });
handler.emit_note(session_diagnostics::IceVersion {
version: util::version_str!().unwrap_or("unknown_version"),
triple: config::host_triple(),
});

let version = util::version_str!().unwrap_or("unknown_version");
let triple = config::host_triple();

static FIRST_PANIC: AtomicBool = AtomicBool::new(true);

let file = if let Some(path) = ice_path().as_ref() {
// Create the ICE dump target file.
match crate::fs::File::options().create(true).append(true).open(&path) {
Ok(mut file) => {
handler
.emit_note(session_diagnostics::IcePath { path: path.display().to_string() });
if FIRST_PANIC.swap(false, Ordering::SeqCst) {
let _ = write!(file, "\n\nrustc version: {version}\nplatform: {triple}");
}
Some(file)
}
Err(err) => {
// The path ICE couldn't be written to disk, provide feedback to the user as to why.
handler.emit_warning(session_diagnostics::IcePathError {
path: path.display().to_string(),
error: err.to_string(),
env_var: std::env::var("RUSTC_ICE")
.ok()
.map(|env_var| session_diagnostics::IcePathErrorEnv { env_var }),
});
handler.emit_note(session_diagnostics::IceVersion { version, triple });
None
}
}
} else {
handler.emit_note(session_diagnostics::IceVersion { version, triple });
None
};

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

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, file);

// We don't trust this callback not to panic itself, so run it at the end after we're sure we've
// printed all the relevant info.
Expand Down
23 changes: 22 additions & 1 deletion compiler/rustc_driver_impl/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_macros::Diagnostic;
use rustc_macros::{Diagnostic, Subdiagnostic};

#[derive(Diagnostic)]
#[diag(driver_impl_rlink_unable_to_read)]
Expand Down Expand Up @@ -56,6 +56,27 @@ pub(crate) struct IceVersion<'a> {
pub triple: &'a str,
}

#[derive(Diagnostic)]
#[diag(driver_impl_ice_path)]
pub(crate) struct IcePath {
pub path: String,
}

#[derive(Diagnostic)]
#[diag(driver_impl_ice_path_error)]
pub(crate) struct IcePathError {
pub path: String,
pub error: String,
#[subdiagnostic]
pub env_var: Option<IcePathErrorEnv>,
}

#[derive(Subdiagnostic)]
#[note(driver_impl_ice_path_error_env)]
pub(crate) struct IcePathErrorEnv {
pub env_var: String,
}

#[derive(Diagnostic)]
#[diag(driver_impl_ice_flags)]
pub(crate) struct IceFlags {
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_error_messages/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,13 @@ impl DiagnosticMessage {
}
}
}

pub fn as_str(&self) -> Option<&str> {
match self {
DiagnosticMessage::Eager(s) | DiagnosticMessage::Str(s) => Some(s),
DiagnosticMessage::FluentIdentifier(_, _) => None,
}
}
}

impl From<String> for DiagnosticMessage {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_errors/src/json/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
);

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

let bytes = output.lock().unwrap();
Expand Down
31 changes: 28 additions & 3 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ use std::borrow::Cow;
use std::error::Report;
use std::fmt;
use std::hash::Hash;
use std::io::Write;
use std::num::NonZeroUsize;
use std::panic;
use std::path::Path;
use std::path::{Path, PathBuf};

use termcolor::{Color, ColorSpec};

Expand Down Expand Up @@ -461,6 +462,10 @@ struct HandlerInner {
///
/// [RFC-2383]: https://rust-lang.github.io/rfcs/2383-lint-reasons.html
fulfilled_expectations: FxHashSet<LintExpectationId>,

/// The file where the ICE information is stored. This allows delayed_span_bug backtraces to be
/// stored along side the main panic backtrace.
ice_file: Option<PathBuf>,
}

/// A key denoting where from a diagnostic was stashed.
Expand Down Expand Up @@ -550,13 +555,15 @@ impl Handler {
sm: Option<Lrc<SourceMap>>,
fluent_bundle: Option<Lrc<FluentBundle>>,
fallback_bundle: LazyFallbackBundle,
ice_file: Option<PathBuf>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not this PR's fault, but we really need to change this to a builder pattern ^^

) -> Self {
Self::with_tty_emitter_and_flags(
color_config,
sm,
fluent_bundle,
fallback_bundle,
HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() },
ice_file,
)
}

Expand All @@ -566,6 +573,7 @@ impl Handler {
fluent_bundle: Option<Lrc<FluentBundle>>,
fallback_bundle: LazyFallbackBundle,
flags: HandlerFlags,
ice_file: Option<PathBuf>,
) -> Self {
let emitter = Box::new(EmitterWriter::stderr(
color_config,
Expand All @@ -579,23 +587,26 @@ impl Handler {
flags.track_diagnostics,
TerminalUrl::No,
));
Self::with_emitter_and_flags(emitter, flags)
Self::with_emitter_and_flags(emitter, flags, ice_file)
}

pub fn with_emitter(
can_emit_warnings: bool,
treat_err_as_bug: Option<NonZeroUsize>,
emitter: Box<dyn Emitter + sync::Send>,
ice_file: Option<PathBuf>,
) -> Self {
Handler::with_emitter_and_flags(
emitter,
HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() },
ice_file,
)
}

pub fn with_emitter_and_flags(
emitter: Box<dyn Emitter + sync::Send>,
flags: HandlerFlags,
ice_file: Option<PathBuf>,
) -> Self {
Self {
flags,
Expand All @@ -618,6 +629,7 @@ impl Handler {
check_unstable_expect_diagnostics: false,
unstable_expect_diagnostics: Vec::new(),
fulfilled_expectations: Default::default(),
ice_file,
}),
}
}
Expand Down Expand Up @@ -1657,8 +1669,21 @@ impl HandlerInner {
explanation: impl Into<DiagnosticMessage> + Copy,
) {
let mut no_bugs = true;
// If backtraces are enabled, also print the query stack
let backtrace = std::env::var_os("RUST_BACKTRACE").map_or(true, |x| &x != "0");
for bug in bugs {
let mut bug = bug.decorate();
if let Some(file) = self.ice_file.as_ref()
&& let Ok(mut out) = std::fs::File::options().append(true).open(file)
{
let _ = write!(
&mut out,
"\n\ndelayed span bug: {}\n{}",
bug.inner.styled_message().iter().filter_map(|(msg, _)| msg.as_str()).collect::<String>(),
&bug.note
);
}
let mut bug =
if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner };

if no_bugs {
// Put the overall explanation before the `DelayedBug`s, to
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_expand/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &
false,
TerminalUrl::No,
);
let handler = Handler::with_emitter(true, None, Box::new(emitter));
let handler = Handler::with_emitter(true, None, Box::new(emitter), None);
#[allow(rustc::untranslatable_diagnostic)]
handler.span_err(msp, "foo");

Expand Down
Loading