fish-shell/src/panic.rs
2024-11-11 16:45:13 -06:00

65 lines
2.2 KiB
Rust

use std::{
panic::{set_hook, take_hook, UnwindSafe},
sync::atomic::{AtomicBool, Ordering},
time::Duration,
};
use libc::STDIN_FILENO;
use once_cell::sync::OnceCell;
use crate::{
common::{read_blocked, PROGRAM_NAME},
nix::isatty,
threads::{asan_maybe_exit, is_main_thread},
};
pub static AT_EXIT: OnceCell<Box<dyn Fn(bool) + Send + Sync>> = OnceCell::new();
pub fn panic_handler(main: impl FnOnce() -> i32 + UnwindSafe) -> ! {
// The isatty() check will stop us from hanging in most fish tests, but not those
// running in a simulated terminal emulator environment (such as the tmux or pexpect
// tests). The FISH_FAST_FAIL environment variable is set in the test driver to
// prevent the test suite from hanging on panic.
if isatty(STDIN_FILENO) && std::env::var_os("FISH_FAST_FAIL").is_none() {
let standard_hook = take_hook();
set_hook(Box::new(move |panic_info| {
standard_hook(panic_info);
static PANICKING: AtomicBool = AtomicBool::new(false);
if PANICKING
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
.is_err()
{
return;
}
if let Some(at_exit) = AT_EXIT.get() {
(at_exit)(false);
}
eprintf!(
"%s crashed, please report a bug.",
PROGRAM_NAME.get().unwrap(),
);
if !is_main_thread() {
eprintf!("\n");
std::thread::sleep(Duration::from_secs(1));
} else {
eprintf!(" Debug PID %d or press Enter to exit\n", unsafe {
libc::getpid()
});
let mut buf = [0_u8; 1];
while let Ok(n) = read_blocked(STDIN_FILENO, &mut buf) {
if n == 0 || matches!(buf[0], b'q' | b'\n' | b'\r') {
eprintf!("\n");
break;
}
}
}
std::process::abort();
}));
}
let exit_status = main();
if let Some(at_exit) = AT_EXIT.get() {
(at_exit)(false);
}
asan_maybe_exit(exit_status);
std::process::exit(exit_status)
}