about summary refs log tree commit diff
path: root/src/libstd/sys/unix/backtrace
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/sys/unix/backtrace')
-rw-r--r--src/libstd/sys/unix/backtrace/mod.rs5
-rw-r--r--src/libstd/sys/unix/backtrace/printing/dladdr.rs62
-rw-r--r--src/libstd/sys/unix/backtrace/printing/gnu.rs11
-rw-r--r--src/libstd/sys/unix/backtrace/printing/mod.rs7
-rw-r--r--src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs51
-rw-r--r--src/libstd/sys/unix/backtrace/tracing/gcc_s.rs159
6 files changed, 144 insertions, 151 deletions
diff --git a/src/libstd/sys/unix/backtrace/mod.rs b/src/libstd/sys/unix/backtrace/mod.rs
index 1eef89bf66f..29d4012dcdf 100644
--- a/src/libstd/sys/unix/backtrace/mod.rs
+++ b/src/libstd/sys/unix/backtrace/mod.rs
@@ -83,7 +83,8 @@
 /// to symbols. This is a bit of a hokey implementation as-is, but it works for
 /// all unix platforms we support right now, so it at least gets the job done.
 
-pub use self::tracing::write;
+pub use self::tracing::unwind_backtrace;
+pub use self::printing::{foreach_symbol_fileline, resolve_symname};
 
 // tracing impls:
 mod tracing;
@@ -100,3 +101,5 @@ pub mod gnu {
         Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
     }
 }
+
+pub struct BacktraceContext;
diff --git a/src/libstd/sys/unix/backtrace/printing/dladdr.rs b/src/libstd/sys/unix/backtrace/printing/dladdr.rs
index d9b759dc673..05a071a7978 100644
--- a/src/libstd/sys/unix/backtrace/printing/dladdr.rs
+++ b/src/libstd/sys/unix/backtrace/printing/dladdr.rs
@@ -9,33 +9,45 @@
 // except according to those terms.
 
 use io;
-use io::prelude::*;
+use intrinsics;
+use ffi::CStr;
 use libc;
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
 
-pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void,
-             _symaddr: *mut libc::c_void) -> io::Result<()> {
-    use sys_common::backtrace::{output};
-    use intrinsics;
-    use ffi::CStr;
-
-    #[repr(C)]
-    struct Dl_info {
-        dli_fname: *const libc::c_char,
-        dli_fbase: *mut libc::c_void,
-        dli_sname: *const libc::c_char,
-        dli_saddr: *mut libc::c_void,
-    }
-    extern {
-        fn dladdr(addr: *const libc::c_void,
-                  info: *mut Dl_info) -> libc::c_int;
+pub fn resolve_symname<F>(frame: Frame,
+                          callback: F,
+                          _: &BacktraceContext) -> io::Result<()>
+    where F: FnOnce(Option<&str>) -> io::Result<()>
+{
+    unsafe {
+        let mut info: Dl_info = intrinsics::init();
+        let symname = if dladdr(frame.exact_position, &mut info) == 0 {
+            None
+        } else {
+            CStr::from_ptr(info.dli_sname).to_str().ok()
+        };
+        callback(symname)
     }
+}
 
-    let mut info: Dl_info = unsafe { intrinsics::init() };
-    if unsafe { dladdr(addr, &mut info) == 0 } {
-        output(w, idx,addr, None)
-    } else {
-        output(w, idx, addr, Some(unsafe {
-            CStr::from_ptr(info.dli_sname).to_bytes()
-        }))
-    }
+pub fn foreach_symbol_fileline<F>(_symbol_addr: Frame,
+                                  _f: F,
+                                  _: &BacktraceContext) -> io::Result<bool>
+    where F: FnMut(&[u8], libc::c_int) -> io::Result<()>
+{
+    Ok(false)
+}
+
+#[repr(C)]
+struct Dl_info {
+    dli_fname: *const libc::c_char,
+    dli_fbase: *mut libc::c_void,
+    dli_sname: *const libc::c_char,
+    dli_saddr: *mut libc::c_void,
+}
+
+extern {
+    fn dladdr(addr: *const libc::c_void,
+              info: *mut Dl_info) -> libc::c_int;
 }
diff --git a/src/libstd/sys/unix/backtrace/printing/gnu.rs b/src/libstd/sys/unix/backtrace/printing/gnu.rs
deleted file mode 100644
index fb06fbedaf5..00000000000
--- a/src/libstd/sys/unix/backtrace/printing/gnu.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2014 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 sys_common::gnu::libbacktrace::print;
diff --git a/src/libstd/sys/unix/backtrace/printing/mod.rs b/src/libstd/sys/unix/backtrace/printing/mod.rs
index 02e53854727..1ae82e01100 100644
--- a/src/libstd/sys/unix/backtrace/printing/mod.rs
+++ b/src/libstd/sys/unix/backtrace/printing/mod.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub use self::imp::print;
+pub use self::imp::{foreach_symbol_fileline, resolve_symname};
 
 #[cfg(any(target_os = "macos", target_os = "ios",
           target_os = "emscripten"))]
