]> git.lizzy.rs Git - rust.git/commitdiff
Improve backtrace formating while panicking.
authorYamakaky <yamakaky@yamaworld.fr>
Sun, 4 Dec 2016 21:38:27 +0000 (16:38 -0500)
committerYamakaky <yamakaky@yamaworld.fr>
Wed, 15 Feb 2017 19:24:37 +0000 (14:24 -0500)
- `RUST_BACKTRACE=full` prints all the informations (old behaviour)
- `RUST_BACKTRACE=(0|no)` disables the backtrace.
- `RUST_BACKTRACE=<everything else>` (including `1`) shows a simplified
  backtrace, without the function addresses and with cleaned filenames
  and symbols. Also removes some unneded frames at the beginning and the
  end.

Fixes #37783.

PR is #38165.

22 files changed:
src/doc/book/src/functions.md
src/libstd/panicking.rs
src/libstd/sys/redox/backtrace.rs
src/libstd/sys/unix/backtrace/mod.rs
src/libstd/sys/unix/backtrace/printing/dladdr.rs
src/libstd/sys/unix/backtrace/printing/gnu.rs [deleted file]
src/libstd/sys/unix/backtrace/printing/mod.rs
src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs
src/libstd/sys/unix/backtrace/tracing/gcc_s.rs
src/libstd/sys/windows/backtrace.rs [deleted file]
src/libstd/sys/windows/backtrace/backtrace_gnu.rs [new file with mode: 0644]
src/libstd/sys/windows/backtrace/mod.rs [new file with mode: 0644]
src/libstd/sys/windows/backtrace/printing/mod.rs [new file with mode: 0644]
src/libstd/sys/windows/backtrace/printing/msvc.rs [new file with mode: 0644]
src/libstd/sys/windows/backtrace_gnu.rs [deleted file]
src/libstd/sys/windows/printing/gnu.rs [deleted file]
src/libstd/sys/windows/printing/msvc.rs [deleted file]
src/libstd/sys_common/backtrace.rs
src/libstd/sys_common/gnu/libbacktrace.rs
src/libunwind/libunwind.rs
src/test/run-pass/backtrace-debuginfo.rs
src/test/run-pass/backtrace.rs

index eff77a54d8395c309f265a491750071e57afaff0..96c8e9f5d683423aca4aafef93b7c9aec840ff19 100644 (file)
@@ -230,6 +230,19 @@ If you want more information, you can get a backtrace by setting the
 ```text
 $ RUST_BACKTRACE=1 ./diverges
 thread 'main' panicked at 'This function never returns!', hello.rs:2
+Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
+stack backtrace:
+  hello::diverges
+        at ./hello.rs:2
+  hello::main
+        at ./hello.rs:6
+```
+
+If you want the complete backtrace and filenames:
+
+```text
+$ RUST_BACKTRACE=full ./diverges
+thread 'main' panicked at 'This function never returns!', hello.rs:2
 stack backtrace:
    1:     0x7f402773a829 - sys::backtrace::write::h0942de78b6c02817K8r
    2:     0x7f402773d7fc - panicking::on_panic::h3f23f9d0b5f4c91bu9w
@@ -262,7 +275,7 @@ note: Run with `RUST_BACKTRACE=1` for a backtrace.
 `RUST_BACKTRACE` also works with Cargo’s `run` command:
 
 ```text
-$ RUST_BACKTRACE=1 cargo run
+$ RUST_BACKTRACE=full cargo run
      Running `target/debug/diverges`
 thread 'main' panicked at 'This function never returns!', hello.rs:2
 stack backtrace:
index 3fba49345e63d1e5ee34cf8f63cc483f44492353..3b5a1cffc7a2203dd7b7c8b9a52c720652396166 100644 (file)
@@ -320,7 +320,11 @@ fn default_hook(info: &PanicInfo) {
     let log_backtrace = {
         let panics = update_panic_count(0);
 
-        panics >= 2 || backtrace::log_enabled()
+        if panics >= 2 {
+            Some(backtrace::PrintFormat::Full)
+        } else {
+            backtrace::log_enabled()
+        }
     };
 
     let file = info.location.file;
@@ -347,8 +351,8 @@ fn default_hook(info: &PanicInfo) {
 
             static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
 
-            if log_backtrace {
-                let _ = backtrace::write(err);
+            if let Some(format) = log_backtrace {
+                let _ = backtrace::print(err, format);
             } else if FIRST_PANIC.compare_and_swap(true, false, Ordering::SeqCst) {
                 let _ = writeln!(err, "note: Run with `RUST_BACKTRACE=1` for a backtrace.");
             }
index 6f53841502ad23dc451283e4f16b0bda6b75feee..961148fb6b4a8f11544f494b181f6ae4df33b6cd 100644 (file)
 
 use libc;
 use io;
-use sys_common::backtrace::output;
+use sys_common::backtrace::Frame;
+
+pub use sys_common::gnu::libbacktrace::*;
+pub struct BacktraceContext;
 
 #[inline(never)]
-pub fn write(w: &mut io::Write) -> io::Result<()> {
-    output(w, 0, 0 as *mut libc::c_void, None)
+pub fn unwind_backtrace(frames: &mut [Frame])
+    -> io::Result<(usize, BacktraceContext)>
+{
+    Ok((0, BacktraceContext))
 }
index 1eef89bf66f74af2eac1eedd7dc06e9db508a023..29d4012dcdf9890150e8e9f50d09f117b2ddffeb 100644 (file)
@@ -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 fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> {
         Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
     }
 }
+
+pub struct BacktraceContext;
index d9b759dc673948351b0e7b33b64d158879979b9e..05a071a79783841072bd20c7bf505e5197cf8b1b 100644 (file)
@@ -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 (file)
index fb06fbe..0000000
+++ /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;
index 02e53854727f7dc25a470cab20742c473fea961a..1ae82e01100169e5cd451f317ce4eb944547e4b7 100644 (file)
@@ -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 @@
 
 #[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};
+}
index ca2e70b5003a12bced9cec8c489be47118a5418e..fd46b8b9cf06e4463f342ec280df26d5b1b91be2 100644 (file)
 /// 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;
 }
index c1b45620ab04a8aa4e69836f9813f754bd42b90c..8691fe55e7cebdd56f48604707b334803f9abc93 100644 (file)
 // 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
 }
diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs
deleted file mode 100644 (file)
index 94aaf43..0000000
+++ /dev/null
@@ -1,163 +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.
-
-//! As always, windows has something very different than unix, we mainly want
-//! to avoid having to depend too much on libunwind for windows.
-//!
-//! If you google around, you'll find a fair bit of references to built-in
-//! functions to get backtraces on windows. It turns out that most of these are
-//! in an external library called dbghelp. I was unable to find this library
-//! via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent
-//! of it.
-//!
-//! You'll also find that there's a function called CaptureStackBackTrace
-//! mentioned frequently (which is also easy to use), but sadly I didn't have a
-//! copy of that function in my mingw install (maybe it was broken?). Instead,
-//! this takes the route of using StackWalk64 in order to walk the stack.
-
-#![allow(deprecated)] // dynamic_lib
-
-use io::prelude::*;
-
-use io;
-use libc::c_void;
-use mem;
-use ptr;
-use sys::c;
-use sys::dynamic_lib::DynamicLibrary;
-use sys::mutex::Mutex;
-
-macro_rules! sym {
-    ($lib:expr, $e:expr, $t:ident) => (
-        match $lib.symbol($e) {
-            Ok(f) => $crate::mem::transmute::<usize, $t>(f),
-            Err(..) => return Ok(())
-        }
-    )
-}
-
-#[cfg(target_env = "msvc")]
-#[path = "printing/msvc.rs"]
-mod printing;
-
-#[cfg(target_env = "gnu")]
-#[path = "printing/gnu.rs"]
-mod printing;
-
-#[cfg(target_env = "gnu")]
-#[path = "backtrace_gnu.rs"]
-pub mod gnu;
-
-type SymInitializeFn =
-    unsafe extern "system" fn(c::HANDLE, *mut c_void,
-                              c::BOOL) -> c::BOOL;
-type SymCleanupFn =
-    unsafe extern "system" fn(c::HANDLE) -> c::BOOL;
-
-type StackWalk64Fn =
-    unsafe extern "system" fn(c::DWORD, c::HANDLE, c::HANDLE,
-                              *mut c::STACKFRAME64, *mut c::CONTEXT,
-                              *mut c_void, *mut c_void,
-                              *mut c_void, *mut c_void) -> c::BOOL;
-
-#[cfg(target_arch = "x86")]
-pub fn init_frame(frame: &mut c::STACKFRAME64,
-                  ctx: &c::CONTEXT) -> c::DWORD {
-    frame.AddrPC.Offset = ctx.Eip as u64;
-    frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
-    frame.AddrStack.Offset = ctx.Esp as u64;
-    frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
-    frame.AddrFrame.Offset = ctx.Ebp as u64;
-    frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
-    c::IMAGE_FILE_MACHINE_I386
-}
-
-#[cfg(target_arch = "x86_64")]
-pub fn init_frame(frame: &mut c::STACKFRAME64,
-                  ctx: &c::CONTEXT) -> c::DWORD {
-    frame.AddrPC.Offset = ctx.Rip as u64;
-    frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
-    frame.AddrStack.Offset = ctx.Rsp as u64;
-    frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
-    frame.AddrFrame.Offset = ctx.Rbp as u64;
-    frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
-    c::IMAGE_FILE_MACHINE_AMD64
-}
-
-struct Cleanup {
-    handle: c::HANDLE,
-    SymCleanup: SymCleanupFn,
-}
-
-impl Drop for Cleanup {
-    fn drop(&mut self) {
-        unsafe { (self.SymCleanup)(self.handle); }
-    }
-}
-
-pub fn write(w: &mut Write) -> io::Result<()> {
-    // According to windows documentation, all dbghelp functions are
-    // single-threaded.
-    static LOCK: Mutex = Mutex::new();
-    unsafe {
-        LOCK.lock();
-        let res = _write(w);
-        LOCK.unlock();
-        return res
-    }
-}
-
-unsafe fn _write(w: &mut Write) -> io::Result<()> {
-    let dbghelp = match DynamicLibrary::open("dbghelp.dll") {
-        Ok(lib) => lib,
-        Err(..) => return Ok(()),
-    };
-
-    // Fetch the symbols necessary from dbghelp.dll
-    let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn);
-    let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn);
-    let StackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn);
-
-    // Allocate necessary structures for doing the stack walk
-    let process = c::GetCurrentProcess();
-    let thread = c::GetCurrentThread();
-    let mut context: c::CONTEXT = mem::zeroed();
-    c::RtlCaptureContext(&mut context);
-    let mut frame: c::STACKFRAME64 = mem::zeroed();
-    let image = init_frame(&mut frame, &context);
-
-    // Initialize this process's symbols
-    let ret = SymInitialize(process, ptr::null_mut(), c::TRUE);
-    if ret != c::TRUE { return Ok(()) }
-    let _c = Cleanup { handle: process, SymCleanup: SymCleanup };
-
-    // And now that we're done with all the setup, do the stack walking!
-    // Start from -1 to avoid printing this stack frame, which will
-    // always be exactly the same.
-    let mut i = -1;
-    write!(w, "stack backtrace:\n")?;
-    while StackWalk64(image, process, thread, &mut frame, &mut context,
-                      ptr::null_mut(),
-                      ptr::null_mut(),
-                      ptr::null_mut(),
-                      ptr::null_mut()) == c::TRUE {
-        let addr = frame.AddrPC.Offset;
-        if addr == frame.AddrReturn.Offset || addr == 0 ||
-           frame.AddrReturn.Offset == 0 { break }
-
-        i += 1;
-
-        if i >= 0 {
-            printing::print(w, i, addr - 1, process, &dbghelp)?;
-        }
-    }
-
-    Ok(())
-}
diff --git a/src/libstd/sys/windows/backtrace/backtrace_gnu.rs b/src/libstd/sys/windows/backtrace/backtrace_gnu.rs
new file mode 100644 (file)
index 0000000..f0d29dd
--- /dev/null
@@ -0,0 +1,62 @@
+// 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.
+
+use io;
+use sys::c;
+use libc::c_char;
+use path::PathBuf;
+use fs::{OpenOptions, File};
+use sys::ext::fs::OpenOptionsExt;
+use sys::handle::Handle;
+use super::super::{fill_utf16_buf, os2path, to_u16s, wide_char_to_multi_byte};
+
+fn query_full_process_image_name() -> io::Result<PathBuf> {
+    unsafe {
+        let process_handle = Handle::new(c::OpenProcess(c::PROCESS_QUERY_INFORMATION,
+                                                        c::FALSE,
+                                                        c::GetCurrentProcessId()));
+        fill_utf16_buf(|buf, mut sz| {
+            if c::QueryFullProcessImageNameW(process_handle.raw(), 0, buf, &mut sz) == 0 {
+                0
+            } else {
+                sz
+            }
+        }, os2path)
+    }
+}
+
+fn lock_and_get_executable_filename() -> io::Result<(PathBuf, File)> {
+    // We query the current image name, open the file without FILE_SHARE_DELETE so it
+    // can't be moved and then get the current image name again. If the names are the
+    // same than we have successfully locked the file
+    let image_name1 = query_full_process_image_name()?;
+    let file = OpenOptions::new()
+                .read(true)
+                .share_mode(c::FILE_SHARE_READ | c::FILE_SHARE_WRITE)
+                .open(&image_name1)?;
+    let image_name2 = query_full_process_image_name()?;
+
+    if image_name1 != image_name2 {
+        return Err(io::Error::new(io::ErrorKind::Other,
+                                  "executable moved while trying to lock it"));
+    }
+
+    Ok((image_name1, file))
+}
+
+// Get the executable filename for libbacktrace
+// This returns the path in the ANSI code page and a File which should remain open
+// for as long as the path should remain valid
+pub fn get_executable_filename() -> io::Result<(Vec<c_char>, File)> {
+    let (executable, file) = lock_and_get_executable_filename()?;
+    let u16_executable = to_u16s(executable.into_os_string())?;
+    Ok((wide_char_to_multi_byte(c::CP_ACP, c::WC_NO_BEST_FIT_CHARS,
+                                &u16_executable, true)?, file))
+}
diff --git a/src/libstd/sys/windows/backtrace/mod.rs b/src/libstd/sys/windows/backtrace/mod.rs
new file mode 100644 (file)
index 0000000..3c3fd8d
--- /dev/null
@@ -0,0 +1,156 @@
+// 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.
+
+//! As always, windows has something very different than unix, we mainly want
+//! to avoid having to depend too much on libunwind for windows.
+//!
+//! If you google around, you'll find a fair bit of references to built-in
+//! functions to get backtraces on windows. It turns out that most of these are
+//! in an external library called dbghelp. I was unable to find this library
+//! via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent
+//! of it.
+//!
+//! You'll also find that there's a function called CaptureStackBackTrace
+//! mentioned frequently (which is also easy to use), but sadly I didn't have a
+//! copy of that function in my mingw install (maybe it was broken?). Instead,
+//! this takes the route of using StackWalk64 in order to walk the stack.
+
+#![allow(deprecated)] // dynamic_lib
+
+use io;
+use libc::c_void;
+use mem;
+use ptr;
+use sys::c;
+use sys::dynamic_lib::DynamicLibrary;
+use sys_common::backtrace::Frame;
+
+macro_rules! sym {
+    ($lib:expr, $e:expr, $t:ident) => (
+        $lib.symbol($e).map(|f| unsafe {
+            $crate::mem::transmute::<usize, $t>(f)
+        })
+    )
+}
+
+mod printing;
+
+#[cfg(target_env = "gnu")]
+#[path = "backtrace_gnu.rs"]
+pub mod gnu;
+
+pub use self::printing::{resolve_symname, foreach_symbol_fileline};
+
+pub fn unwind_backtrace(frames: &mut [Frame])
+    -> io::Result<(usize, BacktraceContext)>
+{
+    let dbghelp = DynamicLibrary::open("dbghelp.dll")?;
+
+    // Fetch the symbols necessary from dbghelp.dll
+    let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn)?;
+    let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn)?;
+    let StackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn)?;
+
+    // Allocate necessary structures for doing the stack walk
+    let process = unsafe { c::GetCurrentProcess() };
+    let thread = unsafe { c::GetCurrentThread() };
+    let mut context: c::CONTEXT = unsafe { mem::zeroed() };
+    unsafe { c::RtlCaptureContext(&mut context) };
+    let mut frame: c::STACKFRAME64 = unsafe { mem::zeroed() };
+    let image = init_frame(&mut frame, &context);
+
+    let backtrace_context = BacktraceContext {
+        handle: process,
+        SymCleanup: SymCleanup,
+        dbghelp: dbghelp,
+    };
+
+    // Initialize this process's symbols
+    let ret = unsafe { SymInitialize(process, ptr::null_mut(), c::TRUE) };
+    if ret != c::TRUE {
+        return Ok((0, backtrace_context))
+    }
+
+    // And now that we're done with all the setup, do the stack walking!
+    // Start from -1 to avoid printing this stack frame, which will
+    // always be exactly the same.
+    let mut i = 0;
+    unsafe {
+        while i < frames.len() &&
+              StackWalk64(image, process, thread, &mut frame, &mut context,
+                          ptr::null_mut(),
+                          ptr::null_mut(),
+                          ptr::null_mut(),
+                          ptr::null_mut()) == c::TRUE
+        {
+            let addr = frame.AddrPC.Offset;
+            if addr == frame.AddrReturn.Offset || addr == 0 ||
+               frame.AddrReturn.Offset == 0 { break }
+
+            frames[i] = Frame {
+                symbol_addr: (addr - 1) as *const c_void,
+                exact_position: (addr - 1) as *const c_void,
+            };
+            i += 1;
+        }
+    }
+
+    Ok((i, backtrace_context))
+}
+
+type SymInitializeFn =
+    unsafe extern "system" fn(c::HANDLE, *mut c_void,
+                              c::BOOL) -> c::BOOL;
+type SymCleanupFn =
+    unsafe extern "system" fn(c::HANDLE) -> c::BOOL;
+
+type StackWalk64Fn =
+    unsafe extern "system" fn(c::DWORD, c::HANDLE, c::HANDLE,
+                              *mut c::STACKFRAME64, *mut c::CONTEXT,
+                              *mut c_void, *mut c_void,
+                              *mut c_void, *mut c_void) -> c::BOOL;
+
+#[cfg(target_arch = "x86")]
+fn init_frame(frame: &mut c::STACKFRAME64,
+              ctx: &c::CONTEXT) -> c::DWORD {
+    frame.AddrPC.Offset = ctx.Eip as u64;
+    frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
+    frame.AddrStack.Offset = ctx.Esp as u64;
+    frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
+    frame.AddrFrame.Offset = ctx.Ebp as u64;
+    frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
+    c::IMAGE_FILE_MACHINE_I386
+}
+
+#[cfg(target_arch = "x86_64")]
+fn init_frame(frame: &mut c::STACKFRAME64,
+              ctx: &c::CONTEXT) -> c::DWORD {
+    frame.AddrPC.Offset = ctx.Rip as u64;
+    frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
+    frame.AddrStack.Offset = ctx.Rsp as u64;
+    frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
+    frame.AddrFrame.Offset = ctx.Rbp as u64;
+    frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
+    c::IMAGE_FILE_MACHINE_AMD64
+}
+
+pub struct BacktraceContext {
+    handle: c::HANDLE,
+    SymCleanup: SymCleanupFn,
+    // Only used in printing for msvc and not gnu
+    #[allow(dead_code)]
+    dbghelp: DynamicLibrary,
+}
+
+impl Drop for BacktraceContext {
+    fn drop(&mut self) {
+        unsafe { (self.SymCleanup)(self.handle); }
+    }
+}
diff --git a/src/libstd/sys/windows/backtrace/printing/mod.rs b/src/libstd/sys/windows/backtrace/printing/mod.rs
new file mode 100644 (file)
index 0000000..3e566f6
--- /dev/null
@@ -0,0 +1,20 @@
+// 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.
+
+#[cfg(target_env = "msvc")]
+#[path = "msvc.rs"]
+mod printing;
+
+#[cfg(target_env = "gnu")]
+mod printing {
+    pub use sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname};
+}
+
+pub use self::printing::{foreach_symbol_fileline, resolve_symname};
diff --git a/src/libstd/sys/windows/backtrace/printing/msvc.rs b/src/libstd/sys/windows/backtrace/printing/msvc.rs
new file mode 100644 (file)
index 0000000..3107d78
--- /dev/null
@@ -0,0 +1,83 @@
+// 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.
+
+use ffi::CStr;
+use io;
+use libc::{c_ulong, c_int, c_char};
+use mem;
+use sys::c;
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
+
+type SymFromAddrFn =
+    unsafe extern "system" fn(c::HANDLE, u64, *mut u64,
+                              *mut c::SYMBOL_INFO) -> c::BOOL;
+type SymGetLineFromAddr64Fn =
+    unsafe extern "system" fn(c::HANDLE, u64, *mut u32,
+                              *mut c::IMAGEHLP_LINE64) -> c::BOOL;
+
+/// Converts a pointer to symbol to its string value.
+pub fn resolve_symname<F>(frame: Frame,
+                          callback: F,
+                          context: &BacktraceContext) -> io::Result<()>
+    where F: FnOnce(Option<&str>) -> io::Result<()>
+{
+    let SymFromAddr = sym!(&context.dbghelp, "SymFromAddr", SymFromAddrFn)?;
+
+    unsafe {
+        let mut info: c::SYMBOL_INFO = mem::zeroed();
+        info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
+        // the struct size in C.  the value is different to
+        // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
+        // due to struct alignment.
+        info.SizeOfStruct = 88;
+
+        let mut displacement = 0u64;
+        let ret = SymFromAddr(context.handle,
+                              frame.symbol_addr as u64,
+                              &mut displacement,
+                              &mut info);
+
+        let symname = if ret == c::TRUE {
+            let ptr = info.Name.as_ptr() as *const c_char;
+            CStr::from_ptr(ptr).to_str().ok()
+        } else {
+            None
+        };
+        callback(symname)
+    }
+}
+
+pub fn foreach_symbol_fileline<F>(frame: Frame,
+                                  mut f: F,
+                                  context: &BacktraceContext)
+    -> io::Result<bool>
+    where F: FnMut(&[u8], c_int) -> io::Result<()>
+{
+    let SymGetLineFromAddr64 = sym!(&context.dbghelp,
+                                    "SymGetLineFromAddr64",
+                                    SymGetLineFromAddr64Fn)?;
+
+    unsafe {
+        let mut line: c::IMAGEHLP_LINE64 = mem::zeroed();
+        line.SizeOfStruct = ::mem::size_of::<c::IMAGEHLP_LINE64>() as u32;
+
+        let mut displacement = 0u32;
+        let ret = SymGetLineFromAddr64(context.handle,
+                                       frame.exact_position as u64,
+                                       &mut displacement,
+                                       &mut line);
+        if ret == c::TRUE {
+            let name = CStr::from_ptr(line.Filename).to_bytes();
+            f(name, line.LineNumber as c_int)?;
+        }
+        Ok(false)
+    }
+}
diff --git a/src/libstd/sys/windows/backtrace_gnu.rs b/src/libstd/sys/windows/backtrace_gnu.rs
deleted file mode 100644 (file)
index f0d29dd..0000000
+++ /dev/null
@@ -1,62 +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.
-
-use io;
-use sys::c;
-use libc::c_char;
-use path::PathBuf;
-use fs::{OpenOptions, File};
-use sys::ext::fs::OpenOptionsExt;
-use sys::handle::Handle;
-use super::super::{fill_utf16_buf, os2path, to_u16s, wide_char_to_multi_byte};
-
-fn query_full_process_image_name() -> io::Result<PathBuf> {
-    unsafe {
-        let process_handle = Handle::new(c::OpenProcess(c::PROCESS_QUERY_INFORMATION,
-                                                        c::FALSE,
-                                                        c::GetCurrentProcessId()));
-        fill_utf16_buf(|buf, mut sz| {
-            if c::QueryFullProcessImageNameW(process_handle.raw(), 0, buf, &mut sz) == 0 {
-                0
-            } else {
-                sz
-            }
-        }, os2path)
-    }
-}
-
-fn lock_and_get_executable_filename() -> io::Result<(PathBuf, File)> {
-    // We query the current image name, open the file without FILE_SHARE_DELETE so it
-    // can't be moved and then get the current image name again. If the names are the
-    // same than we have successfully locked the file
-    let image_name1 = query_full_process_image_name()?;
-    let file = OpenOptions::new()
-                .read(true)
-                .share_mode(c::FILE_SHARE_READ | c::FILE_SHARE_WRITE)
-                .open(&image_name1)?;
-    let image_name2 = query_full_process_image_name()?;
-
-    if image_name1 != image_name2 {
-        return Err(io::Error::new(io::ErrorKind::Other,
-                                  "executable moved while trying to lock it"));
-    }
-
-    Ok((image_name1, file))
-}
-
-// Get the executable filename for libbacktrace
-// This returns the path in the ANSI code page and a File which should remain open
-// for as long as the path should remain valid
-pub fn get_executable_filename() -> io::Result<(Vec<c_char>, File)> {
-    let (executable, file) = lock_and_get_executable_filename()?;
-    let u16_executable = to_u16s(executable.into_os_string())?;
-    Ok((wide_char_to_multi_byte(c::CP_ACP, c::WC_NO_BEST_FIT_CHARS,
-                                &u16_executable, true)?, file))
-}
diff --git a/src/libstd/sys/windows/printing/gnu.rs b/src/libstd/sys/windows/printing/gnu.rs
deleted file mode 100644 (file)
index be2d527..0000000
+++ /dev/null
@@ -1,26 +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.
-
-use io::prelude::*;
-use io;
-use libc::c_void;
-use sys::c;
-use sys::dynamic_lib::DynamicLibrary;
-use sys_common::gnu::libbacktrace;
-
-pub fn print(w: &mut Write,
-             i: isize,
-             addr: u64,
-             _process: c::HANDLE,
-             _dbghelp: &DynamicLibrary)
-              -> io::Result<()> {
-    let addr = addr as usize as *mut c_void;
-    libbacktrace::print(w, i, addr, addr)
-}
diff --git a/src/libstd/sys/windows/printing/msvc.rs b/src/libstd/sys/windows/printing/msvc.rs
deleted file mode 100644 (file)
index 9c29ac4..0000000
+++ /dev/null
@@ -1,73 +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.
-
-use ffi::CStr;
-use io::prelude::*;
-use io;
-use libc::{c_ulong, c_int, c_char, c_void};
-use mem;
-use sys::c;
-use sys::dynamic_lib::DynamicLibrary;
-use sys_common::backtrace::{output, output_fileline};
-
-type SymFromAddrFn =
-    unsafe extern "system" fn(c::HANDLE, u64, *mut u64,
-                              *mut c::SYMBOL_INFO) -> c::BOOL;
-type SymGetLineFromAddr64Fn =
-    unsafe extern "system" fn(c::HANDLE, u64, *mut u32,
-                              *mut c::IMAGEHLP_LINE64) -> c::BOOL;
-
-pub fn print(w: &mut Write,
-             i: isize,
-             addr: u64,
-             process: c::HANDLE,
-             dbghelp: &DynamicLibrary)
-              -> io::Result<()> {
-    unsafe {
-        let SymFromAddr = sym!(dbghelp, "SymFromAddr", SymFromAddrFn);
-        let SymGetLineFromAddr64 = sym!(dbghelp,
-                                        "SymGetLineFromAddr64",
-                                        SymGetLineFromAddr64Fn);
-
-        let mut info: c::SYMBOL_INFO = mem::zeroed();
-        info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
-        // the struct size in C.  the value is different to
-        // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
-        // due to struct alignment.
-        info.SizeOfStruct = 88;
-
-        let mut displacement = 0u64;
-        let ret = SymFromAddr(process, addr, &mut displacement, &mut info);
-
-        let name = if ret == c::TRUE {
-            let ptr = info.Name.as_ptr() as *const c_char;
-            Some(CStr::from_ptr(ptr).to_bytes())
-        } else {
-            None
-        };
-
-        output(w, i, addr as usize as *mut c_void, name)?;
-
-        // Now find out the filename and line number
-        let mut line: c::IMAGEHLP_LINE64 = mem::zeroed();
-        line.SizeOfStruct = ::mem::size_of::<c::IMAGEHLP_LINE64>() as u32;
-
-        let mut displacement = 0u32;
-        let ret = SymGetLineFromAddr64(process, addr, &mut displacement, &mut line);
-        if ret == c::TRUE {
-            output_fileline(w,
-                            CStr::from_ptr(line.Filename).to_bytes(),
-                            line.LineNumber as c_int,
-                            false)
-        } else {
-            Ok(())
-        }
-    }
-}
index a8540fed9286f1aaae8f9843c25ec5f293d301e7..a19f7954e8f9dca643ff831e147c8ff026ed4add 100644 (file)
 
 #![cfg_attr(target_os = "nacl", allow(dead_code))]
 
