-
Notifications
You must be signed in to change notification settings - Fork 903
/
panic.rs
46 lines (40 loc) · 1.4 KB
/
panic.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#[derive(Default, Debug)]
pub(crate) struct PanicError {
pub(crate) info: String,
pub(crate) backtrace: Option<std::backtrace::Backtrace>,
}
impl std::fmt::Display for PanicError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.info)?;
if let Some(backtrace) = &self.backtrace {
writeln!(f, "Backtrace: {backtrace}")
} else {
Ok(())
}
}
}
thread_local! {
static LAST_PANIC: std::cell::Cell<Option<PanicError>> = const { std::cell::Cell::new(None) };
}
/// [`catch_unwind`](std::panic::catch_unwind) wrapper that sets a custom [`set_hook`](std::panic::set_hook)
/// to extract the backtrace. The original panic-hook gets restored before returning.
pub(crate) fn catch_unwind<F, R>(f: F) -> Result<R, PanicError>
where
F: FnOnce() -> R + std::panic::UnwindSafe,
{
let prev = std::panic::take_hook();
std::panic::set_hook(Box::new(|info| {
let info = info.to_string();
let backtrace = std::backtrace::Backtrace::force_capture();
LAST_PANIC.with(|cell| {
cell.set(Some(PanicError {
info,
backtrace: Some(backtrace),
}));
});
}));
let result = std::panic::catch_unwind(f)
.map_err(|_| LAST_PANIC.with(std::cell::Cell::take).unwrap_or_default());
std::panic::set_hook(prev);
result
}