summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_driver_impl
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:36 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:36 +0000
commite02c5b5930c2c9ba3e5423fe12e2ef0155017297 (patch)
treefd60ebbbb5299e16e5fca8c773ddb74f764760db /compiler/rustc_driver_impl
parentAdding debian version 1.73.0+dfsg1-1. (diff)
downloadrustc-e02c5b5930c2c9ba3e5423fe12e2ef0155017297.tar.xz
rustc-e02c5b5930c2c9ba3e5423fe12e2ef0155017297.zip
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_driver_impl')
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs149
-rw-r--r--compiler/rustc_driver_impl/src/signal_handler.rs142
2 files changed, 165 insertions, 126 deletions
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 736877bde..65c7aed3f 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -85,6 +85,15 @@ pub mod pretty;
#[macro_use]
mod print;
mod session_diagnostics;
+#[cfg(all(unix, any(target_env = "gnu", target_os = "macos")))]
+mod signal_handler;
+
+#[cfg(not(all(unix, any(target_env = "gnu", target_os = "macos"))))]
+mod signal_handler {
+ /// On platforms which don't support our signal handler's requirements,
+ /// simply use the default signal handler provided by std.
+ pub(super) fn install() {}
+}
use crate::session_diagnostics::{
RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch,
@@ -140,12 +149,6 @@ pub const EXIT_FAILURE: i32 = 1;
pub const DEFAULT_BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
-const ICE_REPORT_COMPILER_FLAGS: &[&str] = &["-Z", "-C", "--crate-type"];
-
-const ICE_REPORT_COMPILER_FLAGS_EXCLUDE: &[&str] = &["metadata", "extra-filename"];
-
-const ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE: &[&str] = &["incremental"];
-
pub fn abort_on_err<T>(result: Result<T, ErrorGuaranteed>, sess: &Session) -> T {
match result {
Err(..) => {
@@ -159,9 +162,10 @@ pub fn abort_on_err<T>(result: Result<T, ErrorGuaranteed>, sess: &Session) -> T
pub trait Callbacks {
/// Called before creating the compiler instance
fn config(&mut self, _config: &mut interface::Config) {}
- /// Called after parsing. Return value instructs the compiler whether to
+ /// Called after parsing the crate root. Submodules are not yet parsed when
+ /// this callback is called. Return value instructs the compiler whether to
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
- fn after_parsing<'tcx>(
+ fn after_crate_root_parsing<'tcx>(
&mut self,
_compiler: &interface::Compiler,
_queries: &'tcx Queries<'tcx>,
@@ -181,7 +185,6 @@ pub trait Callbacks {
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
fn after_analysis<'tcx>(
&mut self,
- _handler: &EarlyErrorHandler,
_compiler: &interface::Compiler,
_queries: &'tcx Queries<'tcx>,
) -> Compilation {
@@ -310,6 +313,7 @@ fn run_compiler(
override_queries: None,
make_codegen_backend,
registry: diagnostics_registry(),
+ expanded_args: args,
};
match make_input(&early_error_handler, &matches.free) {
@@ -403,7 +407,7 @@ fn run_compiler(
return early_exit();
}
- if callbacks.after_parsing(compiler, queries) == Compilation::Stop {
+ if callbacks.after_crate_root_parsing(compiler, queries) == Compilation::Stop {
return early_exit();
}
@@ -441,7 +445,7 @@ fn run_compiler(
queries.global_ctxt()?.enter(|tcx| tcx.analysis(()))?;
- if callbacks.after_analysis(&handler, compiler, queries) == Compilation::Stop {
+ if callbacks.after_analysis(compiler, queries) == Compilation::Stop {
return early_exit();
}
@@ -696,12 +700,14 @@ pub fn list_metadata(
sess: &Session,
metadata_loader: &dyn MetadataLoader,
) -> Compilation {
- if sess.opts.unstable_opts.ls {
+ let ls_kinds = &sess.opts.unstable_opts.ls;
+ if !ls_kinds.is_empty() {
match sess.io.input {
Input::File(ref ifile) => {
let path = &(*ifile);
let mut v = Vec::new();
- locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v).unwrap();
+ locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v, ls_kinds)
+ .unwrap();
safe_println!("{}", String::from_utf8(v).unwrap());
}
Input::Str { .. } => {
@@ -858,11 +864,9 @@ fn print_crate_info(
use rustc_target::spec::current_apple_deployment_target;
if sess.target.is_like_osx {
- println_info!(
- "deployment_target={}",
- current_apple_deployment_target(&sess.target)
- .expect("unknown Apple target OS")
- )
+ let (major, minor) = current_apple_deployment_target(&sess.target)
+ .expect("unknown Apple target OS");
+ println_info!("deployment_target={}", format!("{major}.{minor}"))
} else {
handler
.early_error("only Apple targets currently support deployment version info")
@@ -1250,47 +1254,6 @@ fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> {
}
}
-/// Gets a list of extra command-line flags provided by the user, as strings.
-///
-/// This function is used during ICEs to show more information useful for
-/// debugging, since some ICEs only happens with non-default compiler flags
-/// (and the users don't always report them).
-fn extra_compiler_flags() -> Option<(Vec<String>, bool)> {
- let mut args = env::args_os().map(|arg| arg.to_string_lossy().to_string()).peekable();
-
- let mut result = Vec::new();
- let mut excluded_cargo_defaults = false;
- while let Some(arg) = args.next() {
- if let Some(a) = ICE_REPORT_COMPILER_FLAGS.iter().find(|a| arg.starts_with(*a)) {
- let content = if arg.len() == a.len() {
- // A space-separated option, like `-C incremental=foo` or `--crate-type rlib`
- match args.next() {
- Some(arg) => arg.to_string(),
- None => continue,
- }
- } else if arg.get(a.len()..a.len() + 1) == Some("=") {
- // An equals option, like `--crate-type=rlib`
- arg[a.len() + 1..].to_string()
- } else {
- // A non-space option, like `-Cincremental=foo`
- arg[a.len()..].to_string()
- };
- let option = content.split_once('=').map(|s| s.0).unwrap_or(&content);
- if ICE_REPORT_COMPILER_FLAGS_EXCLUDE.iter().any(|exc| option == *exc) {
- excluded_cargo_defaults = true;
- } else {
- result.push(a.to_string());
- match ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.iter().find(|s| option == **s) {
- Some(s) => result.push(format!("{s}=[REDACTED]")),
- None => result.push(content),
- }
- }
- }
- }
-
- if !result.is_empty() { Some((result, excluded_cargo_defaults)) } else { None }
-}
-
/// Runs a closure and catches unwinds triggered by fatal errors.
///
/// The compiler currently unwinds with a special sentinel value to abort
@@ -1477,7 +1440,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info:
None
};
- if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
+ if let Some((flags, excluded_cargo_defaults)) = rustc_session::utils::extra_compiler_flags() {
handler.emit_note(session_diagnostics::IceFlags { flags: flags.join(" ") });
if excluded_cargo_defaults {
handler.emit_note(session_diagnostics::IceExcludeCargoDefaults);
@@ -1517,72 +1480,6 @@ pub fn init_env_logger(handler: &EarlyErrorHandler, env: &str) {
}
}
-#[cfg(all(unix, any(target_env = "gnu", target_os = "macos")))]
-mod signal_handler {
- extern "C" {
- fn backtrace_symbols_fd(
- buffer: *const *mut libc::c_void,
- size: libc::c_int,
- fd: libc::c_int,
- );
- }
-
- extern "C" fn print_stack_trace(_: libc::c_int) {
- const MAX_FRAMES: usize = 256;
- static mut STACK_TRACE: [*mut libc::c_void; MAX_FRAMES] =
- [std::ptr::null_mut(); MAX_FRAMES];
- unsafe {
- let depth = libc::backtrace(STACK_TRACE.as_mut_ptr(), MAX_FRAMES as i32);
- if depth == 0 {
- return;
- }
- backtrace_symbols_fd(STACK_TRACE.as_ptr(), depth, 2);
- }
- }
-
- /// When an error signal (such as SIGABRT or SIGSEGV) is delivered to the
- /// process, print a stack trace and then exit.
- pub(super) fn install() {
- use std::alloc::{alloc, Layout};
-
- unsafe {
- let alt_stack_size: usize = min_sigstack_size() + 64 * 1024;
- let mut alt_stack: libc::stack_t = std::mem::zeroed();
- alt_stack.ss_sp = alloc(Layout::from_size_align(alt_stack_size, 1).unwrap()).cast();
- alt_stack.ss_size = alt_stack_size;
- libc::sigaltstack(&alt_stack, std::ptr::null_mut());
-
- let mut sa: libc::sigaction = std::mem::zeroed();
- sa.sa_sigaction = print_stack_trace as libc::sighandler_t;
- sa.sa_flags = libc::SA_NODEFER | libc::SA_RESETHAND | libc::SA_ONSTACK;
- libc::sigemptyset(&mut sa.sa_mask);
- libc::sigaction(libc::SIGSEGV, &sa, std::ptr::null_mut());
- }
- }
-
- /// Modern kernels on modern hardware can have dynamic signal stack sizes.
- #[cfg(any(target_os = "linux", target_os = "android"))]
- fn min_sigstack_size() -> usize {
- const AT_MINSIGSTKSZ: core::ffi::c_ulong = 51;
- let dynamic_sigstksz = unsafe { libc::getauxval(AT_MINSIGSTKSZ) };
- // If getauxval couldn't find the entry, it returns 0,
- // so take the higher of the "constant" and auxval.
- // This transparently supports older kernels which don't provide AT_MINSIGSTKSZ
- libc::MINSIGSTKSZ.max(dynamic_sigstksz as _)
- }
-
- /// Not all OS support hardware where this is needed.
- #[cfg(not(any(target_os = "linux", target_os = "android")))]
- fn min_sigstack_size() -> usize {
- libc::MINSIGSTKSZ
- }
-}
-
-#[cfg(not(all(unix, any(target_env = "gnu", target_os = "macos"))))]
-mod signal_handler {
- pub(super) fn install() {}
-}
-
pub fn main() -> ! {
let start_time = Instant::now();
let start_rss = get_resident_set_size();
diff --git a/compiler/rustc_driver_impl/src/signal_handler.rs b/compiler/rustc_driver_impl/src/signal_handler.rs
new file mode 100644
index 000000000..deca10822
--- /dev/null
+++ b/compiler/rustc_driver_impl/src/signal_handler.rs
@@ -0,0 +1,142 @@
+//! Signal handler for rustc
+//! Primarily used to extract a backtrace from stack overflow
+
+use std::alloc::{alloc, Layout};
+use std::{fmt, mem, ptr};
+
+extern "C" {
+ fn backtrace_symbols_fd(buffer: *const *mut libc::c_void, size: libc::c_int, fd: libc::c_int);
+}
+
+fn backtrace_stderr(buffer: &[*mut libc::c_void]) {
+ let size = buffer.len().try_into().unwrap_or_default();
+ unsafe { backtrace_symbols_fd(buffer.as_ptr(), size, libc::STDERR_FILENO) };
+}
+
+/// Unbuffered, unsynchronized writer to stderr.
+///
+/// Only acceptable because everything will end soon anyways.
+struct RawStderr(());
+
+impl fmt::Write for RawStderr {
+ fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
+ let ret = unsafe { libc::write(libc::STDERR_FILENO, s.as_ptr().cast(), s.len()) };
+ if ret == -1 { Err(fmt::Error) } else { Ok(()) }
+ }
+}
+
+/// We don't really care how many bytes we actually get out. SIGSEGV comes for our head.
+/// Splash stderr with letters of our own blood to warn our friends about the monster.
+macro raw_errln($tokens:tt) {
+ let _ = ::core::fmt::Write::write_fmt(&mut RawStderr(()), format_args!($tokens));
+ let _ = ::core::fmt::Write::write_char(&mut RawStderr(()), '\n');
+}
+
+/// Signal handler installed for SIGSEGV
+extern "C" fn print_stack_trace(_: libc::c_int) {
+ const MAX_FRAMES: usize = 256;
+ // Reserve data segment so we don't have to malloc in a signal handler, which might fail
+ // in incredibly undesirable and unexpected ways due to e.g. the allocator deadlocking
+ static mut STACK_TRACE: [*mut libc::c_void; MAX_FRAMES] = [ptr::null_mut(); MAX_FRAMES];
+ let stack = unsafe {
+ // Collect return addresses
+ let depth = libc::backtrace(STACK_TRACE.as_mut_ptr(), MAX_FRAMES as i32);
+ if depth == 0 {
+ return;
+ }
+ &STACK_TRACE.as_slice()[0..(depth as _)]
+ };
+
+ // Just a stack trace is cryptic. Explain what we're doing.
+ raw_errln!("error: rustc interrupted by SIGSEGV, printing backtrace\n");
+ let mut written = 1;
+ let mut consumed = 0;
+ // Begin elaborating return addrs into symbols and writing them directly to stderr
+ // Most backtraces are stack overflow, most stack overflows are from recursion
+ // Check for cycles before writing 250 lines of the same ~5 symbols
+ let cycled = |(runner, walker)| runner == walker;
+ let mut cyclic = false;
+ if let Some(period) = stack.iter().skip(1).step_by(2).zip(stack).position(cycled) {
+ let period = period.saturating_add(1); // avoid "what if wrapped?" branches
+ let Some(offset) = stack.iter().skip(period).zip(stack).position(cycled) else {
+ // impossible.
+ return;
+ };
+
+ // Count matching trace slices, else we could miscount "biphasic cycles"
+ // with the same period + loop entry but a different inner loop
+ let next_cycle = stack[offset..].chunks_exact(period).skip(1);
+ let cycles = 1 + next_cycle
+ .zip(stack[offset..].chunks_exact(period))
+ .filter(|(next, prev)| next == prev)
+ .count();
+ backtrace_stderr(&stack[..offset]);
+ written += offset;
+ consumed += offset;
+ if cycles > 1 {
+ raw_errln!("\n### cycle encountered after {offset} frames with period {period}");
+ backtrace_stderr(&stack[consumed..consumed + period]);
+ raw_errln!("### recursed {cycles} times\n");
+ written += period + 4;
+ consumed += period * cycles;
+ cyclic = true;
+ };
+ }
+ let rem = &stack[consumed..];
+ backtrace_stderr(rem);
+ raw_errln!("");
+ written += rem.len() + 1;
+
+ let random_depth = || 8 * 16; // chosen by random diceroll (2d20)
+ if cyclic || stack.len() > random_depth() {
+ // technically speculation, but assert it with confidence anyway.
+ // rustc only arrived in this signal handler because bad things happened
+ // and this message is for explaining it's not the programmer's fault
+ raw_errln!("note: rustc unexpectedly overflowed its stack! this is a bug");
+ written += 1;
+ }
+ if stack.len() == MAX_FRAMES {
+ raw_errln!("note: maximum backtrace depth reached, frames may have been lost");
+ written += 1;
+ }
+ raw_errln!("note: we would appreciate a report at https://github.com/rust-lang/rust");
+ written += 1;
+ if written > 24 {
+ // We probably just scrolled the earlier "we got SIGSEGV" message off the terminal
+ raw_errln!("note: backtrace dumped due to SIGSEGV! resuming signal");
+ };
+}
+
+/// When SIGSEGV is delivered to the process, print a stack trace and then exit.
+pub(super) fn install() {
+ unsafe {
+ let alt_stack_size: usize = min_sigstack_size() + 64 * 1024;
+ let mut alt_stack: libc::stack_t = mem::zeroed();
+ alt_stack.ss_sp = alloc(Layout::from_size_align(alt_stack_size, 1).unwrap()).cast();
+ alt_stack.ss_size = alt_stack_size;
+ libc::sigaltstack(&alt_stack, ptr::null_mut());
+
+ let mut sa: libc::sigaction = mem::zeroed();
+ sa.sa_sigaction = print_stack_trace as libc::sighandler_t;
+ sa.sa_flags = libc::SA_NODEFER | libc::SA_RESETHAND | libc::SA_ONSTACK;
+ libc::sigemptyset(&mut sa.sa_mask);
+ libc::sigaction(libc::SIGSEGV, &sa, ptr::null_mut());
+ }
+}
+
+/// Modern kernels on modern hardware can have dynamic signal stack sizes.
+#[cfg(any(target_os = "linux", target_os = "android"))]
+fn min_sigstack_size() -> usize {
+ const AT_MINSIGSTKSZ: core::ffi::c_ulong = 51;
+ let dynamic_sigstksz = unsafe { libc::getauxval(AT_MINSIGSTKSZ) };
+ // If getauxval couldn't find the entry, it returns 0,
+ // so take the higher of the "constant" and auxval.
+ // This transparently supports older kernels which don't provide AT_MINSIGSTKSZ
+ libc::MINSIGSTKSZ.max(dynamic_sigstksz as _)
+}
+
+/// Not all OS support hardware where this is needed.
+#[cfg(not(any(target_os = "linux", target_os = "android")))]
+fn min_sigstack_size() -> usize {
+ libc::MINSIGSTKSZ
+}