+/// Common code for printing the backtrace in the same way across the different
+/// supported platforms.
+
 use env;
 use io::prelude::*;
 use io;
 use libc;
 use str;
 use sync::atomic::{self, Ordering};
+use path::Path;
+use sys::mutex::Mutex;
+use ptr;
 
-pub use sys::backtrace::write;
+pub use sys::backtrace::{
+    unwind_backtrace,
+    resolve_symname,
+    foreach_symbol_fileline,
+    BacktraceContext
+};
 
 #[cfg(target_pointer_width = "64")]
 pub const HEX_WIDTH: usize = 18;
 #[cfg(target_pointer_width = "32")]
 pub const HEX_WIDTH: usize = 10;
 
+/// Represents an item in the backtrace list. See `unwind_backtrace` for how
+/// it is created.
+#[derive(Debug, Copy, Clone)]
+pub struct Frame {
+    /// Exact address of the call that failed.
+    pub exact_position: *const libc::c_void,
+    /// Address of the enclosing function.
+    pub symbol_addr: *const libc::c_void,
+}
+
+/// Max number of frames to print.
+const MAX_NB_FRAMES: usize = 100;
+
+/// Prints the current backtrace.
+pub fn print(w: &mut Write, format: PrintFormat) -> io::Result<()> {
+    static LOCK: Mutex = Mutex::new();
+
+    // Use a lock to prevent mixed output in multithreading context.
+    // Some platforms also requires it, like `SymFromAddr` on Windows.
+    unsafe {
+        LOCK.lock();
+        let res = _print(w, format);
+        LOCK.unlock();
+        res
+    }
+}
+
+fn _print(w: &mut Write, format: PrintFormat) -> io::Result<()> {
+    let mut frames = [Frame {
+        exact_position: ptr::null(),
+        symbol_addr: ptr::null(),
+    }; MAX_NB_FRAMES];
+    let (nb_frames, context) = unwind_backtrace(&mut frames)?;
+    let (skipped_before, skipped_after) =
+        filter_frames(&frames[..nb_frames], format, &context);
+    if format == PrintFormat::Short {
+        writeln!(w, "note: Some details are omitted, \
+                     run with `RUST_BACKTRACE=full` for a verbose backtrace.")?;
+    }
+    writeln!(w, "stack backtrace:")?;
+
+    let filtered_frames = &frames[..nb_frames - skipped_after];
+    for (index, frame) in filtered_frames.iter().skip(skipped_before).enumerate() {
+        resolve_symname(*frame, |symname| {
+            output(w, index, *frame, symname, format)
+        }, &context)?;
+        let has_more_filenames = foreach_symbol_fileline(*frame, |file, line| {
+            output_fileline(w, file, line, format)
+        }, &context)?;
+        if has_more_filenames {
+            w.write_all(b" <... and possibly more>")?;
+        }
+    }
+
+    Ok(())
+}
+
+fn filter_frames(frames: &[Frame],
+                 format: PrintFormat,
+                 context: &BacktraceContext) -> (usize, usize)
+{
+    if format == PrintFormat::Full {
+        return (0, 0);
+    }
+
+    let mut skipped_before = 0;
+    for (i, frame) in frames.iter().enumerate() {
+        skipped_before = i;
+        let mut skip = false;
+
+        let _ = resolve_symname(*frame, |symname| {
+            if let Some(mangled_symbol_name) = symname {
+                let magics_begin = [
+                    "_ZN3std3sys3imp9backtrace",
+                    "_ZN3std10sys_common9backtrace",
+                    "_ZN3std9panicking",
+                    "_ZN4core9panicking",
+                    "rust_begin_unwind",
+                    "_ZN4core6result13unwrap_failed",
+                ];
+                if !magics_begin.iter().any(|s| mangled_symbol_name.starts_with(s)) {
+                    skip = true;
+                }
+            }
+            Ok(())
+        }, context);
+
+        if skip {
+            break;
+        }
+    }
+
+    let mut skipped_after = 0;
+    for (i, frame) in frames.iter().rev().enumerate() {
+        let _ = resolve_symname(*frame, |symname| {
+            if let Some(mangled_symbol_name) = symname {
+                let magics_end = [
+                    "_ZN3std9panicking3try7do_call",
+                    "__rust_maybe_catch_panic",
+                    "__libc_start_main",
+                    "__rust_try",
+                    "_start",
+                ];
+                if magics_end.iter().any(|s| mangled_symbol_name.starts_with(s)) {
+                    skipped_after = i + 1;
+                }
+            }
+            Ok(())
+        }, context);
+    }
+
+    (skipped_before, skipped_after)
+}
+
+/// Controls how the backtrace should be formated.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum PrintFormat {
+    /// Show all the frames with absolute path for files.
+    Full = 2,
+    /// Show only relevant data from the backtrace.
+    Short = 3,
+}
+
 // For now logging is turned off by default, and this function checks to see
 // whether the magical environment variable is present to see if it's turned on.
