Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a5c2937

Browse files
committedFeb 24, 2017
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> ```
2 parents 9f082d2 + 0982a28 commit a5c2937

File tree

19 files changed

+813
-519
lines changed

19 files changed

+813
-519
lines changed
 

‎src/doc/book/src/functions.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,19 @@ If you want more information, you can get a backtrace by setting the
230230
```text
231231
$ RUST_BACKTRACE=1 ./diverges
232232
thread 'main' panicked at 'This function never returns!', hello.rs:2
233+
Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
234+
stack backtrace:
235+
hello::diverges
236+
at ./hello.rs:2
237+
hello::main
238+
at ./hello.rs:6
239+
```
240+
241+
If you want the complete backtrace and filenames:
242+
243+
```text
244+
$ RUST_BACKTRACE=full ./diverges
245+
thread 'main' panicked at 'This function never returns!', hello.rs:2
233246
stack backtrace:
234247
1: 0x7f402773a829 - sys::backtrace::write::h0942de78b6c02817K8r
235248
2: 0x7f402773d7fc - panicking::on_panic::h3f23f9d0b5f4c91bu9w
@@ -262,7 +275,7 @@ note: Run with `RUST_BACKTRACE=1` for a backtrace.
262275
`RUST_BACKTRACE` also works with Cargo’s `run` command:
263276

