1use std::os::raw::c_void;
7use std::fmt::{Debug, Formatter, Result as FmtResult};
8use std::path::Path;
9use std::thread;
10use std::mem;
11
12use bt::{resolve, trace, Backtrace, Symbol, SymbolName, BacktraceSymbol};
13
14pub trait BacktraceFmt {
16 fn format(count: u32, symbol: &Symbol) -> String;
18
19 fn format_captured(count: u32, symbol: &BacktraceSymbol) -> String;
21}
22
23pub struct DefaultBacktraceFmt;
45
46impl DefaultBacktraceFmt {
47 #[inline(never)]
48 fn real_format(count: u32,
49 name: Option<SymbolName>,
50 addr: Option<*mut c_void>,
51 filename: Option<&Path>,
52 lineno: Option<u32>) -> String {
53 let ptr_width = mem::size_of::<usize>() * 2 + 2;
54
55 let name = name.and_then(|name| { name.as_str() }).unwrap_or("<unknown>");
56
57 let begin = format!("{:>4}: {:>4$p} - {:<}\n{:<5$}", count, addr.unwrap_or(0x0 as *mut _), name, "", ptr_width, ptr_width + 6);
58
59 let end = if let Some(filename) = filename {
60 if let Some(lineno) = lineno {
61 format!("at {}:{}\n", filename.display(), lineno)
62 } else {
63 format!("at {}\n", filename.display())
64 }
65 } else if let Some(lineno) = lineno {
66 format!("at <anonymous>:{}\n", lineno)
67 } else {
68 "at <anonymous>\n".to_string()
69 };
70
71 begin + end.as_str()
72 }
73}
74
75impl BacktraceFmt for DefaultBacktraceFmt {
76 #[inline]
77 fn format(count: u32, symbol: &Symbol) -> String {
78 DefaultBacktraceFmt::real_format(count, symbol.name(), symbol.addr(), symbol.filename(), symbol.lineno())
79 }
80
81 #[inline]
82 fn format_captured(count: u32, symbol: &BacktraceSymbol) -> String {
83 DefaultBacktraceFmt::real_format(count, symbol.name(), symbol.addr(), symbol.filename(), symbol.lineno())
85 }
86}
87
88#[inline(never)]
92pub fn format_trace<Fmt: BacktraceFmt>(header: bool, line: u32, file: &str) -> String {
93 let mut traces = if header {
94 format!("Stack backtrace for task \"<{}>\" at line {} of \"{}\":\n",
95 thread::current().name().unwrap_or("unnamed"), line, file)
96 } else {
97 String::new()
98 };
99
100 let mut count = 0;
101
102 trace(|frame| {
103 let before = count;
104
105 resolve(frame.ip(), |symbol| {
106 traces += Fmt::format(count, symbol).as_str();
107
108 count += 1;
109 });
110
111 if count == before {
113 resolve(frame.symbol_address(), |symbol| {
115 traces += Fmt::format(count, symbol).as_str();
116
117 count += 1;
118 });
119 }
120
121 true
123 });
124
125 traces
126}
127
128#[derive(Clone)]
132pub struct SourceBacktrace {
133 backtrace: Backtrace,
134 line: u32,
135 file: &'static str,
136}
137
138impl Debug for SourceBacktrace {
139 fn fmt(&self, f: &mut Formatter) -> FmtResult {
140 write!(f, "SourceBacktrace {{\n line: {},\n file: {},\n backtrace:\n{}}}", self.line, self.file, self.format::<DefaultBacktraceFmt>(false, false))
141 }
142}
143
144impl SourceBacktrace {
145 pub fn new(line: u32, file: &'static str) -> SourceBacktrace {
147 SourceBacktrace {
148 backtrace: Backtrace::new(),
149 line: line,
150 file: file,
151 }
152 }
153
154 #[inline]
156 pub fn raw(&self) -> &Backtrace {
157 &self.backtrace
158 }
159
160 #[inline]
162 pub fn line(&self) -> u32 {
163 self.line
164 }
165
166 #[inline]
168 pub fn file(&self) -> &'static str {
169 self.file
170 }
171
172 #[inline(never)]
174 pub fn format<Fmt: BacktraceFmt>(&self, header: bool, reverse: bool) -> String {
175 let mut traces = if header {
176 format!("Stack backtrace for task \"<{}>\" at line {} of \"{}\":\n",
177 thread::current().name().unwrap_or("unnamed"), self.line, self.file)
178 } else {
179 String::new()
180 };
181
182 let mut count = 0;
183
184 if reverse {
185 let mut symbols = Vec::with_capacity(self.backtrace.frames().len());
187
188 for frame in self.backtrace.frames() {
189 for symbol in frame.symbols() {
190 symbols.push(symbol);
191 }
192 }
193
194 for symbol in symbols.iter().rev() {
195 traces += Fmt::format_captured(count, symbol).as_str();
196
197 count += 1;
198 }
199 } else {
200 for frame in self.backtrace.frames() {
201 for symbol in frame.symbols() {
202 traces += Fmt::format_captured(count, symbol).as_str();
203
204 count += 1;
205 }
206 }
207 }
208
209 traces
210 }
211}
212
213impl From<Backtrace> for SourceBacktrace {
214 fn from(backtrace: Backtrace) -> SourceBacktrace {
215 SourceBacktrace { line: line!(), file: file!(), backtrace: backtrace }
216 }
217}
218
219#[macro_export]
223macro_rules! backtrace {
224 () => {
225 backtrace!($crate::backtrace::DefaultBacktraceFmt)
226 };
227
228 ($fmt:ty) => {
229 $crate::backtrace::format_trace::<$fmt>(true, line!(), file!())
230 };
231}
232
233#[macro_export]
235macro_rules! backtrace_noheader {
236 () => {
237 backtrace_noheader!($crate::backtrace::DefaultBacktraceFmt)
238 };
239
240 ($fmt:ty) => {
241 $crate::backtrace::format_trace::<$fmt>(false, line!(), file!())
242 };
243}