Skip to content

Commit acad7df

Browse files
committed
analyze: panic_detail: call default panic hook when not in catch_unwind
1 parent 898d92e commit acad7df

File tree

2 files changed

+66
-27
lines changed

2 files changed

+66
-27
lines changed

c2rust-analyze/src/main.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1054,7 +1054,10 @@ impl rustc_driver::Callbacks for AnalysisCallbacks {
10541054

10551055
fn main() -> rustc_interface::interface::Result<()> {
10561056
init_logger();
1057-
panic::set_hook(Box::new(panic_detail::panic_hook));
1057+
let default_panic_hook = panic::take_hook();
1058+
panic::set_hook(Box::new(move |info| {
1059+
panic_detail::panic_hook(&default_panic_hook, info)
1060+
}));
10581061
let args = env::args().collect::<Vec<_>>();
10591062
rustc_driver::RunCompiler::new(&args, &mut AnalysisCallbacks).run()
10601063
}

c2rust-analyze/src/panic_detail.rs

Lines changed: 62 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -62,43 +62,79 @@ impl PanicDetail {
6262
}
6363
}
6464

65+
enum PanicState {
66+
/// The current thread is not in a `panic_detail::catch_unwind` call.
67+
OutsideCatchUnwind,
68+
/// The current thread is inside a `panic_detail::catch_unwind` call, but no panic has occurred
69+
/// yet.
70+
InsideCatchUnwind,
71+
/// The current thread panicked while inside `panic_detail::catch_unwind`, producing a
72+
/// `PanicDetail`, and is in the process of unwinding.
73+
Unwinding(PanicDetail),
74+
}
75+
6576
thread_local! {
66-
static CURRENT_PANIC_DETAIL: Cell<Option<PanicDetail>> = Cell::new(None);
77+
static CURRENT_PANIC_DETAIL: Cell<PanicState> =
78+
Cell::new(PanicState::OutsideCatchUnwind);
6779
}
6880

6981
/// Panic hook for use with [`std::panic::set_hook`]. This builds a `PanicDetail` for each panic
7082
/// and stores it for later retrieval by [`take_current`].
71-
pub fn panic_hook(info: &PanicInfo) {
72-
let bt = Backtrace::new();
73-
let detail = PanicDetail {
74-
msg: panic_to_string(info.payload()),
75-
loc: info.location().map(|l| l.to_string()),
76-
relevant_loc: guess_relevant_loc(&bt),
77-
backtrace: Some(bt),
78-
span: CURRENT_SPAN.with(|cell| cell.get()),
79-
};
80-
let old = CURRENT_PANIC_DETAIL.with(|cell| cell.replace(Some(detail)));
81-
if let Some(old) = old {
82-
warn!("discarding old panic detail: {:?}", old);
83-
}
84-
}
83+
pub fn panic_hook(default_hook: &dyn Fn(&PanicInfo), info: &PanicInfo) {
84+
CURRENT_PANIC_DETAIL.with(|cell| {
85+
// Take the old value, replacing it with something arbitrary.
86+
let old = cell.replace(PanicState::OutsideCatchUnwind);
87+
match old {
88+
PanicState::OutsideCatchUnwind => {
89+
// No special behavior is needed. Call the default panic hook instead.
90+
default_hook(info);
91+
return;
92+
}
93+
PanicState::InsideCatchUnwind => {}
94+
PanicState::Unwinding(pd) => {
95+
warn!("discarding old panic detail: {:?}", pd);
96+
}
97+
}
8598

86-
/// Get the [`PanicDetail`] of the most recent panic. This clears the internal storage, so if this
87-
/// is called twice in a row without an intervening panic, the second call always returns `None`.
88-
fn take_current() -> Option<PanicDetail> {
89-
CURRENT_PANIC_DETAIL.with(|cell| cell.take())
99+
// We are inside `panic_detail::catch_unwind`. Build a `PanicDetail` for this panic and
100+
// save it.
101+
let bt = Backtrace::new();
102+
let detail = PanicDetail {
103+
msg: panic_to_string(info.payload()),
104+
loc: info.location().map(|l| l.to_string()),
105+
relevant_loc: guess_relevant_loc(&bt),
106+
backtrace: Some(bt),
107+
span: CURRENT_SPAN.with(|cell| cell.get()),
108+
};
109+
cell.set(PanicState::Unwinding(detail));
110+
});
90111
}
91112

92113
/// Like `std::panic::catch_unwind`, but returns a `PanicDetail` instead of `Box<dyn Any>` on
93114
/// panic.
94115
pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R, PanicDetail> {
95-
panic::catch_unwind(f).map_err(|e| {
96-
take_current().unwrap_or_else(|| {
97-
let msg = panic_to_string(&e);
98-
warn!("missing panic detail; caught message {:?}", msg);
99-
PanicDetail::new(msg)
100-
})
101-
})
116+
let old = CURRENT_PANIC_DETAIL.with(|cell| cell.replace(PanicState::InsideCatchUnwind));
117+
let r = panic::catch_unwind(f);
118+
let new = CURRENT_PANIC_DETAIL.with(|cell| cell.replace(old));
119+
120+
match r {
121+
Ok(x) => {
122+
debug_assert!(matches!(new, PanicState::InsideCatchUnwind));
123+
Ok(x)
124+
}
125+
Err(e) => {
126+
debug_assert!(!matches!(new, PanicState::OutsideCatchUnwind));
127+
let pd = match new {
128+
PanicState::Unwinding(pd) => pd,
129+
_ => {
130+
let msg = panic_to_string(&e);
131+
warn!("missing panic detail; caught message {:?}", msg);
132+
PanicDetail::new(msg)
133+
}
134+
};
135+
Err(pd)
136+
}
137+
}
102138
}
103139

104140
/// Crude heuristic to guess the first interesting location in a [`Backtrace`], skipping over

0 commit comments

Comments
 (0)