use std::fmt;
use std::mem;
use std::os::raw::c_void;
use std::path::{Path, PathBuf};
use {trace, resolve, SymbolName};
#[derive(Clone)]
pub struct Backtrace {
frames: Vec<BacktraceFrame>,
}
#[derive(Clone)]
pub struct BacktraceFrame {
ip: usize,
symbol_address: usize,
symbols: Vec<BacktraceSymbol>,
}
#[derive(Clone)]
pub struct BacktraceSymbol {
name: Option<Vec<u8>>,
addr: Option<usize>,
filename: Option<PathBuf>,
lineno: Option<u32>,
}
impl Backtrace {
pub fn new() -> Backtrace {
let mut frames = Vec::new();
trace(|frame| {
let mut symbols = Vec::new();
resolve(frame.ip(), |symbol| {
symbols.push(BacktraceSymbol {
name: symbol.name().map(|m| m.as_bytes().to_vec()),
addr: symbol.addr().map(|a| a as usize),
filename: symbol.filename().map(|m| m.to_path_buf()),
lineno: symbol.lineno(),
});
});
frames.push(BacktraceFrame {
ip: frame.ip() as usize,
symbol_address: frame.symbol_address() as usize,
symbols: symbols,
});
true
});
Backtrace { frames: frames }
}
pub fn frames(&self) -> &[BacktraceFrame] {
&self.frames
}
}
impl From<Vec<BacktraceFrame>> for Backtrace {
fn from(frames: Vec<BacktraceFrame>) -> Self {
Backtrace {
frames: frames
}
}
}
impl Into<Vec<BacktraceFrame>> for Backtrace {
fn into(self) -> Vec<BacktraceFrame> {
self.frames
}
}
impl BacktraceFrame {
pub fn ip(&self) -> *mut c_void {
self.ip as *mut c_void
}
pub fn symbol_address(&self) -> *mut c_void {
self.symbol_address as *mut c_void
}
}
impl BacktraceFrame {
pub fn symbols(&self) -> &[BacktraceSymbol] {
&self.symbols
}
}
impl BacktraceSymbol {
pub fn name(&self) -> Option<SymbolName> {
self.name.as_ref().map(|s| SymbolName::new(s))
}
pub fn addr(&self) -> Option<*mut c_void> {
self.addr.map(|s| s as *mut c_void)
}
pub fn filename(&self) -> Option<&Path> {
self.filename.as_ref().map(|p| &**p)
}
pub fn lineno(&self) -> Option<u32> {
self.lineno
}
}
impl fmt::Debug for Backtrace {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let hex_width = mem::size_of::<usize>() * 2 + 2;
try!(write!(fmt, "stack backtrace:"));
for (idx, frame) in self.frames().iter().enumerate() {
let ip = frame.ip();
try!(write!(fmt, "\n{:4}: {:2$?}", idx, ip, hex_width));
if frame.symbols.len() == 0 {
try!(write!(fmt, " - <no info>"));
}
for (idx, symbol) in frame.symbols().iter().enumerate() {
if idx != 0 {
try!(write!(fmt, "\n {:1$}", "", hex_width));
}
if let Some(name) = symbol.name() {
try!(write!(fmt, " - {}", name));
} else {
try!(write!(fmt, " - <unknown>"));
}
if let (Some(file), Some(line)) = (symbol.filename(), symbol.lineno()) {
try!(write!(fmt, "\n {:3$}at {}:{}", "", file.display(), line, hex_width));
}
}
}
Ok(())
}
}
impl Default for Backtrace {
fn default() -> Backtrace {
Backtrace::new()
}
}