mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Explain SIGSEGV backtrace handler
...to both compiler contributors and other rustc invokers. The previous error messaging was extremely unfriendly, and potentially both confusing and alarming. The entire modules is extracted into a new file to help ease of reading, and plenty of comments get layered in. The design of the messaging is focused on preventing the overall purpose of the output from being too opaque in common cases, so users understand why it is there. There's not an immediate need for it being actionable, but some suggestions are offered anyways.
This commit is contained in:
parent
7bd81ee190
commit
4fa00c2507
@ -78,6 +78,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,
|
||||
@ -1427,72 +1436,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();
|
||||
|
76
compiler/rustc_driver_impl/src/signal_handler.rs
Normal file
76
compiler/rustc_driver_impl/src/signal_handler.rs
Normal file
@ -0,0 +1,76 @@
|
||||
//! Signal handler for rustc
|
||||
//! Primarily used to extract a backtrace from stack overflow
|
||||
|
||||
use std::alloc::{alloc, Layout};
|
||||
use std::{mem, ptr};
|
||||
|
||||
extern "C" {
|
||||
fn backtrace_symbols_fd(buffer: *const *mut libc::c_void, size: libc::c_int, fd: libc::c_int);
|
||||
}
|
||||
|
||||
/// 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];
|
||||
unsafe {
|
||||
// Collect return addresses
|
||||
let depth = libc::backtrace(STACK_TRACE.as_mut_ptr(), MAX_FRAMES as i32);
|
||||
if depth == 0 {
|
||||
return;
|
||||
}
|
||||
// Just a stack trace is cryptic. Explain what we're doing.
|
||||
write_raw_err("error: rustc interrupted by SIGSEGV, printing stack trace:\n\n");
|
||||
// Elaborate return addrs into symbols and write them directly to stderr
|
||||
backtrace_symbols_fd(STACK_TRACE.as_ptr(), depth, libc::STDERR_FILENO);
|
||||
if depth > 22 {
|
||||
// We probably just scrolled that "we got SIGSEGV" message off the terminal
|
||||
write_raw_err("\nerror: stack trace dumped due to SIGSEGV, possible stack overflow");
|
||||
};
|
||||
write_raw_err("\nerror: please report a bug to https://github.com/rust-lang/rust\n");
|
||||
}
|
||||
}
|
||||
|
||||
/// Write without locking stderr.
|
||||
///
|
||||
/// Only acceptable because everything will end soon anyways.
|
||||
fn write_raw_err(input: &str) {
|
||||
// We do not 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.
|
||||
let _ = unsafe { libc::write(libc::STDERR_FILENO, input.as_ptr().cast(), input.len()) };
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user