Skip to content

Commit 47c5e28

Browse files
pzhan9facebook-github-bot
authored andcommitted
capture panic message inside panic_handler (#1890)
Summary: The main motivation is by making this change, we can log panic message here too: https://www.internalfb.com/code/fbsource/[4a662228cd8bdf2bdf9b760e705cc9958f85e55c]/fbcode/monarch/hyperactor/src/panic_handler.rs?lines=47 it currently does not and caused quite some confusion for me, since I thought there is a bug in the panic catching logic. Differential Revision: D87086712
1 parent 76bc1ae commit 47c5e28

File tree

2 files changed

+37
-28
lines changed

2 files changed

+37
-28
lines changed

hyperactor/src/panic_handler.rs

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,19 @@ use std::future::Future;
1515
use std::ops::Deref;
1616
use std::panic;
1717

18+
/// A struct to store the message and backtrace from a panic.
19+
#[derive(Clone)]
20+
pub struct PanicInfo {
21+
/// The message from the panic.
22+
pub message: String,
23+
/// The backtrace from the panic.
24+
pub backtrace: String,
25+
}
26+
1827
tokio::task_local! {
1928
/// A task_local variable to store the backtrace from a panic, so it can be
2029
/// retrieved later.
21-
static BACKTRACE: RefCell<Option<String>>;
30+
static BACKTRACE: RefCell<Option<PanicInfo>>;
2231
}
2332

2433
/// Call this from the main method of your application, and use it in conjunction
@@ -33,9 +42,23 @@ pub fn set_panic_hook() {
3342
|| "unavailable".to_owned(),
3443
|loc: &panic::Location<'_>| format!("{}:{}:{}", loc.file(), loc.line(), loc.column()),
3544
);
45+
// Extract the panic message from the payload
46+
let panic_msg = if let Some(&s) = info.payload().downcast_ref::<&str>() {
47+
s.to_string()
48+
} else if let Some(s) = info.payload().downcast_ref::<String>() {
49+
s.clone()
50+
} else {
51+
"panic message cannot be downcasted".to_string()
52+
};
53+
54+
tracing::error!("stacktrace"=%backtrace, "panic at {loc}: {panic_msg}");
55+
3656
let _result = BACKTRACE.try_with(|entry| match entry.try_borrow_mut() {
3757
Ok(mut entry_ref) => {
38-
*entry_ref = Some(format!("panicked at {loc}\n{backtrace}"));
58+
*entry_ref = Some(PanicInfo {
59+
message: panic_msg,
60+
backtrace: format!("panicked at {loc}\n{backtrace}"),
61+
});
3962
}
4063
Err(borrow_mut_error) => {
4164
eprintln!(
@@ -44,7 +67,6 @@ pub fn set_panic_hook() {
4467
);
4568
}
4669
});
47-
tracing::error!("stacktrace"=%backtrace, "panic at {loc}");
4870

4971
// Execute the default hood to preserve the default behavior.
5072
prev(info);
@@ -62,11 +84,11 @@ where
6284

6385
/// Take the backtrace from the task_local variable, and reset the task_local to
6486
/// None. Return error if the backtrace is not stored, or cannot be retrieved.
65-
pub fn take_panic_backtrace() -> Result<String, anyhow::Error> {
87+
pub fn take_panic_backtrace() -> Result<PanicInfo, anyhow::Error> {
6688
BACKTRACE.try_with(|entry| {
6789
entry.try_borrow_mut().map(|mut entry_ref| {
6890
let result = match entry_ref.deref() {
69-
Some(bt) => Ok(bt.to_string()),
91+
Some(bt) => Ok(bt.clone()),
7092
None => Err(anyhow::anyhow!("nothing is stored in task_local")),
7193
};
7294
// Clear the task_local because the backtrace has been retrieve.
@@ -143,11 +165,9 @@ mod tests {
143165
.await;
144166
assert!(result.is_err());
145167
if backtrace_captured {
146-
assert!(
147-
take_panic_backtrace()
148-
.unwrap()
149-
.contains("verify_inner_panic")
150-
);
168+
let info = take_panic_backtrace().unwrap();
169+
assert_eq!(info.message, "wow!");
170+
assert!(info.backtrace.contains("verify_inner_panic"));
151171
} else {
152172
assert!(take_panic_backtrace().is_err());
153173
}
@@ -169,11 +189,9 @@ mod tests {
169189
assert!(result.is_ok());
170190

171191
// Verify the outer task can get its own backtrace.
172-
assert!(
173-
take_panic_backtrace()
174-
.unwrap()
175-
.contains("test_nested_tasks")
176-
);
192+
let info = take_panic_backtrace().unwrap();
193+
assert_eq!(info.message, "boom!");
194+
assert!(info.backtrace.contains("test_nested_tasks"));
177195
})
178196
.await;
179197
}

hyperactor/src/proc.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,23 +1247,14 @@ impl<A: Actor> Instance<A> {
12471247
.await
12481248
{
12491249
Ok(result) => result,
1250-
Err(err) => {
1251-
// This is only the error message. Backtrace is not included.
1250+
Err(_) => {
12521251
did_panic = true;
1253-
let err_msg = err
1254-
.downcast_ref::<&str>()
1255-
.copied()
1256-
.or_else(|| err.downcast_ref::<String>().map(|s| s.as_str()))
1257-
.unwrap_or("panic cannot be downcasted");
1258-
1259-
let backtrace = panic_handler::take_panic_backtrace()
1252+
let panic_info = panic_handler::take_panic_backtrace()
1253+
.map(|info| format!("{}\n{}", info.message, info.backtrace))
12601254
.unwrap_or_else(|e| format!("Cannot take backtrace due to: {:?}", e));
12611255
Err(ActorError::new(
12621256
self.self_id(),
1263-
ActorErrorKind::panic(anyhow::anyhow!(
1264-
"{}
1265-
{}", err_msg, backtrace
1266-
)),
1257+
ActorErrorKind::panic(anyhow::anyhow!(panic_info)),
12671258
))
12681259
}
12691260
};

0 commit comments

Comments
 (0)