Skip to content

Commit 2f10d9a

Browse files
authored
Merge pull request #1084 from openmina/ledger-log
Display panics from other threads when main thread panics
2 parents 26ed848 + e9f0487 commit 2f10d9a

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ nix = { version = "0.26.2", features = ["signal"] }
3838
shellexpand = "3.1.0"
3939
dialoguer = "0.10.4"
4040
serde_json = "1.0.107"
41+
backtrace = "0.3"
4142

4243
[target.'cfg(not(target_family = "wasm"))'.dependencies]
4344
redux = { workspace = true, features=["serializable_callbacks"] }

cli/src/main.rs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use backtrace::Backtrace;
2+
use std::panic::PanicHookInfo;
3+
14
#[cfg(not(target_arch = "wasm32"))]
25
use tikv_jemallocator::Jemalloc;
36

@@ -56,8 +59,72 @@ fn setup_var_from_single_and_only_thread() {
5659
}
5760
}
5861

59-
fn main() -> anyhow::Result<()> {
62+
/// Mimic default hook:
63+
/// https://github.com/rust-lang/rust/blob/5986ff05d8480da038dd161b3a6aa79ff364a851/library/std/src/panicking.rs#L246
64+
///
65+
/// Unlike the default hook, this one allocates.
66+
/// We store (+ display) panics in non-main threads, and display them all when the main thread panics.
67+
#[cfg(not(target_family = "wasm"))]
68+
fn new_hook(info: &PanicHookInfo<'_>) {
69+
use std::any::Any;
70+
use std::io::Write;
71+
72+
fn payload_as_str(payload: &dyn Any) -> &str {
73+
if let Some(&s) = payload.downcast_ref::<&'static str>() {
74+
s
75+
} else if let Some(s) = payload.downcast_ref::<String>() {
76+
s.as_str()
77+
} else {
78+
"Box<dyn Any>"
79+
}
80+
}
81+
82+
static PREVIOUS_PANICS: std::sync::Mutex<Vec<Vec<u8>>> =
83+
const { std::sync::Mutex::new(Vec::new()) };
84+
85+
let mut s: Vec<u8> = Vec::with_capacity(64 * 1024);
86+
let backtrace = Backtrace::new();
87+
88+
let current = std::thread::current();
89+
let name = current.name().unwrap_or("<unnamed>");
90+
let location = info.location().unwrap();
91+
let msg = payload_as_str(info.payload());
92+
93+
let _ = writeln!(&mut s, "\nthread '{name}' panicked at {location}:\n{msg}");
94+
let _ = writeln!(&mut s, "{:#?}", &backtrace);
95+
96+
eprintln!("{}", String::from_utf8_lossy(&s));
97+
98+
if name != "main" {
99+
let Ok(mut previous) = PREVIOUS_PANICS.lock() else {
100+
return;
101+
};
102+
// Make sure we don't store too many panics
103+
if previous.len() < 256 {
104+
previous.push(s);
105+
eprintln!("Saved panic from thread '{name}'");
106+
} else {
107+
eprintln!("Panic from thread '{name}' not saved !");
108+
}
109+
} else {
110+
let Ok(previous) = PREVIOUS_PANICS.lock() else {
111+
return;
112+
};
113+
eprintln!("\nNumber of panics from others threads: {}", previous.len());
114+
for panic in previous.iter() {
115+
eprintln!("{}", String::from_utf8_lossy(panic));
116+
}
117+
}
118+
}
119+
120+
fn early_setup() {
60121
setup_var_from_single_and_only_thread();
122+
#[cfg(not(target_family = "wasm"))]
123+
std::panic::set_hook(Box::new(new_hook));
124+
}
125+
126+
fn main() -> anyhow::Result<()> {
127+
early_setup();
61128

62129
#[cfg(feature = "unsafe-signal-handlers")]
63130
unsafe_signal_handlers::setup();

0 commit comments

Comments
 (0)