fish-shell/src/panic.rs

66 lines
2.2 KiB
Rust
Raw Normal View History

Restore terminal state again in panic handler Our panic handler attempts a blocking read from stdin and only exits after the user presses Enter. This is unconventional behavior and might cause surprise but there is a significant upside: crashes become more visible for terminals that don't already detect crashes (see ecdc9ce1d (Install a panic handler to avoid dropping crash stacktraces, 2024-03-24)). As reported in 4d0aa2b5d (Fix panic handler, 2024-08-28), the panic handler failed to exit fish if the panic happens on background threads. It would only exit the background thread (like autosuggestion/highlight/history-pager performer) itself. The fix was to abort the whole process. Aborting has the additional upside of generating a coredump. However since abort() skips stack unwinding, 4d0aa2b5d makes us no longer restore the terminal on panic. In particular, if the terminal supports kitty progressive enhancements, keys like ctrl-p will no longer work in say, a Bash parent shell. So it broke 121680147 (Use RAII for restoring term modes, 2024-03-24). Fix this while still aborting to create coredumps. This means we can't use RAII (for better or worse). The bad part is that we have to deal with added complexity; we need to make sure that we set the AT_EXIT handler only after all its inputs (like TERMINAL_MODE_ON_STARTUP) are initialized to a safe value, but also before any damage has been done to the terminal. I guess we can add a bunch of assertions. Unfortunately, if a background thread panics, I haven't yet figured out how to tell the main thread to do the blocking read. So the trick of "Press Enter to exit", which allows users to attach a debugger doesn't yet work for panics in background threads. We can probably figure that out later. Maybe use pthread_kill(3)? Of course we still create coredumps, so that's fine. As a temporary workaround, let's sleep for a bit so the user can at least see that there is a crash & stacktrace. One ugly bit here is that unit tests run AT_EXIT twice but it should be idempotent.
2024-10-12 07:53:31 +00:00
use std::{
panic::{set_hook, take_hook, UnwindSafe},
sync::atomic::{AtomicBool, Ordering},
time::Duration,
};
use libc::STDIN_FILENO;
Restore terminal state again in panic handler Our panic handler attempts a blocking read from stdin and only exits after the user presses Enter. This is unconventional behavior and might cause surprise but there is a significant upside: crashes become more visible for terminals that don't already detect crashes (see ecdc9ce1d (Install a panic handler to avoid dropping crash stacktraces, 2024-03-24)). As reported in 4d0aa2b5d (Fix panic handler, 2024-08-28), the panic handler failed to exit fish if the panic happens on background threads. It would only exit the background thread (like autosuggestion/highlight/history-pager performer) itself. The fix was to abort the whole process. Aborting has the additional upside of generating a coredump. However since abort() skips stack unwinding, 4d0aa2b5d makes us no longer restore the terminal on panic. In particular, if the terminal supports kitty progressive enhancements, keys like ctrl-p will no longer work in say, a Bash parent shell. So it broke 121680147 (Use RAII for restoring term modes, 2024-03-24). Fix this while still aborting to create coredumps. This means we can't use RAII (for better or worse). The bad part is that we have to deal with added complexity; we need to make sure that we set the AT_EXIT handler only after all its inputs (like TERMINAL_MODE_ON_STARTUP) are initialized to a safe value, but also before any damage has been done to the terminal. I guess we can add a bunch of assertions. Unfortunately, if a background thread panics, I haven't yet figured out how to tell the main thread to do the blocking read. So the trick of "Press Enter to exit", which allows users to attach a debugger doesn't yet work for panics in background threads. We can probably figure that out later. Maybe use pthread_kill(3)? Of course we still create coredumps, so that's fine. As a temporary workaround, let's sleep for a bit so the user can at least see that there is a crash & stacktrace. One ugly bit here is that unit tests run AT_EXIT twice but it should be idempotent.
2024-10-12 07:53:31 +00:00
use once_cell::sync::OnceCell;
use crate::{
common::{read_blocked, PROGRAM_NAME},
nix::isatty,
Restore terminal state again in panic handler Our panic handler attempts a blocking read from stdin and only exits after the user presses Enter. This is unconventional behavior and might cause surprise but there is a significant upside: crashes become more visible for terminals that don't already detect crashes (see ecdc9ce1d (Install a panic handler to avoid dropping crash stacktraces, 2024-03-24)). As reported in 4d0aa2b5d (Fix panic handler, 2024-08-28), the panic handler failed to exit fish if the panic happens on background threads. It would only exit the background thread (like autosuggestion/highlight/history-pager performer) itself. The fix was to abort the whole process. Aborting has the additional upside of generating a coredump. However since abort() skips stack unwinding, 4d0aa2b5d makes us no longer restore the terminal on panic. In particular, if the terminal supports kitty progressive enhancements, keys like ctrl-p will no longer work in say, a Bash parent shell. So it broke 121680147 (Use RAII for restoring term modes, 2024-03-24). Fix this while still aborting to create coredumps. This means we can't use RAII (for better or worse). The bad part is that we have to deal with added complexity; we need to make sure that we set the AT_EXIT handler only after all its inputs (like TERMINAL_MODE_ON_STARTUP) are initialized to a safe value, but also before any damage has been done to the terminal. I guess we can add a bunch of assertions. Unfortunately, if a background thread panics, I haven't yet figured out how to tell the main thread to do the blocking read. So the trick of "Press Enter to exit", which allows users to attach a debugger doesn't yet work for panics in background threads. We can probably figure that out later. Maybe use pthread_kill(3)? Of course we still create coredumps, so that's fine. As a temporary workaround, let's sleep for a bit so the user can at least see that there is a crash & stacktrace. One ugly bit here is that unit tests run AT_EXIT twice but it should be idempotent.
2024-10-12 07:53:31 +00:00
threads::{asan_maybe_exit, is_main_thread},
};
pub static AT_EXIT: OnceCell<Box<dyn Fn(bool) + Send + Sync>> = OnceCell::new();
Restore terminal state again in panic handler Our panic handler attempts a blocking read from stdin and only exits after the user presses Enter. This is unconventional behavior and might cause surprise but there is a significant upside: crashes become more visible for terminals that don't already detect crashes (see ecdc9ce1d (Install a panic handler to avoid dropping crash stacktraces, 2024-03-24)). As reported in 4d0aa2b5d (Fix panic handler, 2024-08-28), the panic handler failed to exit fish if the panic happens on background threads. It would only exit the background thread (like autosuggestion/highlight/history-pager performer) itself. The fix was to abort the whole process. Aborting has the additional upside of generating a coredump. However since abort() skips stack unwinding, 4d0aa2b5d makes us no longer restore the terminal on panic. In particular, if the terminal supports kitty progressive enhancements, keys like ctrl-p will no longer work in say, a Bash parent shell. So it broke 121680147 (Use RAII for restoring term modes, 2024-03-24). Fix this while still aborting to create coredumps. This means we can't use RAII (for better or worse). The bad part is that we have to deal with added complexity; we need to make sure that we set the AT_EXIT handler only after all its inputs (like TERMINAL_MODE_ON_STARTUP) are initialized to a safe value, but also before any damage has been done to the terminal. I guess we can add a bunch of assertions. Unfortunately, if a background thread panics, I haven't yet figured out how to tell the main thread to do the blocking read. So the trick of "Press Enter to exit", which allows users to attach a debugger doesn't yet work for panics in background threads. We can probably figure that out later. Maybe use pthread_kill(3)? Of course we still create coredumps, so that's fine. As a temporary workaround, let's sleep for a bit so the user can at least see that there is a crash & stacktrace. One ugly bit here is that unit tests run AT_EXIT twice but it should be idempotent.
2024-10-12 07:53:31 +00:00
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);
Restore terminal state again in panic handler Our panic handler attempts a blocking read from stdin and only exits after the user presses Enter. This is unconventional behavior and might cause surprise but there is a significant upside: crashes become more visible for terminals that don't already detect crashes (see ecdc9ce1d (Install a panic handler to avoid dropping crash stacktraces, 2024-03-24)). As reported in 4d0aa2b5d (Fix panic handler, 2024-08-28), the panic handler failed to exit fish if the panic happens on background threads. It would only exit the background thread (like autosuggestion/highlight/history-pager performer) itself. The fix was to abort the whole process. Aborting has the additional upside of generating a coredump. However since abort() skips stack unwinding, 4d0aa2b5d makes us no longer restore the terminal on panic. In particular, if the terminal supports kitty progressive enhancements, keys like ctrl-p will no longer work in say, a Bash parent shell. So it broke 121680147 (Use RAII for restoring term modes, 2024-03-24). Fix this while still aborting to create coredumps. This means we can't use RAII (for better or worse). The bad part is that we have to deal with added complexity; we need to make sure that we set the AT_EXIT handler only after all its inputs (like TERMINAL_MODE_ON_STARTUP) are initialized to a safe value, but also before any damage has been done to the terminal. I guess we can add a bunch of assertions. Unfortunately, if a background thread panics, I haven't yet figured out how to tell the main thread to do the blocking read. So the trick of "Press Enter to exit", which allows users to attach a debugger doesn't yet work for panics in background threads. We can probably figure that out later. Maybe use pthread_kill(3)? Of course we still create coredumps, so that's fine. As a temporary workaround, let's sleep for a bit so the user can at least see that there is a crash & stacktrace. One ugly bit here is that unit tests run AT_EXIT twice but it should be idempotent.
2024-10-12 07:53:31 +00:00
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);
Restore terminal state again in panic handler Our panic handler attempts a blocking read from stdin and only exits after the user presses Enter. This is unconventional behavior and might cause surprise but there is a significant upside: crashes become more visible for terminals that don't already detect crashes (see ecdc9ce1d (Install a panic handler to avoid dropping crash stacktraces, 2024-03-24)). As reported in 4d0aa2b5d (Fix panic handler, 2024-08-28), the panic handler failed to exit fish if the panic happens on background threads. It would only exit the background thread (like autosuggestion/highlight/history-pager performer) itself. The fix was to abort the whole process. Aborting has the additional upside of generating a coredump. However since abort() skips stack unwinding, 4d0aa2b5d makes us no longer restore the terminal on panic. In particular, if the terminal supports kitty progressive enhancements, keys like ctrl-p will no longer work in say, a Bash parent shell. So it broke 121680147 (Use RAII for restoring term modes, 2024-03-24). Fix this while still aborting to create coredumps. This means we can't use RAII (for better or worse). The bad part is that we have to deal with added complexity; we need to make sure that we set the AT_EXIT handler only after all its inputs (like TERMINAL_MODE_ON_STARTUP) are initialized to a safe value, but also before any damage has been done to the terminal. I guess we can add a bunch of assertions. Unfortunately, if a background thread panics, I haven't yet figured out how to tell the main thread to do the blocking read. So the trick of "Press Enter to exit", which allows users to attach a debugger doesn't yet work for panics in background threads. We can probably figure that out later. Maybe use pthread_kill(3)? Of course we still create coredumps, so that's fine. As a temporary workaround, let's sleep for a bit so the user can at least see that there is a crash & stacktrace. One ugly bit here is that unit tests run AT_EXIT twice but it should be idempotent.
2024-10-12 07:53:31 +00:00
}
eprintf!(
Restore terminal state again in panic handler Our panic handler attempts a blocking read from stdin and only exits after the user presses Enter. This is unconventional behavior and might cause surprise but there is a significant upside: crashes become more visible for terminals that don't already detect crashes (see ecdc9ce1d (Install a panic handler to avoid dropping crash stacktraces, 2024-03-24)). As reported in 4d0aa2b5d (Fix panic handler, 2024-08-28), the panic handler failed to exit fish if the panic happens on background threads. It would only exit the background thread (like autosuggestion/highlight/history-pager performer) itself. The fix was to abort the whole process. Aborting has the additional upside of generating a coredump. However since abort() skips stack unwinding, 4d0aa2b5d makes us no longer restore the terminal on panic. In particular, if the terminal supports kitty progressive enhancements, keys like ctrl-p will no longer work in say, a Bash parent shell. So it broke 121680147 (Use RAII for restoring term modes, 2024-03-24). Fix this while still aborting to create coredumps. This means we can't use RAII (for better or worse). The bad part is that we have to deal with added complexity; we need to make sure that we set the AT_EXIT handler only after all its inputs (like TERMINAL_MODE_ON_STARTUP) are initialized to a safe value, but also before any damage has been done to the terminal. I guess we can add a bunch of assertions. Unfortunately, if a background thread panics, I haven't yet figured out how to tell the main thread to do the blocking read. So the trick of "Press Enter to exit", which allows users to attach a debugger doesn't yet work for panics in background threads. We can probably figure that out later. Maybe use pthread_kill(3)? Of course we still create coredumps, so that's fine. As a temporary workaround, let's sleep for a bit so the user can at least see that there is a crash & stacktrace. One ugly bit here is that unit tests run AT_EXIT twice but it should be idempotent.
2024-10-12 07:53:31 +00:00
"%s crashed, please report a bug.",
PROGRAM_NAME.get().unwrap(),
);
if !is_main_thread() {
Restore terminal state again in panic handler Our panic handler attempts a blocking read from stdin and only exits after the user presses Enter. This is unconventional behavior and might cause surprise but there is a significant upside: crashes become more visible for terminals that don't already detect crashes (see ecdc9ce1d (Install a panic handler to avoid dropping crash stacktraces, 2024-03-24)). As reported in 4d0aa2b5d (Fix panic handler, 2024-08-28), the panic handler failed to exit fish if the panic happens on background threads. It would only exit the background thread (like autosuggestion/highlight/history-pager performer) itself. The fix was to abort the whole process. Aborting has the additional upside of generating a coredump. However since abort() skips stack unwinding, 4d0aa2b5d makes us no longer restore the terminal on panic. In particular, if the terminal supports kitty progressive enhancements, keys like ctrl-p will no longer work in say, a Bash parent shell. So it broke 121680147 (Use RAII for restoring term modes, 2024-03-24). Fix this while still aborting to create coredumps. This means we can't use RAII (for better or worse). The bad part is that we have to deal with added complexity; we need to make sure that we set the AT_EXIT handler only after all its inputs (like TERMINAL_MODE_ON_STARTUP) are initialized to a safe value, but also before any damage has been done to the terminal. I guess we can add a bunch of assertions. Unfortunately, if a background thread panics, I haven't yet figured out how to tell the main thread to do the blocking read. So the trick of "Press Enter to exit", which allows users to attach a debugger doesn't yet work for panics in background threads. We can probably figure that out later. Maybe use pthread_kill(3)? Of course we still create coredumps, so that's fine. As a temporary workaround, let's sleep for a bit so the user can at least see that there is a crash & stacktrace. One ugly bit here is that unit tests run AT_EXIT twice but it should be idempotent.
2024-10-12 07:53:31 +00:00
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;
}
}
}
Restore terminal state again in panic handler Our panic handler attempts a blocking read from stdin and only exits after the user presses Enter. This is unconventional behavior and might cause surprise but there is a significant upside: crashes become more visible for terminals that don't already detect crashes (see ecdc9ce1d (Install a panic handler to avoid dropping crash stacktraces, 2024-03-24)). As reported in 4d0aa2b5d (Fix panic handler, 2024-08-28), the panic handler failed to exit fish if the panic happens on background threads. It would only exit the background thread (like autosuggestion/highlight/history-pager performer) itself. The fix was to abort the whole process. Aborting has the additional upside of generating a coredump. However since abort() skips stack unwinding, 4d0aa2b5d makes us no longer restore the terminal on panic. In particular, if the terminal supports kitty progressive enhancements, keys like ctrl-p will no longer work in say, a Bash parent shell. So it broke 121680147 (Use RAII for restoring term modes, 2024-03-24). Fix this while still aborting to create coredumps. This means we can't use RAII (for better or worse). The bad part is that we have to deal with added complexity; we need to make sure that we set the AT_EXIT handler only after all its inputs (like TERMINAL_MODE_ON_STARTUP) are initialized to a safe value, but also before any damage has been done to the terminal. I guess we can add a bunch of assertions. Unfortunately, if a background thread panics, I haven't yet figured out how to tell the main thread to do the blocking read. So the trick of "Press Enter to exit", which allows users to attach a debugger doesn't yet work for panics in background threads. We can probably figure that out later. Maybe use pthread_kill(3)? Of course we still create coredumps, so that's fine. As a temporary workaround, let's sleep for a bit so the user can at least see that there is a crash & stacktrace. One ugly bit here is that unit tests run AT_EXIT twice but it should be idempotent.
2024-10-12 07:53:31 +00:00
std::process::abort();
}));
}
let exit_status = main();
Restore terminal state again in panic handler Our panic handler attempts a blocking read from stdin and only exits after the user presses Enter. This is unconventional behavior and might cause surprise but there is a significant upside: crashes become more visible for terminals that don't already detect crashes (see ecdc9ce1d (Install a panic handler to avoid dropping crash stacktraces, 2024-03-24)). As reported in 4d0aa2b5d (Fix panic handler, 2024-08-28), the panic handler failed to exit fish if the panic happens on background threads. It would only exit the background thread (like autosuggestion/highlight/history-pager performer) itself. The fix was to abort the whole process. Aborting has the additional upside of generating a coredump. However since abort() skips stack unwinding, 4d0aa2b5d makes us no longer restore the terminal on panic. In particular, if the terminal supports kitty progressive enhancements, keys like ctrl-p will no longer work in say, a Bash parent shell. So it broke 121680147 (Use RAII for restoring term modes, 2024-03-24). Fix this while still aborting to create coredumps. This means we can't use RAII (for better or worse). The bad part is that we have to deal with added complexity; we need to make sure that we set the AT_EXIT handler only after all its inputs (like TERMINAL_MODE_ON_STARTUP) are initialized to a safe value, but also before any damage has been done to the terminal. I guess we can add a bunch of assertions. Unfortunately, if a background thread panics, I haven't yet figured out how to tell the main thread to do the blocking read. So the trick of "Press Enter to exit", which allows users to attach a debugger doesn't yet work for panics in background threads. We can probably figure that out later. Maybe use pthread_kill(3)? Of course we still create coredumps, so that's fine. As a temporary workaround, let's sleep for a bit so the user can at least see that there is a crash & stacktrace. One ugly bit here is that unit tests run AT_EXIT twice but it should be idempotent.
2024-10-12 07:53:31 +00:00
if let Some(at_exit) = AT_EXIT.get() {
(at_exit)(false);
Restore terminal state again in panic handler Our panic handler attempts a blocking read from stdin and only exits after the user presses Enter. This is unconventional behavior and might cause surprise but there is a significant upside: crashes become more visible for terminals that don't already detect crashes (see ecdc9ce1d (Install a panic handler to avoid dropping crash stacktraces, 2024-03-24)). As reported in 4d0aa2b5d (Fix panic handler, 2024-08-28), the panic handler failed to exit fish if the panic happens on background threads. It would only exit the background thread (like autosuggestion/highlight/history-pager performer) itself. The fix was to abort the whole process. Aborting has the additional upside of generating a coredump. However since abort() skips stack unwinding, 4d0aa2b5d makes us no longer restore the terminal on panic. In particular, if the terminal supports kitty progressive enhancements, keys like ctrl-p will no longer work in say, a Bash parent shell. So it broke 121680147 (Use RAII for restoring term modes, 2024-03-24). Fix this while still aborting to create coredumps. This means we can't use RAII (for better or worse). The bad part is that we have to deal with added complexity; we need to make sure that we set the AT_EXIT handler only after all its inputs (like TERMINAL_MODE_ON_STARTUP) are initialized to a safe value, but also before any damage has been done to the terminal. I guess we can add a bunch of assertions. Unfortunately, if a background thread panics, I haven't yet figured out how to tell the main thread to do the blocking read. So the trick of "Press Enter to exit", which allows users to attach a debugger doesn't yet work for panics in background threads. We can probably figure that out later. Maybe use pthread_kill(3)? Of course we still create coredumps, so that's fine. As a temporary workaround, let's sleep for a bit so the user can at least see that there is a crash & stacktrace. One ugly bit here is that unit tests run AT_EXIT twice but it should be idempotent.
2024-10-12 07:53:31 +00:00
}
asan_maybe_exit(exit_status);
std::process::exit(exit_status)
}