|
| 1 | +use crate::ffi::{CStr, c_void}; |
| 2 | +use crate::mem::MaybeUninit; |
| 3 | +use crate::ptr; |
| 4 | + |
| 5 | +/// Prints the current backtrace, even in a signal handlers. |
| 6 | +/// |
| 7 | +/// Since `backtrace` requires locking and memory allocation, it cannot be used |
| 8 | +/// from inside a signal handler. Instead, this uses `libunwind` and `dladdr`, |
| 9 | +/// even though both of them are not guaranteed to be async-signal-safe, strictly |
| 10 | +/// speaking. However, at least LLVM's libunwind (used by macOS) has a [test] for |
| 11 | +/// unwinding in signal handlers, and `dladdr` is used by `backtrace_symbols_fd` |
| 12 | +/// in glibc, which it [documents] as async-signal-safe. |
| 13 | +/// |
| 14 | +/// In practice, this hack works well enough on GNU/Linux and macOS (and perhaps |
| 15 | +/// some other platforms). Realistically, the worst thing that can happen is that |
| 16 | +/// the stack overflow occurred inside the dynamic loaded while it holds some sort |
| 17 | +/// of lock, which could result in a deadlock if that happens in just the right |
| 18 | +/// moment. That's unlikely enough and not the *worst* thing to happen considering |
| 19 | +/// that a stack overflow is already an unrecoverable error and most likely |
| 20 | +/// indicates a bug. |
| 21 | +/// |
| 22 | +/// [test]: https://github.com/llvm/llvm-project/blob/a6385a3fc8a88f092d07672210a1e773481c2919/libunwind/test/signal_unwind.pass.cpp |
| 23 | +/// [documents]: https://www.gnu.org/software/libc/manual/html_node/Backtraces.html#index-backtrace_005fsymbols_005ffd |
| 24 | +pub fn print() { |
| 25 | + extern "C" fn frame( |
| 26 | + ctx: *mut unwind::_Unwind_Context, |
| 27 | + arg: *mut c_void, |
| 28 | + ) -> unwind::_Unwind_Reason_Code { |
| 29 | + let count = unsafe { &mut *(arg as *mut usize) }; |
| 30 | + let depth = *count; |
| 31 | + *count += 1; |
| 32 | + if depth > 128 { |
| 33 | + return unwind::_URC_NO_REASON; |
| 34 | + } |
| 35 | + |
| 36 | + let ip = unsafe { unwind::_Unwind_GetIP(ctx) }; |
| 37 | + let mut info = MaybeUninit::uninit(); |
| 38 | + let r = unsafe { libc::dladdr(ip.cast(), info.as_mut_ptr()) }; |
| 39 | + if r != 0 { |
| 40 | + let info = unsafe { info.assume_init() }; |
| 41 | + let name = unsafe { CStr::from_ptr(info.dli_sname) }; |
| 42 | + if let Ok(name) = name.to_str() { |
| 43 | + rtprintpanic!("{depth}: {}\n", rustc_demangle::demangle(name)); |
| 44 | + return unwind::_URC_NO_REASON; |
| 45 | + } |
| 46 | + } |
| 47 | + |
| 48 | + rtprintpanic!("{depth}: {ip:p}\n"); |
| 49 | + unwind::_URC_NO_REASON |
| 50 | + } |
| 51 | + |
| 52 | + let mut count = 0usize; |
| 53 | + unsafe { unwind::_Unwind_Backtrace(frame, ptr::from_mut(&mut count).cast()) }; |
| 54 | + if count > 128 { |
| 55 | + rtprintpanic!( |
| 56 | + "[... omitted {} frame{} ...]\n", |
| 57 | + count - 128, |
| 58 | + if count - 128 > 1 { "s" } else { "" } |
| 59 | + ); |
| 60 | + } |
| 61 | +} |
0 commit comments