// skipping the first one as it is write itself
let iter = (1..cnt).map(|i| {
- print(w, i as int, buf[i])
+ print(w, i as int, buf[i], buf[i])
});
result::fold(iter, (), |_, _| ())
}
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 ip = unsafe { uw::_Unwind_GetIP(ctx) as *mut libc::c_void };
+ 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_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 _;
+ }
+
// 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
// 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 ip = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
+ let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
ip
} else {
unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
// Once we hit an error, stop trying to print more frames
if cx.last_error.is_some() { return uw::_URC_FAILURE }
- match print(cx.writer, cx.idx, ip) {
+ match print(cx.writer, cx.idx, ip, symaddr) {
Ok(()) => {}
Err(e) => { cx.last_error = Some(e); }
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
-fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
+fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void,
+ _symaddr: *mut libc::c_void) -> IoResult<()> {
use intrinsics;
#[repr(C)]
struct Dl_info {
}
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
-fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
+fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void,
+ symaddr: *mut libc::c_void) -> IoResult<()> {
use env;
use ptr;
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,
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
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
let mut data = ptr::null();
let data_addr = &mut data as *mut *const libc::c_char;
let ret = unsafe {
- backtrace_syminfo(state, addr as libc::uintptr_t,
+ 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)
+ try!(output(w, idx, addr, None));
} else {
- output(w, idx, addr, Some(unsafe { CStr::from_ptr(data).to_bytes() }))
+ try!(output(w, idx, addr, Some(unsafe { CStr::from_ptr(data).to_bytes() })));
+ }
+
+ // 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 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,
+ fileline_addr as *mut libc::c_void)
+ };
+ fileline_count = FILELINE_SIZE - fileline_win.len();
}
+ if ret == 0 {
+ for (i, &(file, line)) in fileline_buf[..fileline_count].iter().enumerate() {
+ if file.is_null() { continue; } // just to be sure
+ let file = unsafe { CStr::from_ptr(file).to_bytes() };
+ try!(output_fileline(w, file, line, i == FILELINE_SIZE - 1));
+ }
+ }
+
+ Ok(())
}
// Finally, after all that work above, we can emit a symbol.
w.write_all(&['\n' as u8])
}
+#[allow(dead_code)]
+fn output_fileline(w: &mut Writer, file: &[u8], line: libc::c_int,
+ more: bool) -> IoResult<()> {
+ let file = str::from_utf8(file).ok().unwrap_or("<unknown>");
+ // prior line: " ##: {:2$} - func"
+ try!(write!(w, " {:3$}at {}:{}", "", file, line, HEX_WIDTH));
+ if more {
+ try!(write!(w, " <... and possibly more>"));
+ }
+ w.write_all(&['\n' as u8])
+}
+
/// Unwind library interface used for backtraces
///
/// Note that dead code is allowed as here are just bindings
trace_argument: *mut libc::c_void)
-> _Unwind_Reason_Code;
+ // available since GCC 4.2.0, should be fine for our purpose
#[cfg(all(not(all(target_os = "android", target_arch = "arm")),
not(all(target_os = "linux", target_arch = "arm"))))]
- pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
+ pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context,
+ ip_before_insn: *mut libc::c_int)
+ -> libc::uintptr_t;
#[cfg(all(not(target_os = "android"),
not(all(target_os = "linux", target_arch = "arm"))))]
(val & !1) as libc::uintptr_t
}
+ // This function doesn't exist on Android or ARM/Linux, so make it same
+ // to _Unwind_GetIP
+ #[cfg(any(target_os = "android",
+ all(target_os = "linux", target_arch = "arm")))]
+ pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context,
+ ip_before_insn: *mut libc::c_int)
+ -> libc::uintptr_t
+ {
+ *ip_before_insn = 0;
+ _Unwind_GetIP(ctx)
+ }
+
// This function also doesn't exist on Android or ARM/Linux, so make it
// a no-op
#[cfg(any(target_os = "android",