about summary refs log tree commit diff
path: root/src/libstd/sys_common/gnu
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-02-27 17:21:37 +0000
committerbors <bors@rust-lang.org>2017-02-27 17:21:37 +0000
commit4be034e62270ca15c9fff173faef11939092f4e3 (patch)
tree10a0445320179f5150b115e6dda4bac00fde552a /src/libstd/sys_common/gnu
parent0ed75e1f47d099a17de22eb2c1d2b43ce89ea390 (diff)
parent6398b2078d92c583c4c7eb76312d474042a2c68f (diff)
downloadrust-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_common/gnu')
-rw-r--r--src/libstd/sys_common/gnu/libbacktrace.rs344
1 files changed, 181 insertions, 163 deletions
diff --git a/src/libstd/sys_common/gnu/libbacktrace.rs b/src/libstd/sys_common/gnu/libbacktrace.rs
index 0bdbeddb112..1ea5cca44c7 100644
--- a/src/libstd/sys_common/gnu/libbacktrace.rs
+++ b/src/libstd/sys_common/gnu/libbacktrace.rs
@@ -8,186 +8,204 @@
 // 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
 }