Skip to content

std: Include line numbers in backtraces. #22839

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 28, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 107 additions & 10 deletions src/libstd/sys/unix/backtrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ pub fn write(w: &mut Writer) -> IoResult<()> {

// 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, (), |_, _| ())
}
Expand Down Expand Up @@ -171,7 +171,16 @@ pub fn write(w: &mut Writer) -> IoResult<()> {
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 _;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm this may explain some of the weird backtraces I saw when I initially wrote all this!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was that why _Unwind_FindEnclosingFunction is used?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I remember correctly, I think so? Although I'd have to test both ways... It's been awhile :(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I was thinking about replacing that call with backtrace_pcinfo. There is a reason I haven't do so, though:

backtrace_pcinfo does provide a function information as well, but as far as I tested that function name and backtrace_syminfo-based name differ a bit for normal functions and the former is almost unusable for closures (fnfn? seriously?). I guess they go through a different strategy.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, well I'm pretty much game for whatever works right now :)


// 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
Expand All @@ -182,7 +191,7 @@ pub fn write(w: &mut Writer) -> IoResult<()> {
// 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) }
Expand All @@ -203,7 +212,7 @@ pub fn write(w: &mut Writer) -> IoResult<()> {
// 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); }
}
Expand All @@ -214,7 +223,8 @@ pub fn write(w: &mut Writer) -> IoResult<()> {
}

#[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 {
Expand All @@ -239,7 +249,8 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
}

#[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;

Expand All @@ -252,6 +263,12 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
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,
Expand All @@ -272,12 +289,19 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
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
Expand All @@ -290,6 +314,25 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
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
Expand Down Expand Up @@ -358,15 +401,42 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
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.
Expand All @@ -380,6 +450,18 @@ fn output(w: &mut Writer, idx: int, addr: *mut libc::c_void,
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
Expand Down Expand Up @@ -420,9 +502,12 @@ mod uw {
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"))))]
Expand Down Expand Up @@ -478,6 +563,18 @@ mod uw {
(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",
Expand Down
22 changes: 22 additions & 0 deletions src/test/run-pass/backtrace-debuginfo-aux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2015 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.

// ignore-test: not a test, used by backtrace-debuginfo.rs to test file!()

#[inline(never)]
pub fn callback<F>(f: F) where F: FnOnce((&'static str, u32)) {
f((file!(), line!()))
}

#[inline(always)]
pub fn callback_inlined<F>(f: F) where F: FnOnce((&'static str, u32)) {
f((file!(), line!()))
}

Loading