1use core::{
2 cell::RefCell,
3 fmt::{Debug, Display},
4 panic::PanicInfo,
5};
6use lazy_static::lazy_static;
7use std::{
8 panic::{catch_unwind, AssertUnwindSafe, RefUnwindSafe},
9 thread_local,
10};
11
12macro_rules! backtrace {
13 () => {{
14 if CAPTURE_BACKTRACE.with(|capture| *capture.borrow()) {
15 Some(std::backtrace::Backtrace::capture())
16 } else {
17 None
18 }
19 }};
20}
21
22thread_local! {
23 static ERROR: RefCell<Option<PanicError>> = const { RefCell::new(None) };
24 static CAPTURE_BACKTRACE: RefCell<bool> = RefCell::new(*RUST_BACKTRACE);
25 static FORWARD_PANIC: RefCell<bool> = const { RefCell::new(true) };
26 static THREAD_NAME: String = String::from(std::thread::current().name().unwrap_or("main"));
27}
28
29lazy_static! {
30 static ref RUST_BACKTRACE: bool = std::env::var("RUST_BACKTRACE")
31 .ok()
32 .map(|v| v == "1")
33 .unwrap_or(false);
34 static ref PANIC_HOOK: () = {
35 let prev_hook = std::panic::take_hook();
36
37 std::panic::set_hook(Box::new(move |reason| {
38 let panic = PanicError {
39 message: reason.to_string(),
40 location: reason.location().map(|l| l.to_string()),
41 backtrace: backtrace!(),
42 thread_name: thread_name(),
43 };
44 ERROR.with(|error| {
45 *error.borrow_mut() = Some(panic);
46 });
47 if FORWARD_PANIC.with(|forward| *forward.borrow()) {
48 prev_hook(reason);
49 }
50 }));
51 };
52}
53
54#[derive(Debug)]
55pub struct PanicError {
56 pub(crate) message: String,
57 #[allow(dead_code)]
59 pub(crate) location: Option<String>,
60 #[allow(dead_code)]
61 pub(crate) backtrace: Option<std::backtrace::Backtrace>,
62 #[allow(dead_code)]
63 pub(crate) thread_name: String,
64}
65
66impl std::error::Error for PanicError {}
67
68impl Display for PanicError {
69 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
70 write!(f, "{}", self.message)
71 }
72}
73
74impl PanicError {
75 pub(crate) fn new(message: String) -> Self {
76 Self {
77 message,
78 location: None,
79 backtrace: backtrace!(),
80 thread_name: thread_name(),
81 }
82 }
83}
84
85#[inline]
86pub fn catch<F: RefUnwindSafe + FnOnce() -> Result<bool, PanicError>>(
87 fun: F,
88) -> Result<bool, PanicError> {
89 let res = catch_unwind(AssertUnwindSafe(|| __panic_marker_start__(fun)));
90 match res {
91 Ok(Ok(v)) => Ok(v),
92 Ok(Err(err)) => Err(err),
93 Err(err) => {
94 if let Some(err) = take_panic() {
95 return Err(err);
96 }
97 macro_rules! try_downcast {
98 ($ty:ty, $fmt:expr) => {
99 if let Some(err) = err.downcast_ref::<$ty>() {
100 return Err(PanicError::new(format!($fmt, err)));
101 }
102 };
103 }
104
105 #[cfg(feature = "any")]
107 if err.downcast_ref::<bolero_generator::any::Error>().is_some() {
108 return Ok(false);
109 }
110
111 try_downcast!(PanicInfo, "{}");
112 try_downcast!(anyhow::Error, "{}");
113 try_downcast!(String, "{}");
114 try_downcast!(&'static str, "{}");
115 try_downcast!(Box<dyn Display>, "{}");
116 try_downcast!(Box<dyn Debug>, "{:?}");
117 Err(PanicError::new(
118 "thread panicked with an unknown error".to_string(),
119 ))
120 }
121 }
122}
123
124#[inline]
125pub fn take_panic() -> Option<PanicError> {
126 ERROR.with(|error| error.borrow_mut().take())
127}
128
129#[inline]
130pub fn capture_backtrace(value: bool) -> bool {
131 CAPTURE_BACKTRACE.with(|cell| {
132 let prev = *cell.borrow();
133 *cell.borrow_mut() = value;
134 prev
135 })
136}
137
138#[inline]
139pub fn forward_panic(value: bool) -> bool {
140 FORWARD_PANIC.with(|cell| {
141 let prev = *cell.borrow();
142 *cell.borrow_mut() = value;
143 prev
144 })
145}
146
147#[inline]
148pub fn set_hook() {
149 *PANIC_HOOK
150}
151
152#[inline]
153pub fn rust_backtrace() -> bool {
154 *RUST_BACKTRACE
155}
156
157#[inline]
158pub fn thread_name() -> String {
159 THREAD_NAME.with(|cell| cell.clone())
160}
161
162#[inline(never)]
163fn __panic_marker_start__<F: FnOnce() -> R, R>(f: F) -> R {
164 f()
165}