use std::os::raw::c_void;
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::path::Path;
use std::thread;
use std::mem;
use bt::{resolve, trace, Backtrace, Symbol, SymbolName, BacktraceSymbol};
pub trait BacktraceFmt {
fn format(count: u32, symbol: &Symbol) -> String;
fn format_captured(count: u32, symbol: &BacktraceSymbol) -> String;
}
pub struct DefaultBacktraceFmt;
impl DefaultBacktraceFmt {
#[inline(never)]
fn real_format(count: u32,
name: Option<SymbolName>,
addr: Option<*mut c_void>,
filename: Option<&Path>,
lineno: Option<u32>) -> String {
let ptr_width = mem::size_of::<usize>() * 2 + 2;
let name = name.and_then(|name| { name.as_str() }).unwrap_or("<unknown>");
let begin = format!("{:>4}: {:>4$p} - {:<}\n{:<5$}", count, addr.unwrap_or(0x0 as *mut _), name, "", ptr_width, ptr_width + 6);
let end = if let Some(filename) = filename {
if let Some(lineno) = lineno {
format!("at {}:{}\n", filename.display(), lineno)
} else {
format!("at {}\n", filename.display())
}
} else if let Some(lineno) = lineno {
format!("at <anonymous>:{}\n", lineno)
} else {
"at <anonymous>\n".to_string()
};
begin + end.as_str()
}
}
impl BacktraceFmt for DefaultBacktraceFmt {
#[inline]
fn format(count: u32, symbol: &Symbol) -> String {
DefaultBacktraceFmt::real_format(count, symbol.name(), symbol.addr(), symbol.filename(), symbol.lineno())
}
#[inline]
fn format_captured(count: u32, symbol: &BacktraceSymbol) -> String {
DefaultBacktraceFmt::real_format(count, symbol.name(), symbol.addr(), symbol.filename(), symbol.lineno())
}
}
#[inline(never)]
pub fn format_trace<Fmt: BacktraceFmt>(header: bool, line: u32, file: &str) -> String {
let mut traces = if header {
format!("Stack backtrace for task \"<{}>\" at line {} of \"{}\":\n",
thread::current().name().unwrap_or("unnamed"), line, file)
} else {
String::new()
};
let mut count = 0;
trace(|frame| {
let before = count;
resolve(frame.ip(), |symbol| {
traces += Fmt::format(count, symbol).as_str();
count += 1;
});
if count == before {
resolve(frame.symbol_address(), |symbol| {
traces += Fmt::format(count, symbol).as_str();
count += 1;
});
}
true
});
traces
}
#[derive(Clone)]
pub struct SourceBacktrace {
backtrace: Backtrace,
line: u32,
file: &'static str,
}
impl Debug for SourceBacktrace {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, "SourceBacktrace {{\n line: {},\n file: {},\n backtrace:\n{}}}", self.line, self.file, self.format::<DefaultBacktraceFmt>(false, false))
}
}
impl SourceBacktrace {
pub fn new(line: u32, file: &'static str) -> SourceBacktrace {
SourceBacktrace {
backtrace: Backtrace::new(),
line: line,
file: file,
}
}
#[inline]
pub fn raw(&self) -> &Backtrace {
&self.backtrace
}
#[inline]
pub fn line(&self) -> u32 {
self.line
}
#[inline]
pub fn file(&self) -> &'static str {
self.file
}
#[inline(never)]
pub fn format<Fmt: BacktraceFmt>(&self, header: bool, reverse: bool) -> String {
let mut traces = if header {
format!("Stack backtrace for task \"<{}>\" at line {} of \"{}\":\n",
thread::current().name().unwrap_or("unnamed"), self.line, self.file)
} else {
String::new()
};
let mut count = 0;
if reverse {
let mut symbols = Vec::with_capacity(self.backtrace.frames().len());
for frame in self.backtrace.frames() {
for symbol in frame.symbols() {
symbols.push(symbol);
}
}
for symbol in symbols.iter().rev() {
traces += Fmt::format_captured(count, symbol).as_str();
count += 1;
}
} else {
for frame in self.backtrace.frames() {
for symbol in frame.symbols() {
traces += Fmt::format_captured(count, symbol).as_str();
count += 1;
}
}
}
traces
}
}
impl From<Backtrace> for SourceBacktrace {
fn from(backtrace: Backtrace) -> SourceBacktrace {
SourceBacktrace { line: line!(), file: file!(), backtrace: backtrace }
}
}
#[macro_export]
macro_rules! backtrace {
() => {
backtrace!($crate::backtrace::DefaultBacktraceFmt)
};
($fmt:ty) => {
$crate::backtrace::format_trace::<$fmt>(true, line!(), file!())
};
}
#[macro_export]
macro_rules! backtrace_noheader {
() => {
backtrace_noheader!($crate::backtrace::DefaultBacktraceFmt)
};
($fmt:ty) => {
$crate::backtrace::format_trace::<$fmt>(false, line!(), file!())
};
}