Improve backtrace formating while panicking.
- `RUST_BACKTRACE=full` prints all the informations (old behaviour) - `RUST_BACKTRACE=(0|no)` disables the backtrace. - `RUST_BACKTRACE=<everything else>` (including `1`) shows a simplified backtrace, without the function addresses and with cleaned filenames and symbols. Also removes some unneded frames at the beginning and the end. Fixes #37783. PR is #38165.
This commit is contained in:
@@ -10,9 +10,14 @@
|
||||
|
||||
use libc;
|
||||
use io;
|
||||
use sys_common::backtrace::output;
|
||||
use sys_common::backtrace::Frame;
|
||||
|
||||
pub use sys_common::gnu::libbacktrace::*;
|
||||
pub struct BacktraceContext;
|
||||
|
||||
#[inline(never)]
|
||||
pub fn write(w: &mut io::Write) -> io::Result<()> {
|
||||
output(w, 0, 0 as *mut libc::c_void, None)
|
||||
pub fn unwind_backtrace(frames: &mut [Frame])
|
||||
-> io::Result<(usize, BacktraceContext)>
|
||||
{
|
||||
Ok((0, BacktraceContext))
|
||||
}
|
||||
|
||||
@@ -83,7 +83,8 @@
|
||||
/// to symbols. This is a bit of a hokey implementation as-is, but it works for
|
||||
/// all unix platforms we support right now, so it at least gets the job done.
|
||||
|
||||
pub use self::tracing::write;
|
||||
pub use self::tracing::unwind_backtrace;
|
||||
pub use self::printing::{foreach_symbol_fileline, resolve_symname};
|
||||
|
||||
// tracing impls:
|
||||
mod tracing;
|
||||
@@ -100,3 +101,5 @@ pub mod gnu {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BacktraceContext;
|
||||
|
||||
@@ -9,33 +9,45 @@
|
||||
// except according to those terms.
|
||||
|
||||
use io;
|
||||
use io::prelude::*;
|
||||
use intrinsics;
|
||||
use ffi::CStr;
|
||||
use libc;
|
||||
use sys::backtrace::BacktraceContext;
|
||||
use sys_common::backtrace::Frame;
|
||||
|
||||
pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void,
|
||||
_symaddr: *mut libc::c_void) -> io::Result<()> {
|
||||
use sys_common::backtrace::{output};
|
||||
use intrinsics;
|
||||
use ffi::CStr;
|
||||
|
||||
#[repr(C)]
|
||||
struct Dl_info {
|
||||
dli_fname: *const libc::c_char,
|
||||
dli_fbase: *mut libc::c_void,
|
||||
dli_sname: *const libc::c_char,
|
||||
dli_saddr: *mut libc::c_void,
|
||||
}
|
||||
extern {
|
||||
fn dladdr(addr: *const libc::c_void,
|
||||
info: *mut Dl_info) -> libc::c_int;
|
||||
}
|
||||
|
||||
let mut info: Dl_info = unsafe { intrinsics::init() };
|
||||
if unsafe { dladdr(addr, &mut info) == 0 } {
|
||||
output(w, idx,addr, None)
|
||||
} else {
|
||||
output(w, idx, addr, Some(unsafe {
|
||||
CStr::from_ptr(info.dli_sname).to_bytes()
|
||||
}))
|
||||
pub fn resolve_symname<F>(frame: Frame,
|
||||
callback: F,
|
||||
_: &BacktraceContext) -> io::Result<()>
|
||||
where F: FnOnce(Option<&str>) -> io::Result<()>
|
||||
{
|
||||
unsafe {
|
||||
let mut info: Dl_info = intrinsics::init();
|
||||
let symname = if dladdr(frame.exact_position, &mut info) == 0 {
|
||||
None
|
||||
} else {
|
||||
CStr::from_ptr(info.dli_sname).to_str().ok()
|
||||
};
|
||||
callback(symname)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn foreach_symbol_fileline<F>(_symbol_addr: Frame,
|
||||
_f: F,
|
||||
_: &BacktraceContext) -> io::Result<bool>
|
||||
where F: FnMut(&[u8], libc::c_int) -> io::Result<()>
|
||||
{
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct Dl_info {
|
||||
dli_fname: *const libc::c_char,
|
||||
dli_fbase: *mut libc::c_void,
|
||||
dli_sname: *const libc::c_char,
|
||||
dli_saddr: *mut libc::c_void,
|
||||
}
|
||||
|
||||
extern {
|
||||
fn dladdr(addr: *const libc::c_void,
|
||||
info: *mut Dl_info) -> libc::c_int;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
pub use self::imp::print;
|
||||
pub use self::imp::{foreach_symbol_fileline, resolve_symname};
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios",
|
||||
target_os = "emscripten"))]
|
||||
@@ -17,5 +17,6 @@ mod imp;
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "ios",
|
||||
target_os = "emscripten")))]
|
||||
#[path = "gnu.rs"]
|
||||
mod imp;
|
||||
mod imp {
|
||||
pub use sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname};
|
||||
}
|
||||
|
||||
@@ -18,39 +18,32 @@
|
||||
/// simple to use it should be used only on iOS devices as the only viable
|
||||
/// option.
|
||||
|
||||
use io::prelude::*;
|
||||
use io;
|
||||
use libc;
|
||||
use mem;
|
||||
use sys::mutex::Mutex;
|
||||
use sys::backtrace::BacktraceContext;
|
||||
use sys_common::backtrace::Frame;
|
||||
|
||||
use super::super::printing::print;
|
||||
|
||||
#[inline(never)]
|
||||
pub fn write(w: &mut Write) -> io::Result<()> {
|
||||
extern {
|
||||
fn backtrace(buf: *mut *mut libc::c_void,
|
||||
sz: libc::c_int) -> libc::c_int;
|
||||
#[inline(never)] // if we know this is a function call, we can skip it when
|
||||
// tracing
|
||||
pub fn unwind_backtrace(frames: &mut [Frame])
|
||||
-> io::Result<(usize, BacktraceContext)>
|
||||
{
|
||||
const FRAME_LEN: usize = 100;
|
||||
assert!(FRAME_LEN >= frames.len());
|
||||
let mut raw_frames = [::std::ptr::null_mut(); FRAME_LEN];
|
||||
let nb_frames = unsafe {
|
||||
backtrace(raw_frames.as_mut_ptr(), raw_frames.len() as libc::c_int)
|
||||
} as usize;
|
||||
for (from, to) in raw_frames.iter().zip(frames.iter_mut()).take(nb_frames) {
|
||||
*to = Frame {
|
||||
exact_position: *from,
|
||||
symbol_addr: *from,
|
||||
};
|
||||
}
|
||||
|
||||
// while it doesn't requires lock for work as everything is
|
||||
// local, it still displays much nicer backtraces when a
|
||||
// couple of threads panic simultaneously
|
||||
static LOCK: Mutex = Mutex::new();
|
||||
unsafe {
|
||||
LOCK.lock();
|
||||
|
||||
writeln!(w, "stack backtrace:")?;
|
||||
// 100 lines should be enough
|
||||
const SIZE: usize = 100;
|
||||
let mut buf: [*mut libc::c_void; SIZE] = mem::zeroed();
|
||||
let cnt = backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as usize;
|
||||
|
||||
// skipping the first one as it is write itself
|
||||
for i in 1..cnt {
|
||||
print(w, i as isize, buf[i], buf[i])?
|
||||
}
|
||||
LOCK.unlock();
|
||||
}
|
||||
Ok(())
|
||||
Ok((nb_frames as usize, BacktraceContext))
|
||||
}
|
||||
|
||||
extern {
|
||||
fn backtrace(buf: *mut *mut libc::c_void, sz: libc::c_int) -> libc::c_int;
|
||||
}
|
||||
|
||||
@@ -8,102 +8,97 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use error::Error;
|
||||
use io;
|
||||
use io::prelude::*;
|
||||
use libc;
|
||||
use mem;
|
||||
use sys_common::mutex::Mutex;
|
||||
use sys::backtrace::BacktraceContext;
|
||||
use sys_common::backtrace::Frame;
|
||||
|
||||
use super::super::printing::print;
|
||||
use unwind as uw;
|
||||
|
||||
struct Context<'a> {
|
||||
idx: usize,
|
||||
frames: &'a mut [Frame],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct UnwindError(uw::_Unwind_Reason_Code);
|
||||
|
||||
impl Error for UnwindError {
|
||||
fn description(&self) -> &'static str {
|
||||
"unexpected return value while unwinding"
|
||||
}
|
||||
}
|
||||
|
||||
impl ::fmt::Display for UnwindError {
|
||||
fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
|
||||
write!(f, "{}: {:?}", self.description(), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)] // if we know this is a function call, we can skip it when
|
||||
// tracing
|
||||
pub fn write(w: &mut Write) -> io::Result<()> {
|
||||
struct Context<'a> {
|
||||
idx: isize,
|
||||
writer: &'a mut (Write+'a),
|
||||
last_error: Option<io::Error>,
|
||||
}
|
||||
|
||||
// When using libbacktrace, we use some necessary global state, so we
|
||||
// need to prevent more than one thread from entering this block. This
|
||||
// is semi-reasonable in terms of printing anyway, and we know that all
|
||||
// I/O done here is blocking I/O, not green I/O, so we don't have to
|
||||
// worry about this being a native vs green mutex.
|
||||
static LOCK: Mutex = Mutex::new();
|
||||
unsafe {
|
||||
LOCK.lock();
|
||||
|
||||
writeln!(w, "stack backtrace:")?;
|
||||
|
||||
let mut cx = Context { writer: w, last_error: None, idx: 0 };
|
||||
let ret = match {
|
||||
uw::_Unwind_Backtrace(trace_fn,
|
||||
&mut cx as *mut Context as *mut libc::c_void)
|
||||
} {
|
||||
uw::_URC_NO_REASON => {
|
||||
match cx.last_error {
|
||||
Some(err) => Err(err),
|
||||
None => Ok(())
|
||||
}
|
||||
}
|
||||
_ => Ok(()),
|
||||
};
|
||||
LOCK.unlock();
|
||||
return ret
|
||||
}
|
||||
|
||||
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 mut ip_before_insn = 0;
|
||||
let mut ip = unsafe {
|
||||
uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void
|
||||
};
|
||||
if !ip.is_null() && 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 _;
|
||||
pub fn unwind_backtrace(frames: &mut [Frame])
|
||||
-> io::Result<(usize, BacktraceContext)>
|
||||
{
|
||||
let mut cx = Context {
|
||||
idx: 0,
|
||||
frames: frames,
|
||||
};
|
||||
let result_unwind = unsafe {
|
||||
uw::_Unwind_Backtrace(trace_fn,
|
||||
&mut cx as *mut Context
|
||||
as *mut libc::c_void)
|
||||
};
|
||||
// See libunwind:src/unwind/Backtrace.c for the return values.
|
||||
// No, there is no doc.
|
||||
match result_unwind {
|
||||
uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR => {
|
||||
Ok((cx.idx, BacktraceContext))
|
||||
}
|
||||
|
||||
// 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
|
||||
// slightly more accurate stack trace in the process.
|
||||
//
|
||||
// This is often because panic involves the last instruction of a
|
||||
// function being "call std::rt::begin_unwind", with no ret
|
||||
// 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 symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
|
||||
ip
|
||||
} else {
|
||||
unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
|
||||
};
|
||||
|
||||
// Don't print out the first few frames (they're not user frames)
|
||||
cx.idx += 1;
|
||||
if cx.idx <= 0 { return uw::_URC_NO_REASON }
|
||||
// Don't print ginormous backtraces
|
||||
if cx.idx > 100 {
|
||||
match write!(cx.writer, " ... <frames omitted>\n") {
|
||||
Ok(()) => {}
|
||||
Err(e) => { cx.last_error = Some(e); }
|
||||
}
|
||||
return uw::_URC_FAILURE
|
||||
_ => {
|
||||
Err(io::Error::new(io::ErrorKind::Other,
|
||||
UnwindError(result_unwind)))
|
||||
}
|
||||
|
||||
// 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, symaddr) {
|
||||
Ok(()) => {}
|
||||
Err(e) => { cx.last_error = Some(e); }
|
||||
}
|
||||
|
||||
// keep going
|
||||
uw::_URC_NO_REASON
|
||||
}
|
||||
}
|
||||
|
||||
extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
|
||||
arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
|
||||
let cx = unsafe { &mut *(arg as *mut Context) };
|
||||
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.is_null() && 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
|
||||
// slightly more accurate stack trace in the process.
|
||||
//
|
||||
// This is often because panic involves the last instruction of a
|
||||
// function being "call std::rt::begin_unwind", with no ret
|
||||
// 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 symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
|
||||
ip
|
||||
} else {
|
||||
unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
|
||||
};
|
||||
|
||||
if cx.idx < cx.frames.len() {
|
||||
cx.frames[cx.idx] = Frame {
|
||||
symbol_addr: symaddr,
|
||||
exact_position: ip,
|
||||
};
|
||||
cx.idx += 1;
|
||||
}
|
||||
|
||||
uw::_URC_NO_REASON
|
||||
}
|
||||
|
||||
@@ -24,37 +24,87 @@
|
||||
|
||||
#![allow(deprecated)] // dynamic_lib
|
||||
|
||||
use io::prelude::*;
|
||||
|
||||
use io;
|
||||
use libc::c_void;
|
||||
use mem;
|
||||
use ptr;
|
||||
use sys::c;
|
||||
use sys::dynamic_lib::DynamicLibrary;
|
||||
use sys::mutex::Mutex;
|
||||
use sys_common::backtrace::Frame;
|
||||
|
||||
macro_rules! sym {
|
||||
($lib:expr, $e:expr, $t:ident) => (
|
||||
match $lib.symbol($e) {
|
||||
Ok(f) => $crate::mem::transmute::<usize, $t>(f),
|
||||
Err(..) => return Ok(())
|
||||
}
|
||||
$lib.symbol($e).map(|f| unsafe {
|
||||
$crate::mem::transmute::<usize, $t>(f)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(target_env = "msvc")]
|
||||
#[path = "printing/msvc.rs"]
|
||||
mod printing;
|
||||
|
||||
#[cfg(target_env = "gnu")]
|
||||
#[path = "printing/gnu.rs"]
|
||||
mod printing;
|
||||
|
||||
#[cfg(target_env = "gnu")]
|
||||
#[path = "backtrace_gnu.rs"]
|
||||
pub mod gnu;
|
||||
|
||||
pub use self::printing::{resolve_symname, foreach_symbol_fileline};
|
||||
|
||||
pub fn unwind_backtrace(frames: &mut [Frame])
|
||||
-> io::Result<(usize, BacktraceContext)>
|
||||
{
|
||||
let dbghelp = DynamicLibrary::open("dbghelp.dll")?;
|
||||
|
||||
// Fetch the symbols necessary from dbghelp.dll
|
||||
let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn)?;
|
||||
let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn)?;
|
||||
let StackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn)?;
|
||||
|
||||
// Allocate necessary structures for doing the stack walk
|
||||
let process = unsafe { c::GetCurrentProcess() };
|
||||
let thread = unsafe { c::GetCurrentThread() };
|
||||
let mut context: c::CONTEXT = unsafe { mem::zeroed() };
|
||||
unsafe { c::RtlCaptureContext(&mut context) };
|
||||
let mut frame: c::STACKFRAME64 = unsafe { mem::zeroed() };
|
||||
let image = init_frame(&mut frame, &context);
|
||||
|
||||
let backtrace_context = BacktraceContext {
|
||||
handle: process,
|
||||
SymCleanup: SymCleanup,
|
||||
dbghelp: dbghelp,
|
||||
};
|
||||
|
||||
// Initialize this process's symbols
|
||||
let ret = unsafe { SymInitialize(process, ptr::null_mut(), c::TRUE) };
|
||||
if ret != c::TRUE {
|
||||
return Ok((0, backtrace_context))
|
||||
}
|
||||
|
||||
// And now that we're done with all the setup, do the stack walking!
|
||||
// Start from -1 to avoid printing this stack frame, which will
|
||||
// always be exactly the same.
|
||||
let mut i = 0;
|
||||
unsafe {
|
||||
while i < frames.len() &&
|
||||
StackWalk64(image, process, thread, &mut frame, &mut context,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut()) == c::TRUE
|
||||
{
|
||||
let addr = frame.AddrPC.Offset;
|
||||
if addr == frame.AddrReturn.Offset || addr == 0 ||
|
||||
frame.AddrReturn.Offset == 0 { break }
|
||||
|
||||
frames[i] = Frame {
|
||||
symbol_addr: (addr - 1) as *const c_void,
|
||||
exact_position: (addr - 1) as *const c_void,
|
||||
};
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((i, backtrace_context))
|
||||
}
|
||||
|
||||
type SymInitializeFn =
|
||||
unsafe extern "system" fn(c::HANDLE, *mut c_void,
|
||||
c::BOOL) -> c::BOOL;
|
||||
@@ -68,8 +118,8 @@ type StackWalk64Fn =
|
||||
*mut c_void, *mut c_void) -> c::BOOL;
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
pub fn init_frame(frame: &mut c::STACKFRAME64,
|
||||
ctx: &c::CONTEXT) -> c::DWORD {
|
||||
fn init_frame(frame: &mut c::STACKFRAME64,
|
||||
ctx: &c::CONTEXT) -> c::DWORD {
|
||||
frame.AddrPC.Offset = ctx.Eip as u64;
|
||||
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
|
||||
frame.AddrStack.Offset = ctx.Esp as u64;
|
||||
@@ -80,8 +130,8 @@ pub fn init_frame(frame: &mut c::STACKFRAME64,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub fn init_frame(frame: &mut c::STACKFRAME64,
|
||||
ctx: &c::CONTEXT) -> c::DWORD {
|
||||
fn init_frame(frame: &mut c::STACKFRAME64,
|
||||
ctx: &c::CONTEXT) -> c::DWORD {
|
||||
frame.AddrPC.Offset = ctx.Rip as u64;
|
||||
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
|
||||
frame.AddrStack.Offset = ctx.Rsp as u64;
|
||||
@@ -91,73 +141,16 @@ pub fn init_frame(frame: &mut c::STACKFRAME64,
|
||||
c::IMAGE_FILE_MACHINE_AMD64
|
||||
}
|
||||
|
||||
struct Cleanup {
|
||||
pub struct BacktraceContext {
|
||||
handle: c::HANDLE,
|
||||
SymCleanup: SymCleanupFn,
|
||||
// Only used in printing for msvc and not gnu
|
||||
#[allow(dead_code)]
|
||||
dbghelp: DynamicLibrary,
|
||||
}
|
||||
|
||||
impl Drop for Cleanup {
|
||||
impl Drop for BacktraceContext {
|
||||
fn drop(&mut self) {
|
||||
unsafe { (self.SymCleanup)(self.handle); }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(w: &mut Write) -> io::Result<()> {
|
||||
// According to windows documentation, all dbghelp functions are
|
||||
// single-threaded.
|
||||
static LOCK: Mutex = Mutex::new();
|
||||
unsafe {
|
||||
LOCK.lock();
|
||||
let res = _write(w);
|
||||
LOCK.unlock();
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn _write(w: &mut Write) -> io::Result<()> {
|
||||
let dbghelp = match DynamicLibrary::open("dbghelp.dll") {
|
||||
Ok(lib) => lib,
|
||||
Err(..) => return Ok(()),
|
||||
};
|
||||
|
||||
// Fetch the symbols necessary from dbghelp.dll
|
||||
let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn);
|
||||
let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn);
|
||||
let StackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn);
|
||||
|
||||
// Allocate necessary structures for doing the stack walk
|
||||
let process = c::GetCurrentProcess();
|
||||
let thread = c::GetCurrentThread();
|
||||
let mut context: c::CONTEXT = mem::zeroed();
|
||||
c::RtlCaptureContext(&mut context);
|
||||
let mut frame: c::STACKFRAME64 = mem::zeroed();
|
||||
let image = init_frame(&mut frame, &context);
|
||||
|
||||
// Initialize this process's symbols
|
||||
let ret = SymInitialize(process, ptr::null_mut(), c::TRUE);
|
||||
if ret != c::TRUE { return Ok(()) }
|
||||
let _c = Cleanup { handle: process, SymCleanup: SymCleanup };
|
||||
|
||||
// And now that we're done with all the setup, do the stack walking!
|
||||
// Start from -1 to avoid printing this stack frame, which will
|
||||
// always be exactly the same.
|
||||
let mut i = -1;
|
||||
write!(w, "stack backtrace:\n")?;
|
||||
while StackWalk64(image, process, thread, &mut frame, &mut context,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut()) == c::TRUE {
|
||||
let addr = frame.AddrPC.Offset;
|
||||
if addr == frame.AddrReturn.Offset || addr == 0 ||
|
||||
frame.AddrReturn.Offset == 0 { break }
|
||||
|
||||
i += 1;
|
||||
|
||||
if i >= 0 {
|
||||
printing::print(w, i, addr - 1, process, &dbghelp)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -8,4 +8,13 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
pub use sys_common::gnu::libbacktrace::print;
|
||||
#[cfg(target_env = "msvc")]
|
||||
#[path = "msvc.rs"]
|
||||
mod printing;
|
||||
|
||||
#[cfg(target_env = "gnu")]
|
||||
mod printing {
|
||||
pub use sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname};
|
||||
}
|
||||
|
||||
pub use self::printing::{foreach_symbol_fileline, resolve_symname};
|
||||
83
src/libstd/sys/windows/backtrace/printing/msvc.rs
Normal file
83
src/libstd/sys/windows/backtrace/printing/msvc.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
use ffi::CStr;
|
||||
use io;
|
||||
use libc::{c_ulong, c_int, c_char};
|
||||
use mem;
|
||||
use sys::c;
|
||||
use sys::backtrace::BacktraceContext;
|
||||
use sys_common::backtrace::Frame;
|
||||
|
||||
type SymFromAddrFn =
|
||||
unsafe extern "system" fn(c::HANDLE, u64, *mut u64,
|
||||
*mut c::SYMBOL_INFO) -> c::BOOL;
|
||||
type SymGetLineFromAddr64Fn =
|
||||
unsafe extern "system" fn(c::HANDLE, u64, *mut u32,
|
||||
*mut c::IMAGEHLP_LINE64) -> c::BOOL;
|
||||
|
||||
/// Converts a pointer to symbol to its string value.
|
||||
pub fn resolve_symname<F>(frame: Frame,
|
||||
callback: F,
|
||||
context: &BacktraceContext) -> io::Result<()>
|
||||
where F: FnOnce(Option<&str>) -> io::Result<()>
|
||||
{
|
||||
let SymFromAddr = sym!(&context.dbghelp, "SymFromAddr", SymFromAddrFn)?;
|
||||
|
||||
unsafe {
|
||||
let mut info: c::SYMBOL_INFO = mem::zeroed();
|
||||
info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
|
||||
// the struct size in C. the value is different to
|
||||
// `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
|
||||
// due to struct alignment.
|
||||
info.SizeOfStruct = 88;
|
||||
|
||||
let mut displacement = 0u64;
|
||||
let ret = SymFromAddr(context.handle,
|
||||
frame.symbol_addr as u64,
|
||||
&mut displacement,
|
||||
&mut info);
|
||||
|
||||
let symname = if ret == c::TRUE {
|
||||
let ptr = info.Name.as_ptr() as *const c_char;
|
||||
CStr::from_ptr(ptr).to_str().ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
callback(symname)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn foreach_symbol_fileline<F>(frame: Frame,
|
||||
mut f: F,
|
||||
context: &BacktraceContext)
|
||||
-> io::Result<bool>
|
||||
where F: FnMut(&[u8], c_int) -> io::Result<()>
|
||||
{
|
||||
let SymGetLineFromAddr64 = sym!(&context.dbghelp,
|
||||
"SymGetLineFromAddr64",
|
||||
SymGetLineFromAddr64Fn)?;
|
||||
|
||||
unsafe {
|
||||
let mut line: c::IMAGEHLP_LINE64 = mem::zeroed();
|
||||
line.SizeOfStruct = ::mem::size_of::<c::IMAGEHLP_LINE64>() as u32;
|
||||
|
||||
let mut displacement = 0u32;
|
||||
let ret = SymGetLineFromAddr64(context.handle,
|
||||
frame.exact_position as u64,
|
||||
&mut displacement,
|
||||
&mut line);
|
||||
if ret == c::TRUE {
|
||||
let name = CStr::from_ptr(line.Filename).to_bytes();
|
||||
f(name, line.LineNumber as c_int)?;
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
use io::prelude::*;
|
||||
use io;
|
||||
use libc::c_void;
|
||||
use sys::c;
|
||||
use sys::dynamic_lib::DynamicLibrary;
|
||||
use sys_common::gnu::libbacktrace;
|
||||
|
||||
pub fn print(w: &mut Write,
|
||||
i: isize,
|
||||
addr: u64,
|
||||
_process: c::HANDLE,
|
||||
_dbghelp: &DynamicLibrary)
|
||||
-> io::Result<()> {
|
||||
let addr = addr as usize as *mut c_void;
|
||||
libbacktrace::print(w, i, addr, addr)
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
use ffi::CStr;
|
||||
use io::prelude::*;
|
||||
use io;
|
||||
use libc::{c_ulong, c_int, c_char, c_void};
|
||||
use mem;
|
||||
use sys::c;
|
||||
use sys::dynamic_lib::DynamicLibrary;
|
||||
use sys_common::backtrace::{output, output_fileline};
|
||||
|
||||
type SymFromAddrFn =
|
||||
unsafe extern "system" fn(c::HANDLE, u64, *mut u64,
|
||||
*mut c::SYMBOL_INFO) -> c::BOOL;
|
||||
type SymGetLineFromAddr64Fn =
|
||||
unsafe extern "system" fn(c::HANDLE, u64, *mut u32,
|
||||
*mut c::IMAGEHLP_LINE64) -> c::BOOL;
|
||||
|
||||
pub fn print(w: &mut Write,
|
||||
i: isize,
|
||||
addr: u64,
|
||||
process: c::HANDLE,
|
||||
dbghelp: &DynamicLibrary)
|
||||
-> io::Result<()> {
|
||||
unsafe {
|
||||
let SymFromAddr = sym!(dbghelp, "SymFromAddr", SymFromAddrFn);
|
||||
let SymGetLineFromAddr64 = sym!(dbghelp,
|
||||
"SymGetLineFromAddr64",
|
||||
SymGetLineFromAddr64Fn);
|
||||
|
||||
let mut info: c::SYMBOL_INFO = mem::zeroed();
|
||||
info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
|
||||
// the struct size in C. the value is different to
|
||||
// `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
|
||||
// due to struct alignment.
|
||||
info.SizeOfStruct = 88;
|
||||
|
||||
let mut displacement = 0u64;
|
||||
let ret = SymFromAddr(process, addr, &mut displacement, &mut info);
|
||||
|
||||
let name = if ret == c::TRUE {
|
||||
let ptr = info.Name.as_ptr() as *const c_char;
|
||||
Some(CStr::from_ptr(ptr).to_bytes())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
output(w, i, addr as usize as *mut c_void, name)?;
|
||||
|
||||
// Now find out the filename and line number
|
||||
let mut line: c::IMAGEHLP_LINE64 = mem::zeroed();
|
||||
line.SizeOfStruct = ::mem::size_of::<c::IMAGEHLP_LINE64>() as u32;
|
||||
|
||||
let mut displacement = 0u32;
|
||||
let ret = SymGetLineFromAddr64(process, addr, &mut displacement, &mut line);
|
||||
if ret == c::TRUE {
|
||||
output_fileline(w,
|
||||
CStr::from_ptr(line.Filename).to_bytes(),
|
||||
line.LineNumber as c_int,
|
||||
false)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user