Crash reporting

Provide an easy way to send panic messages, stacktrace and version
info, via Sentry (https://sentry.io).

For unclear reasons, this triggers a seemingly unrelated compile error:

	error[E0282]: type annotations needed
	    --> src/reader.rs:2000:81
	     |
	2000 |         if EXIT_STATE.load(Ordering::Relaxed) != ExitState::FinishedHandlers as _ {
	     |                                                                                 ^ cannot infer type
This commit is contained in:
Johannes Altmanninger 2024-12-09 10:56:32 +01:00
parent d3ed3037c5
commit 1c69fa22df
4 changed files with 1554 additions and 12 deletions

1499
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -50,6 +50,7 @@ fish-printf = { path = "./printf", features = ["widestring"] }
# available on macOS < 10.12. We can enable "getrandom" when we raise the
# minimum supported version to 10.12.
rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
sentry = "0.35.0"
widestring = "1.1.0"
# We need 0.9.0 specifically for some crash fixes.
terminfo = "0.9.0"

View file

@ -1,11 +1,15 @@
use std::{
panic::{set_hook, take_hook, UnwindSafe},
sync::atomic::{AtomicBool, Ordering},
panic::{set_hook, take_hook, PanicInfo, UnwindSafe},
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
time::Duration,
};
use libc::{SIGINT, SIG_DFL, STDIN_FILENO};
use once_cell::sync::OnceCell;
use sentry::integrations::panic::PanicIntegration;
use crate::{
common::{read_blocked, PROGRAM_NAME},
@ -54,12 +58,10 @@ pub fn panic_handler(main: impl FnOnce() -> i32 + UnwindSafe) -> ! {
unsafe { libc::sigaction(SIGINT, &act, std::ptr::null_mut()) };
}
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;
}
if read_line("Type 'report' to send a bug report: ") == b"report" {
report_crash(panic_info);
} else {
eprintf!("Not sending crash report\n");
}
}
std::process::abort();
@ -72,3 +74,49 @@ pub fn panic_handler(main: impl FnOnce() -> i32 + UnwindSafe) -> ! {
asan_maybe_exit(exit_status);
std::process::exit(exit_status)
}
fn read_line(prompt: &str) -> Vec<u8> {
eprintf!("%s", prompt);
let mut line = vec![];
let mut c = [0_u8; 1];
while let Ok(n) = read_blocked(STDIN_FILENO, &mut c) {
let c = c[0];
if n == 0 || matches!(c, b'\n' | b'\r') {
break;
}
line.push(c);
}
line
}
fn report_crash(panic_info: &PanicInfo) {
let email = read_line("optional email address or username: ");
let message = read_line("optional other info such as steps to reproduce: ");
let _sentry = sentry::init(("https://cef1ac6352bd78fa3dfe5e3e51e3358c@o4508437566586880.ingest.us.sentry.io/4508437569142784", sentry::ClientOptions {
release: sentry::release_name!(),
default_integrations: false,
integrations: vec![
Arc::new(sentry::integrations::backtrace::AttachStacktraceIntegration),
Arc::new(sentry::integrations::debug_images::DebugImagesIntegration::default()),
Arc::new(sentry::integrations::contexts::ContextIntegration::default()),
Arc::new(sentry::integrations::backtrace::ProcessStacktraceIntegration),
],
..Default::default()
}));
let mut event = PanicIntegration::new().event_from_panic_info(panic_info);
let event_id = event.event_id;
event.user = Some(sentry::User {
email: Some(String::from_utf8_lossy(&email).to_string()),
..Default::default()
});
event.message = Some(String::from_utf8_lossy(&message).to_string());
eprintf!("Sending crash report\n");
sentry::Hub::with_active(|hub| {
hub.capture_event(event);
if let Some(client) = hub.client() {
client.flush(None);
}
});
eprintf!("Sent crash report with ID %s\n", event_id.to_string());
}

View file

@ -1991,7 +1991,7 @@ impl<'a> Reader<'a> {
zelf.clear_pager();
}
if EXIT_STATE.load(Ordering::Relaxed) != ExitState::FinishedHandlers as _ {
if EXIT_STATE.load(Ordering::Relaxed) != ExitState::FinishedHandlers as u8 {
// The order of the two conditions below is important. Try to restore the mode
// in all cases, but only complain if interactive.
if unsafe { libc::tcsetattr(zelf.conf.inputfd, TCSANOW, &old_modes) } == -1