@@ -17,5 +17,6 @@ mod imp;
 
 #[cfg(not(any(target_os = "macos", target_os = "ios",
               target_os = "emscripten")))]
-#[path = "gnu.rs"]
-mod imp;
+mod imp {
+    pub use sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname};
+}
diff --git a/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs b/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs
index ca2e70b5003..fd46b8b9cf0 100644
--- a/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs
+++ b/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs
@@ -18,39 +18,32 @@
 /// simple to use it should be used only on iOS devices as the only viable
 /// option.
 
-use io::prelude::*;
 use io;
 use libc;
 use mem;
-use sys::mutex::Mutex;
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
 
-use super::super::printing::print;
-
-#[inline(never)]
-pub fn write(w: &mut Write) -> io::Result<()> {
-    extern {
-        fn backtrace(buf: *mut *mut libc::c_void,
-                     sz: libc::c_int) -> libc::c_int;
+#[inline(never)] // if we know this is a function call, we can skip it when
+                 // tracing
+pub fn unwind_backtrace(frames: &mut [Frame])
+    -> io::Result<(usize, BacktraceContext)>
+{
+    const FRAME_LEN: usize = 100;
+    assert!(FRAME_LEN >= frames.len());
+    let mut raw_frames = [::std::ptr::null_mut(); FRAME_LEN];
+    let nb_frames = unsafe {
+        backtrace(raw_frames.as_mut_ptr(), raw_frames.len() as libc::c_int)
+    } as usize;
+    for (from, to) in raw_frames.iter().zip(frames.iter_mut()).take(nb_frames) {
+        *to = Frame {
+            exact_position: *from,
+            symbol_addr: *from,
+        };
     }
+    Ok((nb_frames as usize, BacktraceContext))
+}
 
-    // 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: Mutex = Mutex::new();
-    unsafe {
-        LOCK.lock();
-
-        writeln!(w, "stack backtrace:")?;
-        // 100 lines should be enough
-        const SIZE: usize = 100;
-        let mut buf: [*mut libc::c_void; SIZE] = mem::zeroed();
-        let cnt = 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 {
-            print(w, i as isize, buf[i], buf[i])?
-        }
-        LOCK.unlock();
-    }
-    Ok(())
+extern {
+    fn backtrace(buf: *mut *mut libc::c_void, sz: libc::c_int) -> libc::c_int;
 }
diff --git a/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs b/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs
index c1b45620ab0..8691fe55e7c 100644
--- a/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs
+++ b/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs
@@ -8,102 +8,97 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use error::Error;
 use io;
-use io::prelude::*;
 use libc;
-use mem;
-use sys_common::mutex::Mutex;
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
 
-use super::super::printing::print;
 use unwind as uw;
 
-#[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>,
-    }
+struct Context<'a> {
+    idx: usize,
+    frames: &'a mut [Frame],
+}
 
-    // 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: Mutex = Mutex::new();
-    unsafe {
-        LOCK.lock();
+#[derive(Debug)]
+struct UnwindError(uw::_Unwind_Reason_Code);
 
-        writeln!(w, "stack backtrace:")?;
+impl Error for UnwindError {
+    fn description(&self) -> &'static str {
+        "unexpected return value while unwinding"
+    }
+}
 
-        let mut cx = Context { writer: w, last_error: None, idx: 0 };
-        let ret = match {
-            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(()),
-        };
-        LOCK.unlock();
-        return ret
+impl ::fmt::Display for UnwindError {
+    fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
+        write!(f, "{}: {:?}", self.description(), self.0)
     }
+}
 
-    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 _;
+#[inline(never)] // if we know this is a function call, we can skip it when
+                 // tracing
+pub fn unwind_backtrace(frames: &mut [Frame])
+    -> io::Result<(usize, BacktraceContext)>
+{
+    let mut cx = Context {
+        idx: 0,
+        frames: frames,
+    };
+    let result_unwind = unsafe {
+        uw::_Unwind_Backtrace(trace_fn,
+                              &mut cx as *mut Context
+                              as *mut libc::c_void)
+    };
+    // See libunwind:src/unwind/Backtrace.c for the return values.
+    // No, there is no doc.
+    match result_unwind {
+        uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR => {
+            Ok((cx.idx, BacktraceContext))
         }
-
-        // 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
+        _ => {
+            Err(io::Error::new(io::ErrorKind::Other,
+                               UnwindError(result_unwind)))
         }
+    }
+}
 
-        // Once we hit an error, stop trying to print more frames
-        if cx.last_error.is_some() { return uw::_URC_FAILURE }
+extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
+                   arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
+    let cx = unsafe { &mut *(arg as *mut Context) };
+    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 _;
+    }
 
-        match print(cx.writer, cx.idx, ip, symaddr) {
-            Ok(()) => {}
-            Err(e) => { cx.last_error = Some(e); }
-        }
+    // 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) }
+    };
 
-        // keep going
-        uw::_URC_NO_REASON
+    if cx.idx < cx.frames.len() {
+        cx.frames[cx.idx] = Frame {
+            symbol_addr: symaddr,
+            exact_position: ip,
+        };
+        cx.idx += 1;
     }
+
+    uw::_URC_NO_REASON
 }