Prechádzať zdrojové kódy

Add backtrace to panic-handler feature

Gary Guo 3 rokov pred
rodič
commit
210e6def4c
2 zmenil súbory, kde vykonal 66 pridanie a 8 odobranie
  1. 12 8
      example/src/main.rs
  2. 54 0
      src/panic_handler.rs

+ 12 - 8
example/src/main.rs

@@ -36,16 +36,20 @@ fn bar() {
     foo()
 }
 
+fn main() {
+    let _ = unwind::panic::catch_unwind(|| {
+        bar();
+        println!("done");
+    });
+    println!("caught");
+    let _p = PanicOnDrop;
+    foo();
+}
+
 #[start]
-fn main(_argc: isize, _argv: *const *const u8) -> isize {
+fn start(_argc: isize, _argv: *const *const u8) -> isize {
     unwind::panic::catch_unwind(|| {
-        let _ = unwind::panic::catch_unwind(|| {
-            bar();
-            println!("done");
-        });
-        println!("caught");
-        let _p = PanicOnDrop;
-        foo();
+        main();
         0
     })
     .unwrap_or(101)

+ 54 - 0
src/panic_handler.rs

@@ -1,8 +1,11 @@
+use crate::abi::*;
 use crate::print::*;
 use alloc::boxed::Box;
 use core::any::Any;
 use core::cell::Cell;
+use core::ffi::c_void;
 use core::panic::{Location, PanicInfo};
+use core::sync::atomic::{AtomicI32, Ordering};
 
 #[thread_local]
 static PANIC_COUNT: Cell<usize> = Cell::new(0);
@@ -22,12 +25,63 @@ pub(crate) fn panic_caught() {
     PANIC_COUNT.set(0);
 }
 
+fn check_env() -> bool {
+    static ENV: AtomicI32 = AtomicI32::new(-1);
+
+    let env = ENV.load(Ordering::Relaxed);
+    if env != -1 {
+        return env != 0;
+    }
+
+    let val = unsafe {
+        let ptr = libc::getenv(b"RUST_BACKTRACE\0".as_ptr() as _);
+        if ptr.is_null() {
+            b""
+        } else {
+            let len = libc::strlen(ptr);
+            core::slice::from_raw_parts(ptr as *const u8, len)
+        }
+    };
+    let (note, env) = match val {
+        b"" => (true, false),
+        b"1" | b"full" => (false, true),
+        _ => (false, false),
+    };
+
+    // Issue a note for the first panic.
+    if ENV.swap(env as _, Ordering::Relaxed) == -1 && note {
+        eprintln!("note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace");
+    }
+    env
+}
+
+fn stack_trace() {
+    struct CallbackData {
+        counter: usize,
+    }
+    extern "C" fn callback(
+        unwind_ctx: &mut UnwindContext<'_>,
+        arg: *mut c_void,
+    ) -> UnwindReasonCode {
+        let data = unsafe { &mut *(arg as *mut CallbackData) };
+        data.counter += 1;
+        eprintln!("{:4}:{:#19x} - <unknown>", data.counter, _Unwind_GetIP(unwind_ctx));
+        UnwindReasonCode::NO_REASON
+    }
+    let mut data = CallbackData { counter: 0 };
+    _Unwind_Backtrace(callback, &mut data as *mut _ as _);
+}
+
 fn do_panic(msg: Box<dyn Any + Send>) -> ! {
     if PANIC_COUNT.get() >= 1 {
+        stack_trace();
         eprintln!("thread panicked while processing panic. aborting.");
         core::intrinsics::abort();
     }
     PANIC_COUNT.set(1);
+    if check_env() {
+        stack_trace();
+    }
     let code = crate::panic::begin_panic(Box::new(msg));
     eprintln!("failed to initiate panic, error {}", code.0);
     core::intrinsics::abort();