// option. This file may not be copied, modified, or distributed
// except according to those terms.
+//! Backtrace support using libunwind/gcc_s/etc APIs.
+//!
+//! This module contains the ability to unwind the stack using libunwind-style
+//! APIs. Note that there's a whole bunch of implementations of the
+//! libunwind-like API, and this is just trying to be compatible with most of
+//! them all at once instead of being picky.
+//!
+//! The libunwind API is powered by `_Unwind_Backtrace` and is in practice very
+//! reliable at generating a backtrace. It's not entirely clear how it does it
+//! (frame pointers? eh_frame info? both?) but it seems to work!
+//!
+//! Most of the complexity of this module is handling the various platform
+//! differences across libunwind implementations. Otherwise this is a pretty
+//! straightforward Rust binding to the libunwind APIs.
+//!
+//! This is the default unwinding API for all non-Windows platforms currently.
+
use types::c_void;
-pub struct Frame {
- ctx: *mut uw::_Unwind_Context,
+pub enum Frame {
+ Raw(*mut uw::_Unwind_Context),
+ Cloned {
+ ip: *mut c_void,
+ symbol_address: *mut c_void,
+ },
}
+// With a raw libunwind pointer it should only ever be access in a readonly
+// threadsafe fashion, so it's `Sync`. When sending to other threads via `Clone`
+// we always switch to a version which doesn't retain interior pointers, so we
+// should be `Send` as well.
+unsafe impl Send for Frame {}
+unsafe impl Sync for Frame {}
+
impl Frame {
pub fn ip(&self) -> *mut c_void {
+ let ctx = match *self {
+ Frame::Raw(ctx) => ctx,
+ Frame::Cloned { ip, .. } => return ip,
+ };
let mut ip_before_insn = 0;
let mut ip = unsafe {
- uw::_Unwind_GetIPInfo(self.ctx, &mut ip_before_insn) as *mut c_void
+ uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut c_void
};
if !ip.is_null() && ip_before_insn == 0 {
// this is a non-signaling frame, so `ip` refers to the address
}
pub fn symbol_address(&self) -> *mut c_void {
+ if let Frame::Cloned { symbol_address, .. } = *self {
+ return symbol_address;
+ }
+
// dladdr() on osx gets whiny when we use FindEnclosingFunction, and
// it appears to work fine without it, so we only use
// FindEnclosingFunction on non-osx platforms. In doing so, we get a
}
}
+impl Clone for Frame {
+ fn clone(&self) -> Frame {
+ Frame::Cloned {
+ ip: self.ip(),
+ symbol_address: self.symbol_address(),
+ }
+ }
+}
+
#[inline(always)]
pub unsafe fn trace(mut cb: &mut FnMut(&super::Frame) -> bool) {
uw::_Unwind_Backtrace(trace_fn, &mut cb as *mut _ as *mut _);
arg: *mut c_void) -> uw::_Unwind_Reason_Code {
let cb = unsafe { &mut *(arg as *mut &mut FnMut(&super::Frame) -> bool) };
let cx = super::Frame {
- inner: Frame { ctx: ctx },
+ inner: Frame::Raw(ctx),
};
let mut bomb = ::Bomb { enabled: true };
// available since GCC 4.2.0, should be fine for our purpose
#[cfg(all(not(all(target_os = "android", target_arch = "arm")),
+ not(all(target_os = "freebsd", target_arch = "arm")),
not(all(target_os = "linux", target_arch = "arm"))))]
pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context,
ip_before_insn: *mut c_int)
-> libc::uintptr_t;
#[cfg(all(not(target_os = "android"),
+ not(all(target_os = "freebsd", target_arch = "arm")),
not(all(target_os = "linux", target_arch = "arm"))))]
pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void)
-> *mut c_void;
// expansion of the macro. This is all copy/pasted directly from the
// header file with the definition of _Unwind_GetIP.
#[cfg(any(all(target_os = "android", target_arch = "arm"),
+ all(target_os = "freebsd", target_arch = "arm"),
all(target_os = "linux", target_arch = "arm")))]
pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
#[repr(C)]
// This function doesn't exist on Android or ARM/Linux, so make it same
// to _Unwind_GetIP
#[cfg(any(all(target_os = "android", target_arch = "arm"),
+ all(target_os = "freebsd", target_arch = "arm"),
all(target_os = "linux", target_arch = "arm")))]
pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context,
ip_before_insn: *mut c_int)
// This function also doesn't exist on Android or ARM/Linux, so make it
// a no-op
#[cfg(any(target_os = "android",
+ all(target_os = "freebsd", target_arch = "arm"),
all(target_os = "linux", target_arch = "arm")))]
pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void)
-> *mut c_void