Skip to content

Commit

Permalink
Improve backtrace formating while panicking.
Browse files Browse the repository at this point in the history
- `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.
  • Loading branch information
Yamakaky committed Feb 15, 2017
1 parent e0044bd commit d50e4cc
Show file tree
Hide file tree
Showing 19 changed files with 797 additions and 519 deletions.
15 changes: 14 additions & 1 deletion src/doc/book/src/functions.md
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
10 changes: 7 additions & 3 deletions src/libstd/panicking.rs
Expand Up @@ -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;
Expand All @@ -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.");
}
Expand Down
11 changes: 8 additions & 3 deletions src/libstd/sys/redox/backtrace.rs
Expand Up @@ -10,9 +10,14 @@

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))
}
5 changes: 4 additions & 1 deletion src/libstd/sys/unix/backtrace/mod.rs
Expand Up @@ -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;
Expand All @@ -100,3 +101,5 @@ pub mod gnu {
Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
}
}

pub struct BacktraceContext;
62 changes: 37 additions & 25 deletions src/libstd/sys/unix/backtrace/printing/dladdr.rs
Expand Up @@ -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;
}
7 changes: 4 additions & 3 deletions src/libstd/sys/unix/backtrace/printing/mod.rs
Expand Up @@ -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"))]
Expand All @@ -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};
}
51 changes: 22 additions & 29 deletions src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs
Expand Up @@ -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;
}

0 comments on commit d50e4cc

Please sign in to comment.