From 9e0d1a328443b07bc50e9d66fd1c0e2a5568a13b Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Fri, 19 May 2023 16:03:35 -0400 Subject: [PATCH] Print a backtrace in const eval if interrupted --- Cargo.lock | 1 + compiler/rustc_const_eval/messages.ftl | 2 ++ .../src/const_eval/eval_queries.rs | 9 ++++++++- compiler/rustc_const_eval/src/errors.rs | 1 + compiler/rustc_const_eval/src/lib.rs | 7 +++++++ compiler/rustc_driver_impl/Cargo.toml | 1 + compiler/rustc_driver_impl/src/lib.rs | 19 +++++++++++++++++++ .../rustc_middle/src/mir/interpret/error.rs | 2 ++ src/tools/miri/src/bin/miri.rs | 4 ++++ src/tools/miri/src/concurrency/thread.rs | 19 ++++--------------- src/tools/tidy/src/deps.rs | 3 +++ tests/run-make/jobserver-error/Makefile | 4 +++- .../jobserver-error/cannot_open_fd.stderr | 2 +- 13 files changed, 56 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3110f32ade968..3c1e9634c57c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3771,6 +3771,7 @@ dependencies = [ name = "rustc_driver_impl" version = "0.0.0" dependencies = [ + "ctrlc", "libc", "rustc_ast", "rustc_ast_lowering", diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 0046190d20cc7..d6aae60c3382a 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -146,6 +146,8 @@ const_eval_intern_kind = {$kind -> *[other] {""} } +const_eval_interrupted = compilation was interrupted + const_eval_invalid_align_details = invalid align passed to `{$name}`: {$align} is {$err_kind -> [not_power_of_two] not a power of 2 diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 5a1c7cc4209ad..098a6201c4ea3 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -1,3 +1,5 @@ +use std::sync::atomic::Ordering::Relaxed; + use either::{Left, Right}; use rustc_hir::def::DefKind; @@ -22,6 +24,7 @@ use crate::interpret::{ InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup, }; +use crate::CTRL_C_RECEIVED; // Returns a pointer to where the result lives #[instrument(level = "trace", skip(ecx, body))] @@ -79,7 +82,11 @@ fn eval_body_using_ecx<'mir, 'tcx, R: InterpretationResult<'tcx>>( ecx.storage_live_for_always_live_locals()?; // The main interpreter loop. - while ecx.step()? {} + while ecx.step()? { + if CTRL_C_RECEIVED.load(Relaxed) { + throw_exhaust!(Interrupted); + } + } // Intern the result intern_const_alloc_recursive(ecx, intern_kind, &ret)?; diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index cc32640408b7e..5c46ec799f1ec 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -884,6 +884,7 @@ impl ReportErrorExt for ResourceExhaustionInfo { ResourceExhaustionInfo::StackFrameLimitReached => const_eval_stack_frame_limit_reached, ResourceExhaustionInfo::MemoryExhausted => const_eval_memory_exhausted, ResourceExhaustionInfo::AddressSpaceFull => const_eval_address_space_full, + ResourceExhaustionInfo::Interrupted => const_eval_interrupted, } } fn add_args(self, _: &mut Diag<'_, G>) {} diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 1e7ee208af1ab..7928f139aa06b 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -32,6 +32,8 @@ pub mod interpret; pub mod transform; pub mod util; +use std::sync::atomic::AtomicBool; + pub use errors::ReportErrorExt; use rustc_middle::{ty, util::Providers}; @@ -57,3 +59,8 @@ pub fn provide(providers: &mut Providers) { util::check_validity_requirement(tcx, init_kind, param_env_and_ty) }; } + +/// `rustc_driver::main` installs a handler that will set this to `true` if +/// the compiler has been sent a request to shut down, such as by a Ctrl-C. +/// This static lives here because it is only read by the interpreter. +pub static CTRL_C_RECEIVED: AtomicBool = AtomicBool::new(false); diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index fcc0afd3488b0..e4fb13822f81e 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start +ctrlc = "3.4.4" rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 938f9f0beaacd..1a0ae9eb21129 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -19,6 +19,7 @@ extern crate tracing; use rustc_ast as ast; use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults}; +use rustc_const_eval::CTRL_C_RECEIVED; use rustc_data_structures::profiling::{ get_resident_set_size, print_time_passes_entry, TimePassesFormat, }; @@ -1518,6 +1519,22 @@ pub fn init_logger(early_dcx: &EarlyDiagCtxt, cfg: rustc_log::LoggerConfig) { } } +/// Install our usual `ctrlc` handler, which sets [`rustc_const_eval::CTRL_C_RECEIVED`]. +/// Making this handler optional lets tools can install a different handler, if they wish. +pub fn install_ctrlc_handler() { + ctrlc::set_handler(move || { + // Indicate that we have been signaled to stop. If we were already signaled, exit + // immediately. In our interpreter loop we try to consult this value often, but if for + // whatever reason we don't get to that check or the cleanup we do upon finding that + // this bool has become true takes a long time, the exit here will promptly exit the + // process on the second Ctrl-C. + if CTRL_C_RECEIVED.swap(true, Ordering::Relaxed) { + std::process::exit(1); + } + }) + .expect("Unable to install ctrlc handler"); +} + pub fn main() -> ! { let start_time = Instant::now(); let start_rss = get_resident_set_size(); @@ -1528,6 +1545,8 @@ pub fn main() -> ! { signal_handler::install(); let mut callbacks = TimePassesCallbacks::default(); let using_internal_features = install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ()); + install_ctrlc_handler(); + let exit_code = catch_with_exit_code(|| { RunCompiler::new(&args::raw_args(&early_dcx)?, &mut callbacks) .set_using_internal_features(using_internal_features) diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 751d6de83f92c..c86970635a55f 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -482,6 +482,8 @@ pub enum ResourceExhaustionInfo { MemoryExhausted, /// The address space (of the target) is full. AddressSpaceFull, + /// The compiler got an interrupt signal (a user ran out of patience). + Interrupted, } /// A trait for machine-specific errors (or other "machine stop" conditions). diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 6955e649b4d17..5ad4c7d2d1c4b 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -344,6 +344,10 @@ fn main() { let args = rustc_driver::args::raw_args(&early_dcx) .unwrap_or_else(|_| std::process::exit(rustc_driver::EXIT_FAILURE)); + // Install the ctrlc handler that sets `rustc_const_eval::CTRL_C_RECEIVED`, even if + // MIRI_BE_RUSTC is set. + rustc_driver::install_ctrlc_handler(); + // If the environment asks us to actually be rustc, then do that. if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") { // Earliest rustc setup. diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 805c0580b2f15..e2e18d3a7344f 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -3,12 +3,13 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; use std::num::TryFromIntError; -use std::sync::atomic::{AtomicBool, Ordering::Relaxed}; +use std::sync::atomic::Ordering::Relaxed; use std::task::Poll; use std::time::{Duration, SystemTime}; use either::Either; +use rustc_const_eval::CTRL_C_RECEIVED; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_index::{Idx, IndexVec}; @@ -1045,21 +1046,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Run the core interpreter loop. Returns only when an interrupt occurs (an error or program /// termination). fn run_threads(&mut self) -> InterpResult<'tcx, !> { - static SIGNALED: AtomicBool = AtomicBool::new(false); - ctrlc::set_handler(move || { - // Indicate that we have ben signaled to stop. If we were already signaled, exit - // immediately. In our interpreter loop we try to consult this value often, but if for - // whatever reason we don't get to that check or the cleanup we do upon finding that - // this bool has become true takes a long time, the exit here will promptly exit the - // process on the second Ctrl-C. - if SIGNALED.swap(true, Relaxed) { - std::process::exit(1); - } - }) - .unwrap(); - let this = self.eval_context_mut(); + let this = self.eval_context_mut(); loop { - if SIGNALED.load(Relaxed) { + if CTRL_C_RECEIVED.load(Relaxed) { this.machine.handle_abnormal_termination(); std::process::exit(1); } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 10fdfc0a65f75..b74afa0d3e86e 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -208,6 +208,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "byteorder", // via ruzstd in object in thorin-dwp "cc", "cfg-if", + "cfg_aliases", "compiler_builtins", "cpufeatures", "crc32fast", @@ -216,6 +217,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "crossbeam-epoch", "crossbeam-utils", "crypto-common", + "ctrlc", "darling", "darling_core", "darling_macro", @@ -281,6 +283,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "memmap2", "memoffset", "miniz_oxide", + "nix", "nu-ansi-term", "num-conv", "num_cpus", diff --git a/tests/run-make/jobserver-error/Makefile b/tests/run-make/jobserver-error/Makefile index a7601b8671563..9f34970f96f10 100644 --- a/tests/run-make/jobserver-error/Makefile +++ b/tests/run-make/jobserver-error/Makefile @@ -4,9 +4,11 @@ include ../tools.mk # ignore-cross-compile # Test compiler behavior in case environment specifies wrong jobserver. +# Note that by default, the compiler uses file descriptors 0 (stdin), 1 (stdout), 2 (stderr), +# but also 3 and 4 for either end of the ctrl-c signal handler self-pipe. all: - bash -c 'echo "fn main() {}" | MAKEFLAGS="--jobserver-auth=3,3" $(RUSTC)' 2>&1 | diff cannot_open_fd.stderr - + bash -c 'echo "fn main() {}" | MAKEFLAGS="--jobserver-auth=5,5" $(RUSTC)' 2>&1 | diff cannot_open_fd.stderr - bash -c 'echo "fn main() {}" | MAKEFLAGS="--jobserver-auth=3,3" $(RUSTC) - 3&1 | diff not_a_pipe.stderr - # This test randomly fails, see https://github.com/rust-lang/rust/issues/110321 diff --git a/tests/run-make/jobserver-error/cannot_open_fd.stderr b/tests/run-make/jobserver-error/cannot_open_fd.stderr index 343de5cd52c3b..7c42184653594 100644 --- a/tests/run-make/jobserver-error/cannot_open_fd.stderr +++ b/tests/run-make/jobserver-error/cannot_open_fd.stderr @@ -1,4 +1,4 @@ -warning: failed to connect to jobserver from environment variable `MAKEFLAGS="--jobserver-auth=3,3"`: cannot open file descriptor 3 from the jobserver environment variable value: Bad file descriptor (os error 9) +warning: failed to connect to jobserver from environment variable `MAKEFLAGS="--jobserver-auth=5,5"`: cannot open file descriptor 5 from the jobserver environment variable value: Bad file descriptor (os error 9) | = note: the build environment is likely misconfigured