-pub fn log_enabled() -> bool {
+pub fn log_enabled() -> Option<PrintFormat> {
     static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
     match ENABLED.load(Ordering::SeqCst) {
-        1 => return false,
-        2 => return true,
-        _ => {}
+        0 => {},
+        1 => return None,
+        2 => return Some(PrintFormat::Full),
+        3 => return Some(PrintFormat::Short),
+        _ => unreachable!(),
     }
 
     let val = match env::var_os("RUST_BACKTRACE") {
-        Some(x) => if &x == "0" { 1 } else { 2 },
-        None => 1,
+        Some(x) => if &x == "0" {
+            None
+        } else if &x == "full" {
+            Some(PrintFormat::Full)
+        } else {
+            Some(PrintFormat::Short)
+        },
+        None => None,
     };
-    ENABLED.store(val, Ordering::SeqCst);
-    val == 2
+    ENABLED.store(match val {
+        Some(v) => v as isize,
+        None => 1,
+    }, Ordering::SeqCst);
+    val
 }
 
-// These output functions should now be used everywhere to ensure consistency.
-pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void,
-              s: Option<&[u8]>) -> io::Result<()> {
-    write!(w, "  {:2}: {:2$?} - ", idx, addr, HEX_WIDTH)?;
-    match s.and_then(|s| str::from_utf8(s).ok()) {
-        Some(string) => demangle(w, string)?,
-        None => write!(w, "<unknown>")?,
+/// Print the symbol of the backtrace frame.
+///
+/// These output functions should now be used everywhere to ensure consistency.
+/// You may want to also use `output_fileline`.
+fn output(w: &mut Write, idx: usize, frame: Frame,
+              s: Option<&str>, format: PrintFormat) -> io::Result<()> {
+    // Remove the `17: 0x0 - <unknown>` line.
+    if format == PrintFormat::Short && frame.exact_position == ptr::null() {
+        return Ok(());
+    }
+    match format {
+        PrintFormat::Full => write!(w,
+                                    "  {:2}: {:2$?} - ",
+                                    idx,
+                                    frame.exact_position,
+                                    HEX_WIDTH)?,
+        PrintFormat::Short => write!(w, "  {:2}: ", idx)?,
     }
-    w.write_all(&['\n' as u8])
+    match s {
+        Some(string) => demangle(w, string, format)?,
+        None => w.write_all(b"<unknown>")?,
+    }
+    w.write_all(b"\n")
 }
 
+/// Print the filename and line number of the backtrace frame.
+///
+/// See also `output`.
 #[allow(dead_code)]
-pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int,
-                       more: bool) -> io::Result<()> {
-    let file = str::from_utf8(file).unwrap_or("<unknown>");
+fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int,
+                       format: PrintFormat) -> io::Result<()> {
     // prior line: "  ##: {:2$} - func"
-    write!(w, "      {:3$}at {}:{}", "", file, line, HEX_WIDTH)?;
-    if more {
-        write!(w, " <... and possibly more>")?;
+    w.write_all(b"")?;
+    match format {
+        PrintFormat::Full => write!(w,
+                                    "           {:1$}",
+                                    "",
+                                    HEX_WIDTH)?,
+        PrintFormat::Short => write!(w, "           ")?,
     }
-    w.write_all(&['\n' as u8])
+
+    let file = str::from_utf8(file).unwrap_or("<unknown>");
+    let file_path = Path::new(file);
+    let mut already_printed = false;
+    if format == PrintFormat::Short && file_path.is_absolute() {
+        if let Ok(cwd) = env::current_dir() {
+            if let Ok(stripped) = file_path.strip_prefix(&cwd) {
+                if let Some(s) = stripped.to_str() {
+                    write!(w, "  at ./{}:{}", s, line)?;
+                    already_printed = true;
+                }
+            }
+        }
+    }
+    if !already_printed {
+        write!(w, "  at {}:{}", file, line)?;
+    }
+
+    w.write_all(b"\n")
 }
 
 
@@ -84,7 +267,7 @@ pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int,
 // Note that this demangler isn't quite as fancy as it could be. We have lots
 // of other information in our symbols like hashes, version, type information,
 // etc. Additionally, this doesn't handle glue symbols at all.
-pub fn demangle(writer: &mut Write, s: &str) -> io::Result<()> {
+pub fn demangle(writer: &mut Write, s: &str, format: PrintFormat) -> io::Result<()> {
     // First validate the symbol. If it doesn't look like anything we're
     // expecting, we just print it literally. Note that we must handle non-rust
     // symbols because we could have any function in the backtrace.
@@ -123,6 +306,22 @@ pub fn demangle(writer: &mut Write, s: &str) -> io::Result<()> {
     if !valid {
         writer.write_all(s.as_bytes())?;
     } else {
+        // remove the `::hfc2edb670e5eda97` part at the end of the symbol.
+        if format == PrintFormat::Short {
+            // The symbol in still mangled.
+            let mut split = inner.rsplitn(2, "17h");
+            match (split.next(), split.next()) {
+                (Some(addr), rest) => {
+                    if addr.len() == 16 &&
+                       addr.chars().all(|c| c.is_digit(16))
+                    {
+                        inner = rest.unwrap_or("");
+                    }
+                }
+                _ => (),
+            }
+        }
+
         let mut first = true;
         while !inner.is_empty() {
             if !first {
@@ -208,7 +407,9 @@ mod tests {
     use sys_common;
     macro_rules! t { ($a:expr, $b:expr) => ({
         let mut m = Vec::new();
-        sys_common::backtrace::demangle(&mut m, $a).unwrap();
+        sys_common::backtrace::demangle(&mut m,
+                                        $a,
+                                        super::PrintFormat::Full).unwrap();
         assert_eq!(String::from_utf8(m).unwrap(), $b);
     }) }
 
index 0bdbeddb112121b0a015925bc83fc866e4dd0b31..1ea5cca44c7e40154f383bce344187a8b703a94e 100644 (file)
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use io;
-use io::prelude::*;
 use libc;
-use sys_common::backtrace::{output, output_fileline};
-
-pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void,
-             symaddr: *mut libc::c_void) -> io::Result<()> {
-    use ffi::CStr;
-    use mem;
-    use ptr;
-
-    ////////////////////////////////////////////////////////////////////////
-    // libbacktrace.h API
-    ////////////////////////////////////////////////////////////////////////
-    type backtrace_syminfo_callback =
-        extern "C" fn(data: *mut libc::c_void,
-                      pc: libc::uintptr_t,
-                      symname: *const libc::c_char,
-                      symval: libc::uintptr_t,
-                      symsize: libc::uintptr_t);
-    type backtrace_full_callback =
-        extern "C" fn(data: *mut libc::c_void,
-                      pc: libc::uintptr_t,
-                      filename: *const libc::c_char,
-                      lineno: libc::c_int,
-                      function: *const libc::c_char) -> libc::c_int;
-    type backtrace_error_callback =
-        extern "C" fn(data: *mut libc::c_void,
-                      msg: *const libc::c_char,
-                      errnum: libc::c_int);
-    enum backtrace_state {}
-
-    extern {
-        fn backtrace_create_state(filename: *const libc::c_char,
-                                  threaded: libc::c_int,
-                                  error: backtrace_error_callback,
-                                  data: *mut libc::c_void)
-                                  -> *mut backtrace_state;
-        fn backtrace_syminfo(state: *mut backtrace_state,
-                             addr: libc::uintptr_t,
-                             cb: backtrace_syminfo_callback,
-                             error: backtrace_error_callback,
-                             data: *mut libc::c_void) -> libc::c_int;
-        fn backtrace_pcinfo(state: *mut backtrace_state,
-                            addr: libc::uintptr_t,
-                            cb: backtrace_full_callback,
-                            error: backtrace_error_callback,
-                            data: *mut libc::c_void) -> libc::c_int;
-    }
-
-    ////////////////////////////////////////////////////////////////////////
-    // helper callbacks
-    ////////////////////////////////////////////////////////////////////////
-
-    type FileLine = (*const libc::c_char, libc::c_int);
-
-    extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char,
-                       _errnum: libc::c_int) {
-        // do nothing for now
-    }
-    extern fn syminfo_cb(data: *mut libc::c_void,
-                         _pc: libc::uintptr_t,
-                         symname: *const libc::c_char,
-                         _symval: libc::uintptr_t,
-                         _symsize: libc::uintptr_t) {
-        let slot = data as *mut *const libc::c_char;
-        unsafe { *slot = symname; }
-    }
-    extern fn pcinfo_cb(data: *mut libc::c_void,
-                        _pc: libc::uintptr_t,
-                        filename: *const libc::c_char,
-                        lineno: libc::c_int,
-                        _function: *const libc::c_char) -> libc::c_int {
-        if !filename.is_null() {
-            let slot = data as *mut &mut [FileLine];
-            let buffer = unsafe {ptr::read(slot)};
-
-            // if the buffer is not full, add file:line to the buffer
-            // and adjust the buffer for next possible calls to pcinfo_cb.
-            if !buffer.is_empty() {
-                buffer[0] = (filename, lineno);
-                unsafe { ptr::write(slot, &mut buffer[1..]); }
-            }
-        }
-
-        0
-    }
-
-    // The libbacktrace API supports creating a state, but it does not
-    // support destroying a state. I personally take this to mean that a
-    // state is meant to be created and then live forever.
-    //
-    // I would love to register an at_exit() handler which cleans up this
-    // state, but libbacktrace provides no way to do so.
-    //
-    // With these constraints, this function has a statically cached state
-    // that is calculated the first time this is requested. Remember that
-    // backtracing all happens serially (one global lock).
-    //
-    // Things don't work so well on not-Linux since libbacktrace can't track
-    // down that executable this is. We at one point used env::current_exe but
-    // it turns out that there are some serious security issues with that
-    // approach.
-    //
-    // Specifically, on certain platforms like BSDs, a malicious actor can cause
-    // an arbitrary file to be placed at the path returned by current_exe.
-    // libbacktrace does not behave defensively in the presence of ill-formed
-    // DWARF information, and has been demonstrated to segfault in at least one
-    // case. There is no evidence at the moment to suggest that a more carefully
-    // constructed file can't cause arbitrary code execution. As a result of all
-    // of this, we don't hint libbacktrace with the path to the current process.
-    unsafe fn init_state() -> *mut backtrace_state {
-        static mut STATE: *mut backtrace_state = ptr::null_mut();
-        if !STATE.is_null() { return STATE }
-
-        let filename = match ::sys::backtrace::gnu::get_executable_filename() {
-            Ok((filename, file)) => {
-                // filename is purposely leaked here since libbacktrace requires
-                // it to stay allocated permanently, file is also leaked so that
-                // the file stays locked
-                let filename_ptr = filename.as_ptr();
-                mem::forget(filename);
-                mem::forget(file);
-                filename_ptr
-            },
-            Err(_) => ptr::null(),
-        };
-
-        STATE = backtrace_create_state(filename, 0, error_cb,
-                                       ptr::null_mut());
-        STATE
-    }
-
-    ////////////////////////////////////////////////////////////////////////
-    // translation
-    ////////////////////////////////////////////////////////////////////////
-
-    // backtrace errors are currently swept under the rug, only I/O
-    // errors are reported
-    let state = unsafe { init_state() };
-    if state.is_null() {
-        return output(w, idx, addr, None)
-    }
-    let mut data = ptr::null();
-    let data_addr = &mut data as *mut *const libc::c_char;
-    let ret = unsafe {
-        backtrace_syminfo(state, symaddr as libc::uintptr_t,
-                          syminfo_cb, error_cb,
-                          data_addr as *mut libc::c_void)
-    };
-    if ret == 0 || data.is_null() {
-        output(w, idx, addr, None)?;
-    } else {
-        output(w, idx, addr, Some(unsafe { CStr::from_ptr(data).to_bytes() }))?;
-    }
 
+use ffi::CStr;
+use io;
+use mem;
+use ptr;
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
+
+pub fn foreach_symbol_fileline<F>(frame: Frame,
+                                  mut f: F,
+                                  _: &BacktraceContext) -> io::Result<bool>
+where F: FnMut(&[u8], libc::c_int) -> io::Result<()>
+{
     // pcinfo may return an arbitrary number of file:line pairs,
     // in the order of stack trace (i.e. inlined calls first).
     // in order to avoid allocation, we stack-allocate a fixed size of entries.
     const FILELINE_SIZE: usize = 32;
     let mut fileline_buf = [(ptr::null(), -1); FILELINE_SIZE];
     let ret;
-    let fileline_count;
-    {
+    let fileline_count = {
+        let state = unsafe { init_state() };
         let mut fileline_win: &mut [FileLine] = &mut fileline_buf;
         let fileline_addr = &mut fileline_win as *mut &mut [FileLine];
         ret = unsafe {
-            backtrace_pcinfo(state, addr as libc::uintptr_t,
-                             pcinfo_cb, error_cb,
+            backtrace_pcinfo(state,
+                             frame.exact_position as libc::uintptr_t,
+                             pcinfo_cb,
+                             error_cb,
                              fileline_addr as *mut libc::c_void)
         };
-        fileline_count = FILELINE_SIZE - fileline_win.len();
-    }
+        FILELINE_SIZE - fileline_win.len()
+    };
     if ret == 0 {
-        for (i, &(file, line)) in fileline_buf[..fileline_count].iter().enumerate() {
+        for &(file, line) in &fileline_buf[..fileline_count] {
             if file.is_null() { continue; } // just to be sure
             let file = unsafe { CStr::from_ptr(file).to_bytes() };
-            output_fileline(w, file, line, i == FILELINE_SIZE - 1)?;
+            f(file, line)?;
+        }
+        Ok(fileline_count == FILELINE_SIZE)
+    } else {
+        Ok(false)
+    }
+}
+
+/// Converts a pointer to symbol to its string value.
+pub fn resolve_symname<F>(frame: Frame,
+                          callback: F,
+                          _: &BacktraceContext) -> io::Result<()>
+    where F: FnOnce(Option<&str>) -> io::Result<()>
+{
+    let symname = {
+        let state = unsafe { init_state() };
+        if state.is_null() {
+            None
+        } else {
+            let mut data = ptr::null();
+            let data_addr = &mut data as *mut *const libc::c_char;
+            let ret = unsafe {
+                backtrace_syminfo(state,
+                                  frame.symbol_addr as libc::uintptr_t,
+                                  syminfo_cb,
+                                  error_cb,
+                                  data_addr as *mut libc::c_void)
+            };
+            if ret == 0 || data.is_null() {
+                None
+            } else {
+                unsafe {
+                    CStr::from_ptr(data).to_str().ok()
+                }
+            }
+        }
+    };
+    callback(symname)
+}
+
+////////////////////////////////////////////////////////////////////////
+// libbacktrace.h API
+////////////////////////////////////////////////////////////////////////
+type backtrace_syminfo_callback =
+extern "C" fn(data: *mut libc::c_void,
+              pc: libc::uintptr_t,
+              symname: *const libc::c_char,
+              symval: libc::uintptr_t,
+              symsize: libc::uintptr_t);
+type backtrace_full_callback =
+extern "C" fn(data: *mut libc::c_void,
+              pc: libc::uintptr_t,
+              filename: *const libc::c_char,
+              lineno: libc::c_int,
+              function: *const libc::c_char) -> libc::c_int;
+type backtrace_error_callback =
+extern "C" fn(data: *mut libc::c_void,
+              msg: *const libc::c_char,
+              errnum: libc::c_int);
+enum backtrace_state {}
+#[link(name = "backtrace", kind = "static")]
+#[cfg(all(not(test), not(cargobuild)))]
+extern {}
+
+extern {
+    fn backtrace_create_state(filename: *const libc::c_char,
+                              threaded: libc::c_int,
+                              error: backtrace_error_callback,
+                              data: *mut libc::c_void)
+        -> *mut backtrace_state;
+    fn backtrace_syminfo(state: *mut backtrace_state,
+                         addr: libc::uintptr_t,
+                         cb: backtrace_syminfo_callback,
+                         error: backtrace_error_callback,
+                         data: *mut libc::c_void) -> libc::c_int;
+    fn backtrace_pcinfo(state: *mut backtrace_state,
+                        addr: libc::uintptr_t,
+                        cb: backtrace_full_callback,
+                        error: backtrace_error_callback,
+                        data: *mut libc::c_void) -> libc::c_int;
+}
+
+////////////////////////////////////////////////////////////////////////
+// helper callbacks
+////////////////////////////////////////////////////////////////////////
+
+type FileLine = (*const libc::c_char, libc::c_int);
+
+extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char,
+                   _errnum: libc::c_int) {
+    // do nothing for now
+}
+extern fn syminfo_cb(data: *mut libc::c_void,
+                     _pc: libc::uintptr_t,
+                     symname: *const libc::c_char,
+                     _symval: libc::uintptr_t,
+                     _symsize: libc::uintptr_t) {
+    let slot = data as *mut *const libc::c_char;
+    unsafe { *slot = symname; }
+}
+extern fn pcinfo_cb(data: *mut libc::c_void,
+                    _pc: libc::uintptr_t,
+                    filename: *const libc::c_char,
+                    lineno: libc::c_int,
+                    _function: *const libc::c_char) -> libc::c_int {
+    if !filename.is_null() {
+        let slot = data as *mut &mut [FileLine];
+        let buffer = unsafe {ptr::read(slot)};
+
+        // if the buffer is not full, add file:line to the buffer
+        // and adjust the buffer for next possible calls to pcinfo_cb.
+        if !buffer.is_empty() {
+            buffer[0] = (filename, lineno);
+            unsafe { ptr::write(slot, &mut buffer[1..]); }
         }
     }
 
-    Ok(())
+    0
+}
+
+// The libbacktrace API supports creating a state, but it does not
+// support destroying a state. I personally take this to mean that a
+// state is meant to be created and then live forever.
+//
+// I would love to register an at_exit() handler which cleans up this
+// state, but libbacktrace provides no way to do so.
+//
+// With these constraints, this function has a statically cached state
+// that is calculated the first time this is requested. Remember that
+// backtracing all happens serially (one global lock).
+//
+// Things don't work so well on not-Linux since libbacktrace can't track
+// down that executable this is. We at one point used env::current_exe but
+// it turns out that there are some serious security issues with that
+// approach.
+//
+// Specifically, on certain platforms like BSDs, a malicious actor can cause
+// an arbitrary file to be placed at the path returned by current_exe.
+// libbacktrace does not behave defensively in the presence of ill-formed
+// DWARF information, and has been demonstrated to segfault in at least one
+// case. There is no evidence at the moment to suggest that a more carefully
+// constructed file can't cause arbitrary code execution. As a result of all
+// of this, we don't hint libbacktrace with the path to the current process.
+unsafe fn init_state() -> *mut backtrace_state {
+    static mut STATE: *mut backtrace_state = ptr::null_mut();
+    if !STATE.is_null() { return STATE  }
+
+    let filename = match ::sys::backtrace::gnu::get_executable_filename() {
+        Ok((filename, file)) => {
+            // filename is purposely leaked here since libbacktrace requires
+            // it to stay allocated permanently, file is also leaked so that
+            // the file stays locked
+            let filename_ptr = filename.as_ptr();
+            mem::forget(filename);
+            mem::forget(file);
+            filename_ptr
+        },
+        Err(_) => ptr::null(),
+    };
+
+    STATE = backtrace_create_state(filename, 0, error_cb,
+                                   ptr::null_mut());
+    STATE
 }
index 7fb58373251a515be8a104da34b7af5829918597..e22f6702672986091766db4b5f7aadd6ddce784e 100644 (file)
@@ -18,7 +18,7 @@ macro_rules! cfg_if {
 use libc::{c_int, c_void, uintptr_t};
 
 #[repr(C)]
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Debug, Copy, Clone, PartialEq)]
 pub enum _Unwind_Reason_Code {
     _URC_NO_REASON = 0,
     _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
index 626eccfc9ec866ef56411ecc47245c6cc5522dd5..88fee9ed25b8d5aa93cc54e2583e4a7a5a209b7b 100644 (file)
@@ -141,12 +141,12 @@ fn run_test(me: &str) {
     use std::process::Command;
 
     let mut template = Command::new(me);
-    template.env("RUST_BACKTRACE", "1");
+    template.env("RUST_BACKTRACE", "full");
 
     let mut i = 0;
     loop {
         let out = Command::new(me)
-                          .env("RUST_BACKTRACE", "1")
+                          .env("RUST_BACKTRACE", "full")
                           .arg(i.to_string()).output().unwrap();
         let output = str::from_utf8(&out.stdout).unwrap();
         let error = str::from_utf8(&out.stderr).unwrap();
index 834ce984e6632e50678d4358f50947dafd333037..dcdf82682f3a5c9604e5c8e668d8071e99b28e6f 100644 (file)
@@ -47,7 +47,7 @@ fn template(me: &str) -> Command {
 }
 
 fn expected(fn_name: &str) -> String {
-    format!(" backtrace::{}", fn_name)
+    format!(" backtrace::{}", fn_name)
 }
 
 fn runtest(me: &str) {
@@ -59,6 +59,53 @@ fn runtest(me: &str) {
     assert!(s.contains("stack backtrace") && s.contains(&expected("foo")),
             "bad output: {}", s);
 
+    // Make sure than the short version cleans the backtrace.
+    let p = template(me).arg("fail").env("RUST_BACKTRACE", "1").spawn().unwrap();
+    let out = p.wait_with_output().unwrap();
+    assert!(!out.status.success());
+    let s = str::from_utf8(&out.stderr).unwrap();
+    let removed_symbols = &[
+        "std::sys::imp::backtrace",
+        "std::sys_common::backtrace",
+        "std::panicking",
+        "core::panicking",
+        "rust_begin_unwind",
+        "code::result::unwrap_failed",
+        "std::panicking::try::do_call",
+        "__rust_maybe_catch_panic",
+        "__libc_start_main",
+        "__rust_try",
+        "_start",
+    ];
+    for symbol in removed_symbols {
+        assert!(!s.contains(symbol),
+                "{} should be removed from the backtrace",
+                symbol);
+    }
+    assert!(s.contains(" 0:"), "the frame number should start at 0");
+
+    // Only on linux for _start and __libc_start_main
+    #[cfg(target_os="linux")]
+    {
+        // Make sure than the short version cleans the backtrace.
+        let p = template(me).arg("fail").env("RUST_BACKTRACE", "full").spawn().unwrap();
+        let out = p.wait_with_output().unwrap();
+        assert!(!out.status.success());
+        let s = str::from_utf8(&out.stderr).unwrap();
+        let should_be_present = &[
+            "std::panicking",
+            "__rust_maybe_catch_panic",
+            "__libc_start_main",
+            "_start",
+        ];
+        for symbol in should_be_present {
+            // May give false positive due to inlining.
+            assert!(s.contains(symbol),
+            "the full version of the backtrace should contain {}",
+            symbol);
+        }
+    }
+
     // Make sure the stack trace is *not* printed
     // (Remove RUST_BACKTRACE from our own environment, in case developer
     // is running `make check` with it on.)