|
| 1 | +use backtrace::Backtrace; |
| 2 | +use std::panic::PanicHookInfo; |
| 3 | + |
1 | 4 | #[cfg(not(target_arch = "wasm32"))]
|
2 | 5 | use tikv_jemallocator::Jemalloc;
|
3 | 6 |
|
@@ -56,8 +59,72 @@ fn setup_var_from_single_and_only_thread() {
|
56 | 59 | }
|
57 | 60 | }
|
58 | 61 |
|
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() { |
60 | 121 | 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(); |
61 | 128 |
|
62 | 129 | #[cfg(feature = "unsafe-signal-handlers")]
|
63 | 130 | unsafe_signal_handlers::setup();
|
|
0 commit comments