about summary refs log tree commit diff
path: root/src/libstd/sys/unix/backtrace/tracing
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/sys/unix/backtrace/tracing')
-rw-r--r--src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs57
-rw-r--r--src/libstd/sys/unix/backtrace/tracing/gcc_s.rs227
-rw-r--r--src/libstd/sys/unix/backtrace/tracing/mod.rs18
3 files changed, 302 insertions, 0 deletions
diff --git a/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs b/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs
new file mode 100644
index 00000000000..2865abb254c
--- /dev/null
+++ b/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs
@@ -0,0 +1,57 @@
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/// As always - iOS on arm uses SjLj exceptions and
+/// _Unwind_Backtrace is even not available there. Still,
+/// backtraces could be extracted using a backtrace function,
+/// which thanks god is public
+///
+/// As mentioned in a huge comment block in `super::super`, backtrace
+/// doesn't play well with green threads, so while it is extremely nice and
+/// simple to use it should be used only on iOS devices as the only viable
+/// option.
+
+use io;
+use io::prelude::*;
+use iter::Iterator;
+use libc;
+use mem;
+use result::Result::Ok;
+use sync::StaticMutex;
+
+use super::printer::print;
+
+#[inline(never)]
+pub fn write(w: &mut Write) -> io::Result<()> {
+    use mem;
+
+    extern {
+        fn backtrace(buf: *mut *mut libc::c_void,
+                     sz: libc::c_int) -> libc::c_int;
+    }
+
+    // while it doesn't requires lock for work as everything is
+    // local, it still displays much nicer backtraces when a
+    // couple of threads panic simultaneously
+    static LOCK: StaticMutex = StaticMutex::new();
+    let _g = LOCK.lock();
+
+    try!(writeln!(w, "stack backtrace:"));
+    // 100 lines should be enough
+    const SIZE: usize = 100;
+    let mut buf: [*mut libc::c_void; SIZE] = unsafe { mem::zeroed() };
+    let cnt = unsafe { backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as usize};
+
+    // skipping the first one as it is write itself
+    for i in 1..cnt {
+        try!(print(w, i as isize, buf[i], buf[i]))
+    }
+    Ok(())
+}
diff --git a/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs b/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs
new file mode 100644
index 00000000000..cdaf69c4882
--- /dev/null
+++ b/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs
@@ -0,0 +1,227 @@
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use io;
+use io::prelude::*;
+use libc;
+use mem;
+use sync::StaticMutex;
+
+use super::super::printing::print;
+
+#[inline(never)] // if we know this is a function call, we can skip it when
+                 // tracing
+pub fn write(w: &mut Write) -> io::Result<()> {
+    struct Context<'a> {
+        idx: isize,
+        writer: &'a mut (Write+'a),
+        last_error: Option<io::Error>,
+    }
+
+    // When using libbacktrace, we use some necessary global state, so we
+    // need to prevent more than one thread from entering this block. This
+    // is semi-reasonable in terms of printing anyway, and we know that all
+    // I/O done here is blocking I/O, not green I/O, so we don't have to
+    // worry about this being a native vs green mutex.
+    static LOCK: StaticMutex = StaticMutex::new();
+    let _g = LOCK.lock();
+
+    try!(writeln!(w, "stack backtrace:"));
+
+    let mut cx = Context { writer: w, last_error: None, idx: 0 };
+    return match unsafe {
+        uw::_Unwind_Backtrace(trace_fn,
+                              &mut cx as *mut Context as *mut libc::c_void)
+    } {
+        uw::_URC_NO_REASON => {
+            match cx.last_error {
+                Some(err) => Err(err),
+                None => Ok(())
+            }
+        }
+        _ => Ok(()),
+    };
+
+    extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
+                       arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
+        let cx: &mut Context = unsafe { mem::transmute(arg) };
+        let mut ip_before_insn = 0;
+        let mut ip = unsafe {
+            uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void
+        };
+        if !ip.is_null() && ip_before_insn == 0 {
+            // this is a non-signaling frame, so `ip` refers to the address
+            // after the calling instruction. account for that.
+            ip = (ip as usize - 1) as *mut _;
+        }
+
+        // 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
+        // slightly more accurate stack trace in the process.
+        //
+        // This is often because panic involves the last instruction of a
+        // function being "call std::rt::begin_unwind", with no ret
+        // instructions after it. This means that the return instruction
+        // pointer points *outside* of the calling function, and by
+        // unwinding it we go back to the original function.
+        let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
+            ip
+        } else {
+            unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
+        };
+
+        // Don't print out the first few frames (they're not user frames)
+        cx.idx += 1;
+        if cx.idx <= 0 { return uw::_URC_NO_REASON }
+        // Don't print ginormous backtraces
+        if cx.idx > 100 {
+            match write!(cx.writer, " ... <frames omitted>\n") {
+                Ok(()) => {}
+                Err(e) => { cx.last_error = Some(e); }
+            }
+            return uw::_URC_FAILURE
+        }
+
+        // Once we hit an error, stop trying to print more frames
+        if cx.last_error.is_some() { return uw::_URC_FAILURE }
+
+        match print(cx.writer, cx.idx, ip, symaddr) {
+            Ok(()) => {}
+            Err(e) => { cx.last_error = Some(e); }
+        }
+
+        // keep going
+        return uw::_URC_NO_REASON
+    }
+}
+
+/// Unwind library interface used for backtraces
+///
+/// Note that dead code is allowed as here are just bindings
+/// iOS doesn't use all of them it but adding more
+/// platform-specific configs pollutes the code too much
+#[allow(non_camel_case_types)]
+#[allow(non_snake_case)]
+mod uw {
+    pub use self::_Unwind_Reason_Code::*;
+
+    use libc;
+
+    #[repr(C)]
+    pub enum _Unwind_Reason_Code {
+        _URC_NO_REASON = 0,
+        _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
+        _URC_FATAL_PHASE2_ERROR = 2,
+        _URC_FATAL_PHASE1_ERROR = 3,
+        _URC_NORMAL_STOP = 4,
+        _URC_END_OF_STACK = 5,
+        _URC_HANDLER_FOUND = 6,
+        _URC_INSTALL_CONTEXT = 7,
+        _URC_CONTINUE_UNWIND = 8,
+        _URC_FAILURE = 9, // used only by ARM EABI
+    }
+
+    pub enum _Unwind_Context {}
+
+    pub type _Unwind_Trace_Fn =
+            extern fn(ctx: *mut _Unwind_Context,
+                      arg: *mut libc::c_void) -> _Unwind_Reason_Code;
+
+    extern {
+        // No native _Unwind_Backtrace on iOS
+        #[cfg(not(all(target_os = "ios", target_arch = "arm")))]
+        pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
+                                 trace_argument: *mut libc::c_void)
+                    -> _Unwind_Reason_Code;
+
+        // 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 = "linux", target_arch = "arm"))))]
+        pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context,
+                                 ip_before_insn: *mut libc::c_int)
+                    -> libc::uintptr_t;
+
+        #[cfg(all(not(target_os = "android"),
+                  not(all(target_os = "linux", target_arch = "arm"))))]
+        pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void)
+            -> *mut libc::c_void;
+    }
+
+    // On android, the function _Unwind_GetIP is a macro, and this is the
+    // 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 = "linux", target_arch = "arm")))]
+    pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
+        #[repr(C)]
+        enum _Unwind_VRS_Result {
+            _UVRSR_OK = 0,
+            _UVRSR_NOT_IMPLEMENTED = 1,
+            _UVRSR_FAILED = 2,
+        }
+        #[repr(C)]
+        enum _Unwind_VRS_RegClass {
+            _UVRSC_CORE = 0,
+            _UVRSC_VFP = 1,
+            _UVRSC_FPA = 2,
+            _UVRSC_WMMXD = 3,
+            _UVRSC_WMMXC = 4,
+        }
+        #[repr(C)]
+        enum _Unwind_VRS_DataRepresentation {
+            _UVRSD_UINT32 = 0,
+            _UVRSD_VFPX = 1,
+            _UVRSD_FPAX = 2,
+            _UVRSD_UINT64 = 3,
+            _UVRSD_FLOAT = 4,
+            _UVRSD_DOUBLE = 5,
+        }
+
+        type _Unwind_Word = libc::c_uint;
+        extern {
+            fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context,
+                               klass: _Unwind_VRS_RegClass,
+                               word: _Unwind_Word,
+                               repr: _Unwind_VRS_DataRepresentation,
+                               data: *mut libc::c_void)
+                -> _Unwind_VRS_Result;
+        }
+
+        let mut val: _Unwind_Word = 0;
+        let ptr = &mut val as *mut _Unwind_Word;
+        let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15,
+                                _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
+                                ptr as *mut libc::c_void);
+        (val & !1) as libc::uintptr_t
+    }
+
+    // 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 = "linux", target_arch = "arm")))]
+    pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context,
+                                    ip_before_insn: *mut libc::c_int)
+        -> libc::uintptr_t
+    {
+        *ip_before_insn = 0;
+        _Unwind_GetIP(ctx)
+    }
+
+    // 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 = "linux", target_arch = "arm")))]
+    pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void)
+        -> *mut libc::c_void
+    {
+        pc
+    }
+}
diff --git a/src/libstd/sys/unix/backtrace/tracing/mod.rs b/src/libstd/sys/unix/backtrace/tracing/mod.rs
new file mode 100644
index 00000000000..c9c8e260d2f
--- /dev/null
+++ b/src/libstd/sys/unix/backtrace/tracing/mod.rs
@@ -0,0 +1,18 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub use self::imp::*;
+
+#[cfg(not(all(target_os = "ios", target_arch = "arm")))]
+#[path = "gcc_s.rs"]
+mod imp;
+#[cfg(all(target_os = "ios", target_arch = "arm"))]
+#[path = "backtrace_fn.rs"]
+mod imp;