diff options
| author | bors <bors@rust-lang.org> | 2017-02-27 17:21:37 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2017-02-27 17:21:37 +0000 |
| commit | 4be034e62270ca15c9fff173faef11939092f4e3 (patch) | |
| tree | 10a0445320179f5150b115e6dda4bac00fde552a /src/libstd/sys/unix | |
| parent | 0ed75e1f47d099a17de22eb2c1d2b43ce89ea390 (diff) | |
| parent | 6398b2078d92c583c4c7eb76312d474042a2c68f (diff) | |
| download | rust-4be034e62270ca15c9fff173faef11939092f4e3.tar.gz rust-4be034e62270ca15c9fff173faef11939092f4e3.zip | |
Auto merge of #38165 - Yamakaky:better-backtrace, r=petrochenkov
Improve backtrace formating while panicking.
Fixes #37783.
Done:
- Fix alignment of file paths for better readability
- `RUST_BACKTRACE=full` prints all the informations (current behaviour)
- `RUST_BACKTRACE=(short|yes)` is the default and does:
- Skip irrelevant frames at the beginning and the end
- Remove function address
- Remove the current directory from the absolute paths
- Remove `::hfabe6541873` at the end of the symbols
- `RUST_BACKTRACE=(0|no)` disables the backtrace.
- `RUST_BACKTRACE=<everything else>` is equivalent to `short` for
backward compatibility.
- doc
- More uniform printing across platforms.
Removed, TODO in a new PR:
- Remove path prefix for libraries and libstd
Example of short backtrace:
```rust
fn fail() {
panic!();
}
fn main() {
let closure = || fail();
closure();
}
```
Short:
```
thread 'main' panicked at 'explicit panic', t.rs:2
Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
0: t::fail
at ./t.rs:2
1: t::main::{{closure}}
at ./t.rs:6
2: t::main
at ./t.rs:7
```
Full:
```
thread 'main' panicked at 'This function never returns!', t.rs:2
stack backtrace:
0: 0x558ddf666478 - std::sys::imp::backtrace::tracing::imp::unwind_backtrace::hec84c9dd8389cc5d
at /home/yamakaky/dev/rust/rust/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
1: 0x558ddf65d90e - std::sys_common::backtrace::_print::hfa25f8b31f4b4353
at /home/yamakaky/dev/rust/rust/src/libstd/sys_common/backtrace.rs:71
2: 0x558ddf65cb5e - std::sys_common::backtrace::print::h9b711e11ac3ba805
at /home/yamakaky/dev/rust/rust/src/libstd/sys_common/backtrace.rs:60
3: 0x558ddf66796e - std::panicking::default_hook::{{closure}}::h736d216e74748044
at /home/yamakaky/dev/rust/rust/src/libstd/panicking.rs:355
4: 0x558ddf66743c - std::panicking::default_hook::h16baff397e46ea10
at /home/yamakaky/dev/rust/rust/src/libstd/panicking.rs:371
5: 0x558ddf6682bc - std::panicking::rust_panic_with_hook::h6d5a9bb4eca42c80
at /home/yamakaky/dev/rust/rust/src/libstd/panicking.rs:559
6: 0x558ddf64ea93 - std::panicking::begin_panic::h17dc549df2f10b99
at /home/yamakaky/dev/rust/rust/src/libstd/panicking.rs:521
7: 0x558ddf64ec42 - t::diverges::he6bc43fc925905f5
at /tmp/p/t.rs:2
8: 0x558ddf64ec5a - t::main::h0ffc20356b8a69c0
at /tmp/p/t.rs:6
9: 0x558ddf6687f5 - core::ops::FnOnce::call_once::hce41f19c0db56f93
10: 0x558ddf667cde - std::panicking::try::do_call::hd4c8c97efb4291df
at /home/yamakaky/dev/rust/rust/src/libstd/panicking.rs:464
11: 0x558ddf698d77 - __rust_try
12: 0x558ddf698c57 - __rust_maybe_catch_panic
at /home/yamakaky/dev/rust/rust/src/libpanic_unwind/lib.rs:98
13: 0x558ddf667adb - std::panicking::try::h2c56ed2a59ec1d12
at /home/yamakaky/dev/rust/rust/src/libstd/panicking.rs:440
14: 0x558ddf66cc9a - std::panic::catch_unwind::h390834e0251cc9af
at /home/yamakaky/dev/rust/rust/src/libstd/panic.rs:361
15: 0x558ddf6809ee - std::rt::lang_start::hb73087428e233982
at /home/yamakaky/dev/rust/rust/src/libstd/rt.rs:57
16: 0x558ddf64ec92 - main
17: 0x7fecb869e290 - __libc_start_main
18: 0x558ddf64e8b9 - _start
19: 0x0 - <unknown>
```
Diffstat (limited to 'src/libstd/sys/unix')
| -rw-r--r-- | src/libstd/sys/unix/backtrace/mod.rs | 5 | ||||
| -rw-r--r-- | src/libstd/sys/unix/backtrace/printing/dladdr.rs | 62 | ||||
| -rw-r--r-- | src/libstd/sys/unix/backtrace/printing/gnu.rs | 11 | ||||
| -rw-r--r-- | src/libstd/sys/unix/backtrace/printing/mod.rs | 7 | ||||
| -rw-r--r-- | src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs | 52 | ||||
| -rw-r--r-- | src/libstd/sys/unix/backtrace/tracing/gcc_s.rs | 161 |
6 files changed, 146 insertions, 152 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..ecd32aa9462 100644 --- a/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs +++ b/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs @@ -18,39 +18,31 @@ /// 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 = [::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..cfeabaddda9 100644 --- a/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs +++ b/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs @@ -8,102 +8,99 @@ // 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 { + // These return codes seem to be benign and need to be ignored for backtraces + // to show up properly on all tested platforms. + uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => { + 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 } |