264277
```text
265-
$ RUST_BACKTRACE=1 cargo run
278+
$ RUST_BACKTRACE=full cargo run
266279
Running `target/debug/diverges`
267280
thread 'main' panicked at 'This function never returns!', hello.rs:2
268281
stack backtrace:

‎src/libstd/panicking.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,11 @@ fn default_hook(info: &PanicInfo) {
320320
let log_backtrace = {
321321
let panics = update_panic_count(0);
322322

323-
panics >= 2 || backtrace::log_enabled()
323+
if panics >= 2 {
324+
Some(backtrace::PrintFormat::Full)
325+
} else {
326+
backtrace::log_enabled()
327+
}
324328
};
325329

326330
let file = info.location.file;
@@ -347,8 +351,8 @@ fn default_hook(info: &PanicInfo) {
347351

348352
static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
349353

350-
if log_backtrace {
351-
let _ = backtrace::write(err);
354+
if let Some(format) = log_backtrace {
355+
let _ = backtrace::print(err, format);
352356
} else if FIRST_PANIC.compare_and_swap(true, false, Ordering::SeqCst) {
353357
let _ = writeln!(err, "note: Run with `RUST_BACKTRACE=1` for a backtrace.");
354358
}

‎src/libstd/sys/redox/backtrace.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,14 @@
1010

1111
use libc;
1212
use io;
13-
use sys_common::backtrace::output;
13+
use sys_common::backtrace::Frame;
14+
15+
pub use sys_common::gnu::libbacktrace::*;
16+
pub struct BacktraceContext;
1417

1518
#[inline(never)]
16-
pub fn write(w: &mut io::Write) -> io::Result<()> {
17-
output(w, 0, 0 as *mut libc::c_void, None)
19+
pub fn unwind_backtrace(frames: &mut [Frame])
20+
-> io::Result<(usize, BacktraceContext)>
21+
{
22+
Ok((0, BacktraceContext))
1823
}

‎src/libstd/sys/unix/backtrace/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@
8383
/// to symbols. This is a bit of a hokey implementation as-is, but it works for
8484
/// all unix platforms we support right now, so it at least gets the job done.
8585
86-
pub use self::tracing::write;
86+
pub use self::tracing::unwind_backtrace;
87+
pub use self::printing::{foreach_symbol_fileline, resolve_symname};
8788

8889
// tracing impls:
8990
mod tracing;
@@ -100,3 +101,5 @@ pub mod gnu {
100101
Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
101102
}
102103
}
104+
105+
pub struct BacktraceContext;

‎src/libstd/sys/unix/backtrace/printing/dladdr.rs

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,45 @@
99
// except according to those terms.
1010

1111
use io;
12-
use io::prelude::*;
12+
use intrinsics;
13+
use ffi::CStr;
1314
use libc;
15+
use sys::backtrace::BacktraceContext;
16+
use sys_common::backtrace::Frame;
1417

15-
pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void,
16-
_symaddr: *mut libc::c_void) -> io::Result<()> {
17-
use sys_common::backtrace::{output};
18-
use intrinsics;
19-
use ffi::CStr;
20-
21-
#[repr(C)]
22-
struct Dl_info {
23-
dli_fname: *const libc::c_char,
24-
dli_fbase: *mut libc::c_void,
25-
dli_sname: *const libc::c_char,
26-
dli_saddr: *mut libc::c_void,
27-
}
28-
extern {
29-
fn dladdr(addr: *const libc::c_void,
30-
info: *mut Dl_info) -> libc::c_int;
18+
pub fn resolve_symname<F>(frame: Frame,
19+
callback: F,
20+
_: &BacktraceContext) -> io::Result<()>
21+
where F: FnOnce(Option<&str>) -> io::Result<()>
22+
{
23+
unsafe {
24+
let mut info: Dl_info = intrinsics::init();
25+
let symname = if dladdr(frame.exact_position, &mut info) == 0 {
26+
None
27+
} else {
28+
CStr::from_ptr(info.dli_sname).to_str().ok()
29+
};
30+
callback(symname)
3131
}
32+
}
3233

33-
let mut info: Dl_info = unsafe { intrinsics::init() };
34-
if unsafe { dladdr(addr, &mut info) == 0 } {
35-
output(w, idx,addr, None)
36-
} else {
37-
output(w, idx, addr, Some(unsafe {
38-
CStr::from_ptr(info.dli_sname).to_bytes()
39-
}))
40-
}
34+
pub fn foreach_symbol_fileline<F>(_symbol_addr: Frame,
35+
_f: F,
36+
_: &BacktraceContext) -> io::Result<bool>
37+
where F: FnMut(&[u8], libc::c_int) -> io::Result<()>
38+
{
39+
Ok(false)
40+
}
41+
42+
#[repr(C)]
43+
struct Dl_info {
44+
dli_fname: *const libc::c_char,
45+
dli_fbase: *mut libc::c_void,
46+
dli_sname: *const libc::c_char,
47+
dli_saddr: *mut libc::c_void,
48+
}
49+
50+
extern {
51+
fn dladdr(addr: *const libc::c_void,
52+
info: *mut Dl_info) -> libc::c_int;
4153
}

‎src/libstd/sys/unix/backtrace/printing/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
pub use self::imp::print;
11+
pub use self::imp::{foreach_symbol_fileline, resolve_symname};
1212

1313
#[cfg(any(target_os = "macos", target_os = "ios",
1414
target_os = "emscripten"))]
@@ -17,5 +17,6 @@ mod imp;
1717

1818
#[cfg(not(any(target_os = "macos", target_os = "ios",
1919
target_os = "emscripten")))]
20-
#[path = "gnu.rs"]
21-
mod imp;
20+
mod imp {
21+
pub use sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname};
22+
}

‎src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,39 +18,32 @@
1818
/// simple to use it should be used only on iOS devices as the only viable
1919
/// option.
2020
21-
use io::prelude::*;
2221
use io;
2322
use libc;
2423
use mem;
25-
use sys::mutex::Mutex;
24+
use sys::backtrace::BacktraceContext;
25+
use sys_common::backtrace::Frame;
2626

27-
use super::super::printing::print;
28-
29-
#[inline(never)]
30-
pub fn write(w: &mut Write) -> io::Result<()> {
31-
extern {
32-
fn backtrace(buf: *mut *mut libc::c_void,
33-
sz: libc::c_int) -> libc::c_int;
27+
#[inline(never)] // if we know this is a function call, we can skip it when
28+
// tracing
29+
pub fn unwind_backtrace(frames: &mut [Frame])
30+
-> io::Result<(usize, BacktraceContext)>
31+
{
32+
const FRAME_LEN: usize = 100;
33+
assert!(FRAME_LEN >= frames.len());
34+
let mut raw_frames = [::std::ptr::null_mut(); FRAME_LEN];
35+
let nb_frames = unsafe {
36+
backtrace(raw_frames.as_mut_ptr(), raw_frames.len() as libc::c_int)
37+
} as usize;
38+
for (from, to) in raw_frames.iter().zip(frames.iter_mut()).take(nb_frames) {
39+
*to = Frame {
40+
exact_position: *from,
41+
symbol_addr: *from,
42+
};
3443
}
44+
Ok((nb_frames as usize, BacktraceContext))
45+
}
3546

36-
// while it doesn't requires lock for work as everything is
37-
// local, it still displays much nicer backtraces when a
38-
// couple of threads panic simultaneously
39-
static LOCK: Mutex = Mutex::new();
40-
unsafe {
41-
LOCK.lock();
42-
43-
writeln!(w, "stack backtrace:")?;
44-
// 100 lines should be enough
45-
const SIZE: usize = 100;
46-
let mut buf: [*mut libc::c_void; SIZE] = mem::zeroed();
47-
let cnt = backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as usize;
48-
49-
// skipping the first one as it is write itself
50-
for i in 1..cnt {
51-
print(w, i as isize, buf[i], buf[i])?
52-
}
53-
LOCK.unlock();
54-
}
55-
Ok(())
47+
extern {
48+
fn backtrace(buf: *mut *mut libc::c_void, sz: libc::c_int) -> libc::c_int;
5649
}

‎src/libstd/sys/unix/backtrace/tracing/gcc_s.rs

Lines changed: 77 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -8,102 +8,97 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use error::Error;
1112
use io;
12-
use io::prelude::*;
1313
use libc;
14-
use mem;
15-
use sys_common::mutex::Mutex;
14+
use sys::backtrace::BacktraceContext;
15+
use sys_common::backtrace::Frame;
1616

17-
use super::super::printing::print;
1817
use unwind as uw;
1918

20-
#[inline(never)] // if we know this is a function call, we can skip it when
21-
// tracing
22-
pub fn write(w: &mut Write) -> io::Result<()> {
23-
struct Context<'a> {
24-
idx: isize,
25-
writer: &'a mut (Write+'a),
26-
last_error: Option<io::Error>,
27-
}
19+
struct Context<'a> {
20+
idx: usize,
21+
frames: &'a mut [Frame],
22+
}
2823

29-
// When using libbacktrace, we use some necessary global state, so we
30-
// need to prevent more than one thread from entering this block. This
31-
// is semi-reasonable in terms of printing anyway, and we know that all
32-
// I/O done here is blocking I/O, not green I/O, so we don't have to
33-
// worry about this being a native vs green mutex.
34-
static LOCK: Mutex = Mutex::new();
35-
unsafe {
36-
LOCK.lock();
24+
#[derive(Debug)]
25+
struct UnwindError(uw::_Unwind_Reason_Code);
3726

38-
writeln!(w, "stack backtrace:")?;
27+
impl Error for UnwindError {
28+
fn description(&self) -> &'static str {
29+
"unexpected return value while unwinding"
30+
}
31+
}
3932

40-
let mut cx = Context { writer: w, last_error: None, idx: 0 };
41-
let ret = match {
42-
uw::_Unwind_Backtrace(trace_fn,
43-
&mut cx as *mut Context as *mut libc::c_void)
44-
} {
45-
uw::_URC_NO_REASON => {
46-
match cx.last_error {
47-
Some(err) => Err(err),
48-
None => Ok(())
49-
}
50-
}
51-
_ => Ok(()),
52-
};
53-
LOCK.unlock();
54-
return ret
33+
impl ::fmt::Display for UnwindError {
34+
fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
35+
write!(f, "{}: {:?}", self.description(), self.0)
5536
}
37+
}
5638

57-
extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
58-
arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
59-
let cx: &mut Context = unsafe { mem::transmute(arg) };
60-
let mut ip_before_insn = 0;
61-
let mut ip = unsafe {
62-
uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void
63-
};
64-
if !ip.is_null() && ip_before_insn == 0 {
65-
// this is a non-signaling frame, so `ip` refers to the address
66-
// after the calling instruction. account for that.
67-
ip = (ip as usize - 1) as *mut _;
39+
#[inline(never)] // if we know this is a function call, we can skip it when
40+
// tracing
41+
pub fn unwind_backtrace(frames: &mut [Frame])
42+
-> io::Result<(usize, BacktraceContext)>
43+
{
44+
let mut cx = Context {
45+
idx: 0,
46+
frames: frames,
47+
};
48+
let result_unwind = unsafe {
49+
uw::_Unwind_Backtrace(trace_fn,
50+
&mut cx as *mut Context
51+
as *mut libc::c_void)
52+
};
53+
// See libunwind:src/unwind/Backtrace.c for the return values.
54+
// No, there is no doc.
55+
match result_unwind {
56+
uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR => {
57+
Ok((cx.idx, BacktraceContext))
6858
}
69-
70-
// dladdr() on osx gets whiny when we use FindEnclosingFunction, and
71-
// it appears to work fine without it, so we only use
72-
// FindEnclosingFunction on non-osx platforms. In doing so, we get a
73-
// slightly more accurate stack trace in the process.
74-
//
75-
// This is often because panic involves the last instruction of a
76-
// function being "call std::rt::begin_unwind", with no ret
77-
// instructions after it. This means that the return instruction
78-
// pointer points *outside* of the calling function, and by
79-
// unwinding it we go back to the original function.
80-
let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
81-
ip
82-
} else {
83-
unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
84-
};
85-
86-
// Don't print out the first few frames (they're not user frames)
87-
cx.idx += 1;
88-
if cx.idx <= 0 { return uw::_URC_NO_REASON }
89-
// Don't print ginormous backtraces
90-
if cx.idx > 100 {
91-
match write!(cx.writer, " ... <frames omitted>\n") {
92-
Ok(()) => {}
93-
Err(e) => { cx.last_error = Some(e); }
94-
}
95-
return uw::_URC_FAILURE
59+
_ => {
60+
Err(io::Error::new(io::ErrorKind::Other,
61+
UnwindError(result_unwind)))
9662
}
63+
}
64+
}
9765

98-
// Once we hit an error, stop trying to print more frames
99-
if cx.last_error.is_some() { return uw::_URC_FAILURE }
66+
extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
67+
arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
68+
let cx = unsafe { &mut *(arg as *mut Context) };
69+
let mut ip_before_insn = 0;
70+
let mut ip = unsafe {
71+
uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void
72+
};
73+
if !ip.is_null() && ip_before_insn == 0 {
74+
// this is a non-signaling frame, so `ip` refers to the address
75+
// after the calling instruction. account for that.
76+
ip = (ip as usize - 1) as *mut _;
77+
}
10078

101-
match print(cx.writer, cx.idx, ip, symaddr) {
102-
Ok(()) => {}
103-
Err(e) => { cx.last_error = Some(e); }
104-
}
79+
// dladdr() on osx gets whiny when we use FindEnclosingFunction, and
80+
// it appears to work fine without it, so we only use
81+
// FindEnclosingFunction on non-osx platforms. In doing so, we get a
82+
// slightly more accurate stack trace in the process.
83+
//
84+
// This is often because panic involves the last instruction of a
85+
// function being "call std::rt::begin_unwind", with no ret
86+
// instructions after it. This means that the return instruction
87+
// pointer points *outside* of the calling function, and by
88+
// unwinding it we go back to the original function.
89+
let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
90+
ip
91+
} else {
92+
unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
93+
};
10594

106-
// keep going
107-
uw::_URC_NO_REASON
95+
if cx.idx < cx.frames.len() {
96+
cx.frames[cx.idx] = Frame {
97+
symbol_addr: symaddr,
98+
exact_position: ip,
99+
};
100+
cx.idx += 1;
108101
}
102+
103+
uw::_URC_NO_REASON
109104
}

‎src/libstd/sys/windows/backtrace.rs renamed to ‎src/libstd/sys/windows/backtrace/mod.rs

Lines changed: 72 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -24,37 +24,87 @@
2424
2525
#![allow(deprecated)] // dynamic_lib
2626

27-
use io::prelude::*;
28-
2927
use io;
3028
use libc::c_void;
3129
use mem;
3230
use ptr;
3331
use sys::c;
3432
use sys::dynamic_lib::DynamicLibrary;
35-
use sys::mutex::Mutex;
33+
use sys_common::backtrace::Frame;
3634

3735
macro_rules! sym {
3836
($lib:expr, $e:expr, $t:ident) => (
39-
match $lib.symbol($e) {
40-
Ok(f) => $crate::mem::transmute::<usize, $t>(f),
41-
Err(..) => return Ok(())
42-
}
37+
$lib.symbol($e).map(|f| unsafe {
38+
$crate::mem::transmute::<usize, $t>(f)
39+
})
4340
)
4441
}
4542

46-
#[cfg(target_env = "msvc")]
47-
#[path = "printing/msvc.rs"]
48-
mod printing;
49-
50-
#[cfg(target_env = "gnu")]
51-
#[path = "printing/gnu.rs"]
5243
mod printing;
5344

5445
#[cfg(target_env = "gnu")]
5546
#[path = "backtrace_gnu.rs"]
5647
pub mod gnu;
5748

49+
pub use self::printing::{resolve_symname, foreach_symbol_fileline};
50+
51+
pub fn unwind_backtrace(frames: &mut [Frame])
52+
-> io::Result<(usize, BacktraceContext)>
53+
{
54+
let dbghelp = DynamicLibrary::open("dbghelp.dll")?;
55+
56+
// Fetch the symbols necessary from dbghelp.dll
57+
let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn)?;
58+
let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn)?;
59+
let StackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn)?;
60+
61+
// Allocate necessary structures for doing the stack walk
62+
let process = unsafe { c::GetCurrentProcess() };
63+
let thread = unsafe { c::GetCurrentThread() };
64+
let mut context: c::CONTEXT = unsafe { mem::zeroed() };
65+
unsafe { c::RtlCaptureContext(&mut context) };
66+
let mut frame: c::STACKFRAME64 = unsafe { mem::zeroed() };
67+
let image = init_frame(&mut frame, &context);
68+
69+
let backtrace_context = BacktraceContext {
70+
handle: process,
71+
SymCleanup: SymCleanup,
72+
dbghelp: dbghelp,
73+
};
74+
75+
// Initialize this process's symbols
76+
let ret = unsafe { SymInitialize(process, ptr::null_mut(), c::TRUE) };
77+
if ret != c::TRUE {
78+
return Ok((0, backtrace_context))
79+
}
80+
81+
// And now that we're done with all the setup, do the stack walking!
82+
// Start from -1 to avoid printing this stack frame, which will
83+
// always be exactly the same.
84+
let mut i = 0;
85+
unsafe {
86+
while i < frames.len() &&
87+
StackWalk64(image, process, thread, &mut frame, &mut context,
88+
ptr::null_mut(),
89+
ptr::null_mut(),
90+
ptr::null_mut(),
91+
ptr::null_mut()) == c::TRUE
92+
{
93+
let addr = frame.AddrPC.Offset;
94+
if addr == frame.AddrReturn.Offset || addr == 0 ||
95+
frame.AddrReturn.Offset == 0 { break }
96+
97+
frames[i] = Frame {
98+
symbol_addr: (addr - 1) as *const c_void,
99+
exact_position: (addr - 1) as *const c_void,
100+
};
101+
i += 1;
102+
}
103+
}
104+
105+
Ok((i, backtrace_context))
106+
}
107+
58108
type SymInitializeFn =
59109
unsafe extern "system" fn(c::HANDLE, *mut c_void,
60110
c::BOOL) -> c::BOOL;
@@ -68,8 +118,8 @@ type StackWalk64Fn =
68118
*mut c_void, *mut c_void) -> c::BOOL;
69119

70120
#[cfg(target_arch = "x86")]
71-
pub fn init_frame(frame: &mut c::STACKFRAME64,
72-
ctx: &c::CONTEXT) -> c::DWORD {
121+
fn init_frame(frame: &mut c::STACKFRAME64,
122+
ctx: &c::CONTEXT) -> c::DWORD {
73123
frame.AddrPC.Offset = ctx.Eip as u64;
74124
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
75125
frame.AddrStack.Offset = ctx.Esp as u64;
@@ -80,8 +130,8 @@ pub fn init_frame(frame: &mut c::STACKFRAME64,
80130
}
81131

82132
#[cfg(target_arch = "x86_64")]
83-
pub fn init_frame(frame: &mut c::STACKFRAME64,
84-
ctx: &c::CONTEXT) -> c::DWORD {
133+
fn init_frame(frame: &mut c::STACKFRAME64,
134+
ctx: &c::CONTEXT) -> c::DWORD {
85135
frame.AddrPC.Offset = ctx.Rip as u64;
86136
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
87137
frame.AddrStack.Offset = ctx.Rsp as u64;
@@ -91,73 +141,16 @@ pub fn init_frame(frame: &mut c::STACKFRAME64,
91141
c::IMAGE_FILE_MACHINE_AMD64
92142
}
93143

94-
struct Cleanup {
144+
pub struct BacktraceContext {
95145
handle: c::HANDLE,
96146
SymCleanup: SymCleanupFn,
147+
// Only used in printing for msvc and not gnu
148+
#[allow(dead_code)]
149+
dbghelp: DynamicLibrary,
97150
}
98151

99-
impl Drop for Cleanup {
152+
impl Drop for BacktraceContext {
100153
fn drop(&mut self) {
101154
unsafe { (self.SymCleanup)(self.handle); }
102155
}
103156
}
104-
105-
pub fn write(w: &mut Write) -> io::Result<()> {
106-
// According to windows documentation, all dbghelp functions are
107-
// single-threaded.
108-
static LOCK: Mutex = Mutex::new();
109-
unsafe {
110-
LOCK.lock();
111-
let res = _write(w);
112-
LOCK.unlock();
113-
return res
114-
}
115-
}
116-
117-
unsafe fn _write(w: &mut Write) -> io::Result<()> {
118-
let dbghelp = match DynamicLibrary::open("dbghelp.dll") {
119-
Ok(lib) => lib,
120-
Err(..) => return Ok(()),
121-
};
122-
123-
// Fetch the symbols necessary from dbghelp.dll
124-
let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn);
125-
let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn);
126-
let StackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn);
127-
128-
// Allocate necessary structures for doing the stack walk
129-
let process = c::GetCurrentProcess();
130-
let thread = c::GetCurrentThread();
131-
let mut context: c::CONTEXT = mem::zeroed();
132-
c::RtlCaptureContext(&mut context);
133-
let mut frame: c::STACKFRAME64 = mem::zeroed();
134-
let image = init_frame(&mut frame, &context);
135-
136-
// Initialize this process's symbols
137-
let ret = SymInitialize(process, ptr::null_mut(), c::TRUE);
138-
if ret != c::TRUE { return Ok(()) }
139-
let _c = Cleanup { handle: process, SymCleanup: SymCleanup };
140-
141-
// And now that we're done with all the setup, do the stack walking!
142-
// Start from -1 to avoid printing this stack frame, which will
143-
// always be exactly the same.
144-
let mut i = -1;
145-
write!(w, "stack backtrace:\n")?;
146-
while StackWalk64(image, process, thread, &mut frame, &mut context,
147-
ptr::null_mut(),
148-
ptr::null_mut(),
149-
ptr::null_mut(),
150-
ptr::null_mut()) == c::TRUE {
151-
let addr = frame.AddrPC.Offset;
152-
if addr == frame.AddrReturn.Offset || addr == 0 ||
153-
frame.AddrReturn.Offset == 0 { break }
154-
155-
i += 1;
156-
157-
if i >= 0 {
158-
printing::print(w, i, addr - 1, process, &dbghelp)?;
159-
}
160-
}
161-
162-
Ok(())
163-
}

‎src/libstd/sys/unix/backtrace/printing/gnu.rs renamed to ‎src/libstd/sys/windows/backtrace/printing/mod.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,13 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
pub use sys_common::gnu::libbacktrace::print;
11+
#[cfg(target_env = "msvc")]
12+
#[path = "msvc.rs"]
13+
mod printing;
14+
15+
#[cfg(target_env = "gnu")]
16+
mod printing {
17+
pub use sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname};
18+
}
19+
20+
pub use self::printing::{foreach_symbol_fileline, resolve_symname};
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use ffi::CStr;
12+
use io;
13+
use libc::{c_ulong, c_int, c_char};
14+
use mem;
15+
use sys::c;
16+
use sys::backtrace::BacktraceContext;
17+
use sys_common::backtrace::Frame;
18+
19+
type SymFromAddrFn =
20+
unsafe extern "system" fn(c::HANDLE, u64, *mut u64,
21+
*mut c::SYMBOL_INFO) -> c::BOOL;
22+
type SymGetLineFromAddr64Fn =
23+
unsafe extern "system" fn(c::HANDLE, u64, *mut u32,
24+
*mut c::IMAGEHLP_LINE64) -> c::BOOL;
25+
26+
/// Converts a pointer to symbol to its string value.
27+
pub fn resolve_symname<F>(frame: Frame,
28+
callback: F,
29+
context: &BacktraceContext) -> io::Result<()>
30+
where F: FnOnce(Option<&str>) -> io::Result<()>
31+
{
32+
let SymFromAddr = sym!(&context.dbghelp, "SymFromAddr", SymFromAddrFn)?;
33+
34+
unsafe {
35+
let mut info: c::SYMBOL_INFO = mem::zeroed();
36+
info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
37+
// the struct size in C. the value is different to
38+
// `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
39+
// due to struct alignment.
40+
info.SizeOfStruct = 88;
41+
42+
let mut displacement = 0u64;
43+
let ret = SymFromAddr(context.handle,
44+
frame.symbol_addr as u64,
45+
&mut displacement,
46+
&mut info);
47+
48+
let symname = if ret == c::TRUE {
49+
let ptr = info.Name.as_ptr() as *const c_char;
50+
CStr::from_ptr(ptr).to_str().ok()
51+
} else {
52+
None
53+
};
54+
callback(symname)
55+
}
56+
}
57+
58+
pub fn foreach_symbol_fileline<F>(frame: Frame,
59+
mut f: F,
60+
context: &BacktraceContext)
61+
-> io::Result<bool>
62+
where F: FnMut(&[u8], c_int) -> io::Result<()>
63+
{
64+
let SymGetLineFromAddr64 = sym!(&context.dbghelp,
65+
"SymGetLineFromAddr64",
66+
SymGetLineFromAddr64Fn)?;
67+
68+
unsafe {
69+
let mut line: c::IMAGEHLP_LINE64 = mem::zeroed();
70+
line.SizeOfStruct = ::mem::size_of::<c::IMAGEHLP_LINE64>() as u32;
71+
72+
let mut displacement = 0u32;
73+
let ret = SymGetLineFromAddr64(context.handle,
74+
frame.exact_position as u64,
75+
&mut displacement,
76+
&mut line);
77+
if ret == c::TRUE {
78+
let name = CStr::from_ptr(line.Filename).to_bytes();
79+
f(name, line.LineNumber as c_int)?;
80+
}
81+
Ok(false)
82+
}
83+
}

