From d4fc3ec208186f0a9de8d05a6802c95b07e058fd Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Wed, 26 Aug 2015 01:44:55 +0100 Subject: Add line numbers to windows-gnu backtraces Fix formatting Remove unused imports Refactor Fix msvc build Fix line lengths Formatting Enable backtrace tests Fix using directive on mac pwd info Work-around buildbot PWD bug, and fix libbacktrace configuration Use alternative to `env -u` which is not supported on bitrig Disable tests on 32-bit windows gnu --- src/libstd/sys/common/backtrace.rs | 30 ++- src/libstd/sys/common/gnu/libbacktrace.rs | 206 +++++++++++++++++++++ src/libstd/sys/common/gnu/mod.rs | 15 ++ src/libstd/sys/common/mod.rs | 4 + src/libstd/sys/unix/backtrace/mod.rs | 29 --- src/libstd/sys/unix/backtrace/printing/dladdr.rs | 2 +- src/libstd/sys/unix/backtrace/printing/gnu.rs | 11 ++ .../sys/unix/backtrace/printing/libbacktrace.rs | 206 --------------------- src/libstd/sys/unix/backtrace/printing/mod.rs | 4 +- src/libstd/sys/windows/backtrace.rs | 63 +++---- src/libstd/sys/windows/printing/gnu.rs | 22 +++ src/libstd/sys/windows/printing/msvc.rs | 42 +++++ 12 files changed, 356 insertions(+), 278 deletions(-) create mode 100644 src/libstd/sys/common/gnu/libbacktrace.rs create mode 100644 src/libstd/sys/common/gnu/mod.rs create mode 100644 src/libstd/sys/unix/backtrace/printing/gnu.rs delete mode 100644 src/libstd/sys/unix/backtrace/printing/libbacktrace.rs create mode 100644 src/libstd/sys/windows/printing/gnu.rs create mode 100644 src/libstd/sys/windows/printing/msvc.rs (limited to 'src/libstd') diff --git a/src/libstd/sys/common/backtrace.rs b/src/libstd/sys/common/backtrace.rs index 7f56afc9e1f..7845714e7f6 100644 --- a/src/libstd/sys/common/backtrace.rs +++ b/src/libstd/sys/common/backtrace.rs @@ -8,9 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use io::prelude::*; - use io; +use io::prelude::*; +use str; +use libc; #[cfg(target_pointer_width = "64")] pub const HEX_WIDTH: usize = 18; @@ -18,6 +19,31 @@ pub const HEX_WIDTH: usize = 18; #[cfg(target_pointer_width = "32")] pub const HEX_WIDTH: usize = 10; + +// These output functions should now be used everywhere to ensure consistency. +pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void, + s: Option<&[u8]>) -> io::Result<()> { + try!(write!(w, " {:2}: {:2$?} - ", idx, addr, HEX_WIDTH)); + match s.and_then(|s| str::from_utf8(s).ok()) { + Some(string) => try!(demangle(w, string)), + None => try!(write!(w, "")), + } + w.write_all(&['\n' as u8]) +} + +#[allow(dead_code)] +pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int, + more: bool) -> io::Result<()> { + let file = str::from_utf8(file).unwrap_or(""); + // 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]) +} + + // All rust symbols are in theory lists of "::"-separated identifiers. Some // assemblers, however, can't handle these characters in symbol names. To get // around this, we use C++-style mangling. The mangling method is: diff --git a/src/libstd/sys/common/gnu/libbacktrace.rs b/src/libstd/sys/common/gnu/libbacktrace.rs new file mode 100644 index 00000000000..7a2ca0a9f09 --- /dev/null +++ b/src/libstd/sys/common/gnu/libbacktrace.rs @@ -0,0 +1,206 @@ +// Copyright 2014-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 or the MIT license +// , at your +// 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 env; + use ffi::CStr; + 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 {} + #[link(name = "backtrace", kind = "static")] + #[cfg(not(test))] + 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..]); } + } + } + + 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). + // + // An additionally oddity in this function is that we initialize the + // filename via self_exe_name() to pass to libbacktrace. It turns out + // that on Linux libbacktrace seamlessly gets the filename of the + // current executable, but this fails on freebsd. by always providing + // it, we make sure that libbacktrace never has a reason to not look up + // the symbols. The libbacktrace API also states that the filename must + // be in "permanent memory", so we copy it to a static and then use the + // static as the pointer. + // + // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't + // tested if this is required or not. + unsafe fn init_state() -> *mut backtrace_state { + static mut STATE: *mut backtrace_state = ptr::null_mut(); + static mut LAST_FILENAME: [libc::c_char; 256] = [0; 256]; + if !STATE.is_null() { return STATE } + let selfname = if cfg!(target_os = "freebsd") || + cfg!(target_os = "dragonfly") || + cfg!(target_os = "bitrig") || + cfg!(target_os = "openbsd") || + cfg!(target_os = "windows") { + env::current_exe().ok() + } else { + None + }; + let filename = match selfname.as_ref().and_then(|s| s.as_os_str().to_bytes()) { + Some(path) => { + let bytes = path; + if bytes.len() < LAST_FILENAME.len() { + let i = bytes.iter(); + for (slot, val) in LAST_FILENAME.iter_mut().zip(i) { + *slot = *val as libc::c_char; + } + LAST_FILENAME.as_ptr() + } else { + ptr::null() + } + } + None => ptr::null(), + }; + STATE = backtrace_create_state(filename, 0, error_cb, + ptr::null_mut()); + return 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() { + try!(output(w, idx, addr, None)); + } else { + 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(()) +} diff --git a/src/libstd/sys/common/gnu/mod.rs b/src/libstd/sys/common/gnu/mod.rs new file mode 100644 index 00000000000..3a8cf2d8425 --- /dev/null +++ b/src/libstd/sys/common/gnu/mod.rs @@ -0,0 +1,15 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(missing_docs)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +pub mod libbacktrace; diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index aca1f42c529..b8074235fb2 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -23,6 +23,10 @@ pub mod thread_info; pub mod thread_local; pub mod wtf8; +#[cfg(any(all(unix, not(any(target_os = "macos", target_os = "ios"))), + all(windows, target_env = "gnu")))] +pub mod gnu; + // common error constructors /// A trait for viewing representations from std types diff --git a/src/libstd/sys/unix/backtrace/mod.rs b/src/libstd/sys/unix/backtrace/mod.rs index 79833c1cacb..d7c05e513f6 100644 --- a/src/libstd/sys/unix/backtrace/mod.rs +++ b/src/libstd/sys/unix/backtrace/mod.rs @@ -85,36 +85,7 @@ pub use self::tracing::write; -use io; -use io::prelude::*; -use libc; -use str; - -use sys_common::backtrace::{demangle, HEX_WIDTH}; - // tracing impls: mod tracing; // symbol resolvers: mod printing; - -pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void, - s: Option<&[u8]>) -> io::Result<()> { - try!(write!(w, " {:2}: {:2$?} - ", idx, addr, HEX_WIDTH)); - match s.and_then(|s| str::from_utf8(s).ok()) { - Some(string) => try!(demangle(w, string)), - None => try!(write!(w, "")), - } - w.write_all(&['\n' as u8]) -} - -#[allow(dead_code)] -pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int, - more: bool) -> io::Result<()> { - let file = str::from_utf8(file).unwrap_or(""); - // 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]) -} diff --git a/src/libstd/sys/unix/backtrace/printing/dladdr.rs b/src/libstd/sys/unix/backtrace/printing/dladdr.rs index 2bca56921d5..d9b759dc673 100644 --- a/src/libstd/sys/unix/backtrace/printing/dladdr.rs +++ b/src/libstd/sys/unix/backtrace/printing/dladdr.rs @@ -14,7 +14,7 @@ use libc; pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void, _symaddr: *mut libc::c_void) -> io::Result<()> { - use sys::backtrace::{output}; + use sys_common::backtrace::{output}; use intrinsics; use ffi::CStr; diff --git a/src/libstd/sys/unix/backtrace/printing/gnu.rs b/src/libstd/sys/unix/backtrace/printing/gnu.rs new file mode 100644 index 00000000000..fb06fbedaf5 --- /dev/null +++ b/src/libstd/sys/unix/backtrace/printing/gnu.rs @@ -0,0 +1,11 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use sys_common::gnu::libbacktrace::print; diff --git a/src/libstd/sys/unix/backtrace/printing/libbacktrace.rs b/src/libstd/sys/unix/backtrace/printing/libbacktrace.rs deleted file mode 100644 index 5640eb81f2a..00000000000 --- a/src/libstd/sys/unix/backtrace/printing/libbacktrace.rs +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2014-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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use io; -use io::prelude::*; -use libc; - -use sys::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 env; - use ffi::CStr; - use os::unix::prelude::*; - 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 {} - #[link(name = "backtrace", kind = "static")] - #[cfg(not(test))] - 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..]); } - } - } - - 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). - // - // An additionally oddity in this function is that we initialize the - // filename via self_exe_name() to pass to libbacktrace. It turns out - // that on Linux libbacktrace seamlessly gets the filename of the - // current executable, but this fails on freebsd. by always providing - // it, we make sure that libbacktrace never has a reason to not look up - // the symbols. The libbacktrace API also states that the filename must - // be in "permanent memory", so we copy it to a static and then use the - // static as the pointer. - // - // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't - // tested if this is required or not. - unsafe fn init_state() -> *mut backtrace_state { - static mut STATE: *mut backtrace_state = ptr::null_mut(); - static mut LAST_FILENAME: [libc::c_char; 256] = [0; 256]; - if !STATE.is_null() { return STATE } - let selfname = if cfg!(target_os = "freebsd") || - cfg!(target_os = "dragonfly") || - cfg!(target_os = "bitrig") || - cfg!(target_os = "openbsd") { - env::current_exe().ok() - } else { - None - }; - let filename = match selfname { - Some(path) => { - let bytes = path.as_os_str().as_bytes(); - if bytes.len() < LAST_FILENAME.len() { - let i = bytes.iter(); - for (slot, val) in LAST_FILENAME.iter_mut().zip(i) { - *slot = *val as libc::c_char; - } - LAST_FILENAME.as_ptr() - } else { - ptr::null() - } - } - None => ptr::null(), - }; - STATE = backtrace_create_state(filename, 0, error_cb, - ptr::null_mut()); - return 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() { - try!(output(w, idx, addr, None)); - } else { - 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(()) -} diff --git a/src/libstd/sys/unix/backtrace/printing/mod.rs b/src/libstd/sys/unix/backtrace/printing/mod.rs index a3bd45566f1..e09832c231e 100644 --- a/src/libstd/sys/unix/backtrace/printing/mod.rs +++ b/src/libstd/sys/unix/backtrace/printing/mod.rs @@ -8,12 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use self::imp::*; +pub use self::imp::print; #[cfg(any(target_os = "macos", target_os = "ios"))] #[path = "dladdr.rs"] mod imp; #[cfg(not(any(target_os = "macos", target_os = "ios")))] -#[path = "libbacktrace.rs"] +#[path = "gnu.rs"] mod imp; diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs index d36ca709c5c..35e3c1d4663 100644 --- a/src/libstd/sys/windows/backtrace.rs +++ b/src/libstd/sys/windows/backtrace.rs @@ -27,17 +27,28 @@ use io::prelude::*; use dynamic_lib::DynamicLibrary; -use ffi::CStr; use intrinsics; use io; use libc; -use mem; use path::Path; use ptr; -use str; use sync::StaticMutex; -use sys_common::backtrace::*; +macro_rules! sym{ ($lib:expr, $e:expr, $t:ident) => (unsafe { + let lib = $lib; + match lib.symbol($e) { + Ok(f) => $crate::mem::transmute::<*mut u8, $t>(f), + Err(..) => return Ok(()) + } +}) } + +#[cfg(target_env = "msvc")] +#[path = "printing/msvc.rs"] +mod printing; + +#[cfg(target_env = "gnu")] +#[path = "printing/gnu.rs"] +mod printing; #[allow(non_snake_case)] extern "system" { @@ -302,23 +313,15 @@ pub fn write(w: &mut Write) -> io::Result<()> { // Open up dbghelp.dll, we don't link to it explicitly because it can't // always be found. Additionally, it's nice having fewer dependencies. let path = Path::new("dbghelp.dll"); - let lib = match DynamicLibrary::open(Some(&path)) { + let dbghelp = match DynamicLibrary::open(Some(&path)) { Ok(lib) => lib, Err(..) => return Ok(()), }; - macro_rules! sym{ ($e:expr, $t:ident) => (unsafe { - match lib.symbol($e) { - Ok(f) => mem::transmute::<*mut u8, $t>(f), - Err(..) => return Ok(()) - } - }) } - // Fetch the symbols necessary from dbghelp.dll - let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn); - let SymInitialize = sym!("SymInitialize", SymInitializeFn); - let SymCleanup = sym!("SymCleanup", SymCleanupFn); - let StackWalk64 = sym!("StackWalk64", StackWalk64Fn); + 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 { GetCurrentProcess() }; @@ -334,7 +337,9 @@ pub fn write(w: &mut Write) -> io::Result<()> { let _c = Cleanup { handle: process, SymCleanup: SymCleanup }; // And now that we're done with all the setup, do the stack walking! - let mut i = 0; + // Start from -1 to avoid printing this stack frame, which will + // always be exactly the same. + let mut i = -1; try!(write!(w, "stack backtrace:\n")); while StackWalk64(image, process, thread, &mut frame, &mut context, ptr::null_mut(), @@ -346,28 +351,10 @@ pub fn write(w: &mut Write) -> io::Result<()> { frame.AddrReturn.Offset == 0 { break } i += 1; - try!(write!(w, " {:2}: {:#2$x}", i, addr, HEX_WIDTH)); - let mut info: SYMBOL_INFO = unsafe { intrinsics::init() }; - info.MaxNameLen = MAX_SYM_NAME as libc::c_ulong; - // the struct size in C. the value is different to - // `size_of::() - MAX_SYM_NAME + 1` (== 81) - // due to struct alignment. - info.SizeOfStruct = 88; - - let mut displacement = 0u64; - let ret = SymFromAddr(process, addr as u64, &mut displacement, - &mut info); - - if ret == libc::TRUE { - try!(write!(w, " - ")); - let ptr = info.Name.as_ptr() as *const libc::c_char; - let bytes = unsafe { CStr::from_ptr(ptr).to_bytes() }; - match str::from_utf8(bytes) { - Ok(s) => try!(demangle(w, s)), - Err(..) => try!(w.write_all(&bytes[..bytes.len()-1])), - } + + if i >= 0 { + try!(printing::print(w, i, addr-1, &dbghelp, process)); } - try!(w.write_all(&['\n' as u8])); } Ok(()) diff --git a/src/libstd/sys/windows/printing/gnu.rs b/src/libstd/sys/windows/printing/gnu.rs new file mode 100644 index 00000000000..8d3c93bb7b1 --- /dev/null +++ b/src/libstd/sys/windows/printing/gnu.rs @@ -0,0 +1,22 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use dynamic_lib::DynamicLibrary; +use io; +use io::prelude::*; +use libc; + +use sys_common::gnu::libbacktrace; + +pub fn print(w: &mut Write, i: isize, addr: u64, _: &DynamicLibrary, _: libc::HANDLE) + -> io::Result<()> { + let addr = addr as usize as *mut libc::c_void; + libbacktrace::print(w, i, addr, addr) +} diff --git a/src/libstd/sys/windows/printing/msvc.rs b/src/libstd/sys/windows/printing/msvc.rs new file mode 100644 index 00000000000..25cef04ca96 --- /dev/null +++ b/src/libstd/sys/windows/printing/msvc.rs @@ -0,0 +1,42 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use sys_common::backtrace::output; +use ffi::CStr; +use dynamic_lib::DynamicLibrary; +use super::{SymFromAddrFn, SYMBOL_INFO, MAX_SYM_NAME}; +use io; +use io::prelude::*; +use intrinsics; +use libc; + +pub fn print(w: &mut Write, i: isize, addr: u64, dbghelp: &DynamicLibrary, process: libc::HANDLE) + -> io::Result<()> { + let SymFromAddr = sym!(dbghelp, "SymFromAddr", SymFromAddrFn); + + let mut info: SYMBOL_INFO = unsafe { intrinsics::init() }; + info.MaxNameLen = MAX_SYM_NAME as libc::c_ulong; + // the struct size in C. the value is different to + // `size_of::() - MAX_SYM_NAME + 1` (== 81) + // due to struct alignment. + info.SizeOfStruct = 88; + + let mut displacement = 0u64; + let ret = SymFromAddr(process, addr as u64, &mut displacement, &mut info); + + let name = if ret == libc::TRUE { + let ptr = info.Name.as_ptr() as *const libc::c_char; + Some(unsafe { CStr::from_ptr(ptr).to_bytes() }) + } else { + None + }; + + output(w, i, addr as usize as *mut libc::c_void, name) +} -- cgit 1.4.1-3-g733a5