‎src/libstd/sys/windows/printing/gnu.rs

Lines changed: 0 additions & 26 deletions
This file was deleted.

‎src/libstd/sys/windows/printing/msvc.rs

Lines changed: 0 additions & 73 deletions
This file was deleted.

‎src/libstd/sys_common/backtrace.rs

Lines changed: 243 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,60 +10,259 @@
1010

1111
#![cfg_attr(target_os = "nacl", allow(dead_code))]
1212

13+
/// Common code for printing the backtrace in the same way across the different
14+
/// supported platforms.
15+
1316
use env;
1417
use io::prelude::*;
1518
use io;
1619
use libc;
1720
use str;
1821
use sync::atomic::{self, Ordering};
22+
use path::Path;
23+
use sys::mutex::Mutex;
24+
use ptr;
1925

20-
pub use sys::backtrace::write;
26+
pub use sys::backtrace::{
27+
unwind_backtrace,
28+
resolve_symname,
29+
foreach_symbol_fileline,
30+
BacktraceContext
31+
};
2132

2233
#[cfg(target_pointer_width = "64")]
2334
pub const HEX_WIDTH: usize = 18;
2435

2536
#[cfg(target_pointer_width = "32")]
2637
pub const HEX_WIDTH: usize = 10;
2738

39+
/// Represents an item in the backtrace list. See `unwind_backtrace` for how
40+
/// it is created.
41+
#[derive(Debug, Copy, Clone)]
42+
pub struct Frame {
43+
/// Exact address of the call that failed.
44+
pub exact_position: *const libc::c_void,
45+
/// Address of the enclosing function.
46+
pub symbol_addr: *const libc::c_void,
47+
}
48+
49+
/// Max number of frames to print.
50+
const MAX_NB_FRAMES: usize = 100;
51+
52+
/// Prints the current backtrace.
53+
pub fn print(w: &mut Write, format: PrintFormat) -> io::Result<()> {
54+
static LOCK: Mutex = Mutex::new();
55+
56+
// Use a lock to prevent mixed output in multithreading context.
57+
// Some platforms also requires it, like `SymFromAddr` on Windows.
58+
unsafe {
59+
LOCK.lock();
60+
let res = _print(w, format);
61+
LOCK.unlock();
62+
res
63+
}
64+
}
65+
66+
fn _print(w: &mut Write, format: PrintFormat) -> io::Result<()> {
67+
let mut frames = [Frame {
68+
exact_position: ptr::null(),
69+
symbol_addr: ptr::null(),
70+
}; MAX_NB_FRAMES];
71+
let (nb_frames, context) = unwind_backtrace(&mut frames)?;
72+
let (skipped_before, skipped_after) =
73+
filter_frames(&frames[..nb_frames], format, &context);
74+
if skipped_before + skipped_after > 0 {
75+
writeln!(w, "note: Some details are omitted, \
76+
run with `RUST_BACKTRACE=full` for a verbose backtrace.")?;
77+
}
78+
writeln!(w, "stack backtrace:")?;
79+
80+
let filtered_frames = &frames[..nb_frames - skipped_after];
81+
for (index, frame) in filtered_frames.iter().skip(skipped_before).enumerate() {
82+
resolve_symname(*frame, |symname| {
83+
output(w, index, *frame, symname, format)
84+
}, &context)?;
85+
let has_more_filenames = foreach_symbol_fileline(*frame, |file, line| {
86+
output_fileline(w, file, line, format)
87+
}, &context)?;
88+
if has_more_filenames {
89+
w.write_all(b" <... and possibly more>")?;
90+
}
91+
}
92+
93+
Ok(())
94+
}
95+
96+
fn filter_frames(frames: &[Frame],
97+
format: PrintFormat,
98+
context: &BacktraceContext) -> (usize, usize)
99+
{
100+
if format == PrintFormat::Full {
101+
return (0, 0);
102+
}
103+
104+
// We want to filter out frames with some prefixes
105+
// from both top and bottom of the call stack.
106+
static BAD_PREFIXES_TOP: &'static [&'static str] = &[
107+
"_ZN3std3sys3imp9backtrace",
108+
"ZN3std3sys3imp9backtrace",
109+
"_ZN3std10sys_common9backtrace",
110+
"ZN3std10sys_common9backtrace",
111+
"_ZN3std9panicking",
112+
"ZN3std9panicking",
113+
"std::panicking",
114+
"_ZN4core9panicking",
115+
"ZN4core9panicking",
116+
"core::panicking",
117+
"_ZN4core6result13unwrap_failed",
118+
"ZN4core6result13unwrap_failed",
119+
"rust_begin_unwind",
120+
"_ZN4drop",
121+
"mingw_set_invalid_parameter_handler",
122+
];
123+
static BAD_PREFIXES_BOTTOM: &'static [&'static str] = &[
124+
"_ZN3std9panicking",
125+
"ZN3std9panicking",
126+
"std::panicking",
127+
"_ZN4core9panicking",
128+
"ZN4core9panicking",
129+
"core::panicking",
130+
"_ZN3std2rt10lang_start",
131+
"ZN3std2rt10lang_start",
132+
"__rust_maybe_catch_panic",
133+
"_rust_maybe_catch_panic",
134+
"__libc_start_main",
135+
"__rust_try",
136+
"_start",
137+
"BaseThreadInitThunk",
138+
"__scrt_common_main_seh",
139+
"_ZN4drop",
140+
"mingw_set_invalid_parameter_handler",
141+
];
142+
143+
let is_good_frame = |frame: Frame, bad_prefixes: &[&str]| {
144+
resolve_symname(frame, |symname| {
145+
if let Some(mangled_symbol_name) = symname {
146+
if !bad_prefixes.iter().any(|s| mangled_symbol_name.starts_with(s)) {
147+
return Ok(())
148+
}
149+
}
150+
Err(io::Error::from(io::ErrorKind::Other))
151+
}, context).is_ok()
152+
};
153+
154+
let skipped_before = frames.iter().position(|frame| {
155+
is_good_frame(*frame, BAD_PREFIXES_TOP)
156+
}).unwrap_or(frames.len());
157+
let skipped_after = frames[skipped_before..].iter().rev().position(|frame| {
158+
is_good_frame(*frame, BAD_PREFIXES_BOTTOM)
159+
}).unwrap_or(frames.len() - skipped_before);
160+
161+
if skipped_before + skipped_after == frames.len() {
162+
// Avoid showing completely empty backtraces
163+
return (0, 0);
164+
}
165+
166+
(skipped_before, skipped_after)
167+
}
168+
169+
/// Controls how the backtrace should be formated.
170+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
171+
pub enum PrintFormat {
172+
/// Show all the frames with absolute path for files.
173+
Full = 2,
174+
/// Show only relevant data from the backtrace.
175+
Short = 3,
176+
}
177+
28178
// For now logging is turned off by default, and this function checks to see
29179
// whether the magical environment variable is present to see if it's turned on.
30-
pub fn log_enabled() -> bool {
180+
pub fn log_enabled() -> Option<PrintFormat> {
31181
static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
32182
match ENABLED.load(Ordering::SeqCst) {
33-
1 => return false,
34-
2 => return true,
35-
_ => {}
183+
0 => {},
184+
1 => return None,
185+
2 => return Some(PrintFormat::Full),
186+
3 => return Some(PrintFormat::Short),
187+
_ => unreachable!(),
36188
}
37189

38190
let val = match env::var_os("RUST_BACKTRACE") {
39-
Some(x) => if &x == "0" { 1 } else { 2 },
40-
None => 1,
191+
Some(x) => if &x == "0" {
192+
None
193+
} else if &x == "full" {
194+
Some(PrintFormat::Full)
195+
} else {
196+
Some(PrintFormat::Short)
197+
},
198+
None => None,
41199
};
42-
ENABLED.store(val, Ordering::SeqCst);
43-
val == 2
200+
ENABLED.store(match val {
201+
Some(v) => v as isize,
202+
None => 1,
203+
}, Ordering::SeqCst);
204+
val
44205
}
45206

46-
// These output functions should now be used everywhere to ensure consistency.
47-
pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void,
48-
s: Option<&[u8]>) -> io::Result<()> {
49-
write!(w, " {:2}: {:2$?} - ", idx, addr, HEX_WIDTH)?;
50-
match s.and_then(|s| str::from_utf8(s).ok()) {
51-
Some(string) => demangle(w, string)?,
52-
None => write!(w, "<unknown>")?,
207+
/// Print the symbol of the backtrace frame.
208+
///
209+
/// These output functions should now be used everywhere to ensure consistency.
210+
/// You may want to also use `output_fileline`.
211+
fn output(w: &mut Write, idx: usize, frame: Frame,
212+
s: Option<&str>, format: PrintFormat) -> io::Result<()> {
213+
// Remove the `17: 0x0 - <unknown>` line.
214+
if format == PrintFormat::Short && frame.exact_position == ptr::null() {
215+
return Ok(());
216+
}
217+
match format {
218+
PrintFormat::Full => write!(w,
219+
" {:2}: {:2$?} - ",
220+
idx,
221+
frame.exact_position,
222+
HEX_WIDTH)?,
223+
PrintFormat::Short => write!(w, " {:2}: ", idx)?,
53224
}
54-
w.write_all(&['\n' as u8])
225+
match s {
226+
Some(string) => demangle(w, string, format)?,
227+
None => w.write_all(b"<unknown>")?,
228+
}
229+
w.write_all(b"\n")
55230
}
56231

232+
/// Print the filename and line number of the backtrace frame.
233+
///
234+
/// See also `output`.
57235
#[allow(dead_code)]
58-
pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int,
59-
more: bool) -> io::Result<()> {
60-
let file = str::from_utf8(file).unwrap_or("<unknown>");
236+
fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int,
237+
format: PrintFormat) -> io::Result<()> {
61238
// prior line: " ##: {:2$} - func"
62-
write!(w, " {:3$}at {}:{}", "", file, line, HEX_WIDTH)?;
63-
if more {
64-
write!(w, " <... and possibly more>")?;
239+
w.write_all(b"")?;
240+
match format {
241+
PrintFormat::Full => write!(w,
242+
" {:1$}",
243+
"",
244+
HEX_WIDTH)?,
245+
PrintFormat::Short => write!(w, " ")?,
246+
}
247+
248+
let file = str::from_utf8(file).unwrap_or("<unknown>");
249+
let file_path = Path::new(file);
250+
let mut already_printed = false;
251+
if format == PrintFormat::Short && file_path.is_absolute() {
252+
if let Ok(cwd) = env::current_dir() {
253+
if let Ok(stripped) = file_path.strip_prefix(&cwd) {
254+
if let Some(s) = stripped.to_str() {
255+
write!(w, " at ./{}:{}", s, line)?;
256+
already_printed = true;
257+
}
258+
}
259+
}
260+
}
261+
if !already_printed {
262+
write!(w, " at {}:{}", file, line)?;
65263
}
66-
w.write_all(&['\n' as u8])
264+
265+
w.write_all(b"\n")
67266
}
68267

69268

@@ -84,7 +283,7 @@ pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int,
84283
// Note that this demangler isn't quite as fancy as it could be. We have lots
85284
// of other information in our symbols like hashes, version, type information,
86285
// etc. Additionally, this doesn't handle glue symbols at all.
87-
pub fn demangle(writer: &mut Write, s: &str) -> io::Result<()> {
286+
pub fn demangle(writer: &mut Write, s: &str, format: PrintFormat) -> io::Result<()> {
88287
// First validate the symbol. If it doesn't look like anything we're
89288
// expecting, we just print it literally. Note that we must handle non-rust
90289
// symbols because we could have any function in the backtrace.
@@ -123,6 +322,22 @@ pub fn demangle(writer: &mut Write, s: &str) -> io::Result<()> {
123322
if !valid {
124323
writer.write_all(s.as_bytes())?;
125324
} else {
325+
// remove the `::hfc2edb670e5eda97` part at the end of the symbol.
326+
if format == PrintFormat::Short {
327+
// The symbol in still mangled.
328+
let mut split = inner.rsplitn(2, "17h");
329+
match (split.next(), split.next()) {
330+
(Some(addr), rest) => {
331+
if addr.len() == 16 &&
332+
addr.chars().all(|c| c.is_digit(16))
333+
{
334+
inner = rest.unwrap_or("");
335+
}
336+
}
337+
_ => (),
338+
}
339+
}
340+
126341
let mut first = true;
127342
while !inner.is_empty() {
128343
if !first {
@@ -208,7 +423,9 @@ mod tests {
208423
use sys_common;
209424
macro_rules! t { ($a:expr, $b:expr) => ({
210425
let mut m = Vec::new();
211-
sys_common::backtrace::demangle(&mut m, $a).unwrap();
426+
sys_common::backtrace::demangle(&mut m,
427+
$a,
428+
super::PrintFormat::Full).unwrap();
212429
assert_eq!(String::from_utf8(m).unwrap(), $b);
213430
}) }
214431

‎src/libstd/sys_common/gnu/libbacktrace.rs

Lines changed: 181 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -8,186 +8,204 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use io;
12-
use io::prelude::*;
1311
use libc;
14-
use sys_common::backtrace::{output, output_fileline};
15-
16-
pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void,
17-
symaddr: *mut libc::c_void) -> io::Result<()> {
18-
use ffi::CStr;
19-
use mem;
20-
use ptr;
21-
22-
////////////////////////////////////////////////////////////////////////
23-
// libbacktrace.h API
24-
////////////////////////////////////////////////////////////////////////
25-
type backtrace_syminfo_callback =
26-
extern "C" fn(data: *mut libc::c_void,
27-
pc: libc::uintptr_t,
28-
symname: *const libc::c_char,
29-
symval: libc::uintptr_t,
30-
symsize: libc::uintptr_t);
31-
type backtrace_full_callback =
32-
extern "C" fn(data: *mut libc::c_void,
33-
pc: libc::uintptr_t,
34-
filename: *const libc::c_char,
35-
lineno: libc::c_int,
36-
function: *const libc::c_char) -> libc::c_int;
37-
type backtrace_error_callback =
38-
extern "C" fn(data: *mut libc::c_void,
39-
msg: *const libc::c_char,
40-
errnum: libc::c_int);
41-
enum backtrace_state {}
42-
43-
extern {
44-
fn backtrace_create_state(filename: *const libc::c_char,
45-
threaded: libc::c_int,
46-
error: backtrace_error_callback,
47-
data: *mut libc::c_void)
48-
-> *mut backtrace_state;
49-
fn backtrace_syminfo(state: *mut backtrace_state,
50-
addr: libc::uintptr_t,
51-
cb: backtrace_syminfo_callback,
52-
error: backtrace_error_callback,
53-
data: *mut libc::c_void) -> libc::c_int;
54-
fn backtrace_pcinfo(state: *mut backtrace_state,
55-
addr: libc::uintptr_t,
56-
cb: backtrace_full_callback,
57-
error: backtrace_error_callback,
58-
data: *mut libc::c_void) -> libc::c_int;
59-
}
60-
61-
////////////////////////////////////////////////////////////////////////
62-
// helper callbacks
63-
////////////////////////////////////////////////////////////////////////
64-
65-
type FileLine = (*const libc::c_char, libc::c_int);
66-
67-
extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char,
68-
_errnum: libc::c_int) {
69-
// do nothing for now
70-
}
71-
extern fn syminfo_cb(data: *mut libc::c_void,
72-
_pc: libc::uintptr_t,
73-
symname: *const libc::c_char,
74-
_symval: libc::uintptr_t,
75-
_symsize: libc::uintptr_t) {
76-
let slot = data as *mut *const libc::c_char;
77-
unsafe { *slot = symname; }
78-
}
79-
extern fn pcinfo_cb(data: *mut libc::c_void,
80-
_pc: libc::uintptr_t,
81-
filename: *const libc::c_char,
82-
lineno: libc::c_int,
83-
_function: *const libc::c_char) -> libc::c_int {
84-
if !filename.is_null() {
85-
let slot = data as *mut &mut [FileLine];
86-
let buffer = unsafe {ptr::read(slot)};
87-
88-
// if the buffer is not full, add file:line to the buffer
89-
// and adjust the buffer for next possible calls to pcinfo_cb.
90-
if !buffer.is_empty() {
91-
buffer[0] = (filename, lineno);
92-
unsafe { ptr::write(slot, &mut buffer[1..]); }
93-
}
94-
}
95-
96-
0
97-
}
98-
99-
// The libbacktrace API supports creating a state, but it does not
100-
// support destroying a state. I personally take this to mean that a
101-
// state is meant to be created and then live forever.
102-
//
103-
// I would love to register an at_exit() handler which cleans up this
104-
// state, but libbacktrace provides no way to do so.
105-
//
106-
// With these constraints, this function has a statically cached state
107-
// that is calculated the first time this is requested. Remember that
108-
// backtracing all happens serially (one global lock).
109-
//
110-
// Things don't work so well on not-Linux since libbacktrace can't track
111-
// down that executable this is. We at one point used env::current_exe but
112-
// it turns out that there are some serious security issues with that
113-
// approach.
114-
//
115-
// Specifically, on certain platforms like BSDs, a malicious actor can cause
116-
// an arbitrary file to be placed at the path returned by current_exe.
117-
// libbacktrace does not behave defensively in the presence of ill-formed
118-
// DWARF information, and has been demonstrated to segfault in at least one
119-
// case. There is no evidence at the moment to suggest that a more carefully
120-
// constructed file can't cause arbitrary code execution. As a result of all
121-
// of this, we don't hint libbacktrace with the path to the current process.
122-
unsafe fn init_state() -> *mut backtrace_state {
123-
static mut STATE: *mut backtrace_state = ptr::null_mut();
124-
if !STATE.is_null() { return STATE }
125-
126-
let filename = match ::sys::backtrace::gnu::get_executable_filename() {
127-
Ok((filename, file)) => {
128-
// filename is purposely leaked here since libbacktrace requires
129-
// it to stay allocated permanently, file is also leaked so that
130-
// the file stays locked
131-
let filename_ptr = filename.as_ptr();
132-
mem::forget(filename);
133-
mem::forget(file);
134-
filename_ptr
135-
},
136-
Err(_) => ptr::null(),
137-
};
138-
139-
STATE = backtrace_create_state(filename, 0, error_cb,
140-
ptr::null_mut());
141-
STATE
142-
}
143-
144-
////////////////////////////////////////////////////////////////////////
145-
// translation
146-
////////////////////////////////////////////////////////////////////////
147-
148-
// backtrace errors are currently swept under the rug, only I/O
149-
// errors are reported
150-
let state = unsafe { init_state() };
151-
if state.is_null() {
152-
return output(w, idx, addr, None)
153-
}
154-
let mut data = ptr::null();
155-
let data_addr = &mut data as *mut *const libc::c_char;
156-
let ret = unsafe {
157-
backtrace_syminfo(state, symaddr as libc::uintptr_t,
158-
syminfo_cb, error_cb,
159-
data_addr as *mut libc::c_void)
160-
};
161-
if ret == 0 || data.is_null() {
162-
output(w, idx, addr, None)?;
163-
} else {
164-
output(w, idx, addr, Some(unsafe { CStr::from_ptr(data).to_bytes() }))?;
165-
}
16612

13+
use ffi::CStr;
14+
use io;
15+
use mem;
16+
use ptr;
17+
use sys::backtrace::BacktraceContext;
18+
use sys_common::backtrace::Frame;
19+
20+
pub fn foreach_symbol_fileline<F>(frame: Frame,
21+
mut f: F,
22+
_: &BacktraceContext) -> io::Result<bool>
23+
where F: FnMut(&[u8], libc::c_int) -> io::Result<()>
24+
{
16725
// pcinfo may return an arbitrary number of file:line pairs,
16826
// in the order of stack trace (i.e. inlined calls first).
16927
// in order to avoid allocation, we stack-allocate a fixed size of entries.
17028
const FILELINE_SIZE: usize = 32;
17129
let mut fileline_buf = [(ptr::null(), -1); FILELINE_SIZE];
17230
let ret;
173-
let fileline_count;
174-
{
31+
let fileline_count = {
32+
let state = unsafe { init_state() };
17533
let mut fileline_win: &mut [FileLine] = &mut fileline_buf;
17634
let fileline_addr = &mut fileline_win as *mut &mut [FileLine];
17735
ret = unsafe {
178-
backtrace_pcinfo(state, addr as libc::uintptr_t,
179-
pcinfo_cb, error_cb,
36+
backtrace_pcinfo(state,
37+
frame.exact_position as libc::uintptr_t,
38+
pcinfo_cb,
39+
error_cb,
18040
fileline_addr as *mut libc::c_void)
18141
};
182-
fileline_count = FILELINE_SIZE - fileline_win.len();
183-
}
42+
FILELINE_SIZE - fileline_win.len()
43+
};
18444
if ret == 0 {
185-
for (i, &(file, line)) in fileline_buf[..fileline_count].iter().enumerate() {
45+
for &(file, line) in &fileline_buf[..fileline_count] {
18646
if file.is_null() { continue; } // just to be sure
18747
let file = unsafe { CStr::from_ptr(file).to_bytes() };
188-
output_fileline(w, file, line, i == FILELINE_SIZE - 1)?;
48+
f(file, line)?;
49+
}
50+
Ok(fileline_count == FILELINE_SIZE)
51+
} else {
52+
Ok(false)
53+
}
54+
}
55+
56+
/// Converts a pointer to symbol to its string value.
57+
pub fn resolve_symname<F>(frame: Frame,
58+
callback: F,
59+
_: &BacktraceContext) -> io::Result<()>
60+
where F: FnOnce(Option<&str>) -> io::Result<()>
61+
{
62+
let symname = {
63+
let state = unsafe { init_state() };
64+
if state.is_null() {
65+
None
66+
} else {
67+
let mut data = ptr::null();
68+
let data_addr = &mut data as *mut *const libc::c_char;
69+
let ret = unsafe {
70+
backtrace_syminfo(state,
71+
frame.symbol_addr as libc::uintptr_t,
72+
syminfo_cb,
73+
error_cb,
74+
data_addr as *mut libc::c_void)
75+
};
76+
if ret == 0 || data.is_null() {
77+
None
78+
} else {
79+
unsafe {
80+
CStr::from_ptr(data).to_str().ok()
81+
}
82+
}
83+
}
84+
};
85+
callback(symname)
86+
}
87+
88+
////////////////////////////////////////////////////////////////////////
89+
// libbacktrace.h API
90+
////////////////////////////////////////////////////////////////////////
91+
type backtrace_syminfo_callback =
92+
extern "C" fn(data: *mut libc::c_void,
93+
pc: libc::uintptr_t,
94+
symname: *const libc::c_char,
95+
symval: libc::uintptr_t,
96+
symsize: libc::uintptr_t);
97+
type backtrace_full_callback =
98+
extern "C" fn(data: *mut libc::c_void,
99+
pc: libc::uintptr_t,
100+
filename: *const libc::c_char,
101+
lineno: libc::c_int,
102+
function: *const libc::c_char) -> libc::c_int;
103+
type backtrace_error_callback =
104+
extern "C" fn(data: *mut libc::c_void,
105+
msg: *const libc::c_char,
106+
errnum: libc::c_int);
107+
enum backtrace_state {}
108+
#[link(name = "backtrace", kind = "static")]
109+
#[cfg(all(not(test), not(cargobuild)))]
110+
extern {}
111+
112+
extern {
113+
fn backtrace_create_state(filename: *const libc::c_char,
114+
threaded: libc::c_int,
115+
error: backtrace_error_callback,
116+
data: *mut libc::c_void)
117+
-> *mut backtrace_state;
118+
fn backtrace_syminfo(state: *mut backtrace_state,
119+
addr: libc::uintptr_t,
120+
cb: backtrace_syminfo_callback,
121+
error: backtrace_error_callback,
122+
data: *mut libc::c_void) -> libc::c_int;
123+
fn backtrace_pcinfo(state: *mut backtrace_state,
124+
addr: libc::uintptr_t,
125+
cb: backtrace_full_callback,
126+
error: backtrace_error_callback,
127+
data: *mut libc::c_void) -> libc::c_int;
128+
}
129+
130+
////////////////////////////////////////////////////////////////////////
131+
// helper callbacks
132+
////////////////////////////////////////////////////////////////////////
133+
134+
type FileLine = (*const libc::c_char, libc::c_int);
135+
136+
extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char,
137+
_errnum: libc::c_int) {
138+
// do nothing for now
139+
}
140+
extern fn syminfo_cb(data: *mut libc::c_void,
141+
_pc: libc::uintptr_t,
142+
symname: *const libc::c_char,
143+
_symval: libc::uintptr_t,
144+
_symsize: libc::uintptr_t) {
145+
let slot = data as *mut *const libc::c_char;
146+
unsafe { *slot = symname; }
147+
}
148+
extern fn pcinfo_cb(data: *mut libc::c_void,
149+
_pc: libc::uintptr_t,
150+
filename: *const libc::c_char,
151+
lineno: libc::c_int,
152+
_function: *const libc::c_char) -> libc::c_int {
153+
if !filename.is_null() {
154+
let slot = data as *mut &mut [FileLine];
155+
let buffer = unsafe {ptr::read(slot)};
156+
157+
// if the buffer is not full, add file:line to the buffer
158+
// and adjust the buffer for next possible calls to pcinfo_cb.
159+
if !buffer.is_empty() {
160+
buffer[0] = (filename, lineno);
161+
unsafe { ptr::write(slot, &mut buffer[1..]); }
189162
}
190163
}
191164

192-
Ok(())
165+
0
166+
}
167+
168+
// The libbacktrace API supports creating a state, but it does not
169+
// support destroying a state. I personally take this to mean that a
170+
// state is meant to be created and then live forever.
171+
//
172+
// I would love to register an at_exit() handler which cleans up this
173+
// state, but libbacktrace provides no way to do so.
174+
//
175+
// With these constraints, this function has a statically cached state
176+
// that is calculated the first time this is requested. Remember that
177+
// backtracing all happens serially (one global lock).
178+
//
179+
// Things don't work so well on not-Linux since libbacktrace can't track
180+
// down that executable this is. We at one point used env::current_exe but
181+
// it turns out that there are some serious security issues with that
182+
// approach.
183+
//
184+
// Specifically, on certain platforms like BSDs, a malicious actor can cause
185+
// an arbitrary file to be placed at the path returned by current_exe.
186+
// libbacktrace does not behave defensively in the presence of ill-formed
187+
// DWARF information, and has been demonstrated to segfault in at least one
188+
// case. There is no evidence at the moment to suggest that a more carefully
189+
// constructed file can't cause arbitrary code execution. As a result of all
190+
// of this, we don't hint libbacktrace with the path to the current process.
191+
unsafe fn init_state() -> *mut backtrace_state {
192+
static mut STATE: *mut backtrace_state = ptr::null_mut();
193+
if !STATE.is_null() { return STATE }
194+
195+
let filename = match ::sys::backtrace::gnu::get_executable_filename() {
196+
Ok((filename, file)) => {
197+
// filename is purposely leaked here since libbacktrace requires
198+
// it to stay allocated permanently, file is also leaked so that
199+
// the file stays locked
200+
let filename_ptr = filename.as_ptr();
201+
mem::forget(filename);
202+
mem::forget(file);
203+
filename_ptr
204+
},
205+
Err(_) => ptr::null(),
206+
};
207+
208+
STATE = backtrace_create_state(filename, 0, error_cb,
209+
ptr::null_mut());
210+
STATE
193211
}

‎src/libunwind/libunwind.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ macro_rules! cfg_if {
1818
use libc::{c_int, c_void, uintptr_t};
1919

2020
#[repr(C)]
21-
#[derive(Copy, Clone, PartialEq)]
21+
#[derive(Debug, Copy, Clone, PartialEq)]
2222
pub enum _Unwind_Reason_Code {
2323
_URC_NO_REASON = 0,
2424
_URC_FOREIGN_EXCEPTION_CAUGHT = 1,

‎src/test/run-pass/backtrace-debuginfo.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,12 +141,12 @@ fn run_test(me: &str) {
141141
use std::process::Command;
142142

143143
let mut template = Command::new(me);
144-
template.env("RUST_BACKTRACE", "1");
144+
template.env("RUST_BACKTRACE", "full");
145145

146146
let mut i = 0;
147147
loop {
148148
let out = Command::new(me)
149-
.env("RUST_BACKTRACE", "1")
149+
.env("RUST_BACKTRACE", "full")
150150
.arg(i.to_string()).output().unwrap();
151151
let output = str::from_utf8(&out.stdout).unwrap();
152152
let error = str::from_utf8(&out.stderr).unwrap();

‎src/test/run-pass/backtrace.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ fn template(me: &str) -> Command {
4747
}
4848

4949
fn expected(fn_name: &str) -> String {
50-
format!(" - backtrace::{}", fn_name)
50+
format!(" backtrace::{}", fn_name)
5151
}
5252

5353
fn runtest(me: &str) {
@@ -59,6 +59,53 @@ fn runtest(me: &str) {
5959
assert!(s.contains("stack backtrace") && s.contains(&expected("foo")),
6060
"bad output: {}", s);
6161

62+
// Make sure than the short version cleans the backtrace.
63+
let p = template(me).arg("fail").env("RUST_BACKTRACE", "1").spawn().unwrap();
64+
let out = p.wait_with_output().unwrap();
65+
assert!(!out.status.success());
66+
let s = str::from_utf8(&out.stderr).unwrap();
67+
let removed_symbols = &[
68+
"std::sys::imp::backtrace",
69+
"std::sys_common::backtrace",
70+
"std::panicking",
71+
"core::panicking",
72+
"rust_begin_unwind",
73+
"code::result::unwrap_failed",
74+
"std::panicking::try::do_call",
75+
"__rust_maybe_catch_panic",
76+
"__libc_start_main",
77+
"__rust_try",
78+
"_start",
79+
];
80+
for symbol in removed_symbols {
81+
assert!(!s.contains(symbol),
82+
"{} should be removed from the backtrace\n{}",
83+
symbol, s);
84+
}
85+
assert!(s.contains(" 0:"), "the frame number should start at 0");
86+
87+
// Only on linux for _start and __libc_start_main
88+
#[cfg(target_os="linux")]
89+
{
90+
// Make sure than the short version cleans the backtrace.
91+
let p = template(me).arg("fail").env("RUST_BACKTRACE", "full").spawn().unwrap();
92+
let out = p.wait_with_output().unwrap();
93+
assert!(!out.status.success());
94+
let s = str::from_utf8(&out.stderr).unwrap();
95+
let should_be_present = &[
96+
"std::panicking",
97+
"__rust_maybe_catch_panic",
98+
"__libc_start_main",
99+
"_start",
100+
];
101+
for symbol in should_be_present {
102+
// May give false positive due to inlining.
103+
assert!(s.contains(symbol),
104+
"the full version of the backtrace should contain {}",
105+
symbol);
106+
}
107+
}
108+
62109
// Make sure the stack trace is *not* printed
63110
// (Remove RUST_BACKTRACE from our own environment, in case developer
64111
// is running `make check` with it on.)

0 commit comments

Comments
 (0)
Please sign in to comment.