]>
Commit | Line | Data |
---|---|---|
0731742a XL |
1 | use core::{fmt, str}; |
2 | ||
416331ca | 3 | cfg_if::cfg_if! { |
0731742a XL |
4 | if #[cfg(feature = "std")] { |
5 | use std::path::Path; | |
6 | use std::prelude::v1::*; | |
7 | } | |
8 | } | |
9 | ||
3dfed10e XL |
10 | use super::backtrace::Frame; |
11 | use super::types::BytesOrWideString; | |
416331ca | 12 | use core::ffi::c_void; |
041b39d2 | 13 | use rustc_demangle::{try_demangle, Demangle}; |
7cac9316 XL |
14 | |
15 | /// Resolve an address to a symbol, passing the symbol to the specified | |
16 | /// closure. | |
17 | /// | |
18 | /// This function will look up the given address in areas such as the local | |
19 | /// symbol table, dynamic symbol table, or DWARF debug info (depending on the | |
20 | /// activated implementation) to find symbols to yield. | |
21 | /// | |
22 | /// The closure may not be called if resolution could not be performed, and it | |
23 | /// also may be called more than once in the case of inlined functions. | |
24 | /// | |
25 | /// Symbols yielded represent the execution at the specified `addr`, returning | |
ea8adc8c | 26 | /// file/line pairs for that address (if available). |
7cac9316 | 27 | /// |
dc9dc135 XL |
28 | /// Note that if you have a `Frame` then it's recommended to use the |
29 | /// `resolve_frame` function instead of this one. | |
30 | /// | |
0731742a XL |
31 | /// # Required features |
32 | /// | |
33 | /// This function requires the `std` feature of the `backtrace` crate to be | |
34 | /// enabled, and the `std` feature is enabled by default. | |
35 | /// | |
dc9dc135 XL |
36 | /// # Panics |
37 | /// | |
38 | /// This function strives to never panic, but if the `cb` provided panics then | |
39 | /// some platforms will force a double panic to abort the process. Some | |
40 | /// platforms use a C library which internally uses callbacks which cannot be | |
41 | /// unwound through, so panicking from `cb` may trigger a process abort. | |
42 | /// | |
7cac9316 XL |
43 | /// # Example |
44 | /// | |
45 | /// ``` | |
46 | /// extern crate backtrace; | |
47 | /// | |
48 | /// fn main() { | |
49 | /// backtrace::trace(|frame| { | |
50 | /// let ip = frame.ip(); | |
51 | /// | |
52 | /// backtrace::resolve(ip, |symbol| { | |
53 | /// // ... | |
54 | /// }); | |
55 | /// | |
56 | /// false // only look at the top frame | |
57 | /// }); | |
58 | /// } | |
59 | /// ``` | |
0731742a XL |
60 | #[cfg(feature = "std")] |
61 | pub fn resolve<F: FnMut(&Symbol)>(addr: *mut c_void, cb: F) { | |
416331ca | 62 | let _guard = crate::lock::lock(); |
0731742a XL |
63 | unsafe { resolve_unsynchronized(addr, cb) } |
64 | } | |
65 | ||
dc9dc135 XL |
66 | /// Resolve a previously capture frame to a symbol, passing the symbol to the |
67 | /// specified closure. | |
68 | /// | |
69 | /// This functin performs the same function as `resolve` except that it takes a | |
70 | /// `Frame` as an argument instead of an address. This can allow some platform | |
71 | /// implementations of backtracing to provide more accurate symbol information | |
72 | /// or information about inline frames for example. It's recommended to use this | |
73 | /// if you can. | |
74 | /// | |
75 | /// # Required features | |
76 | /// | |
77 | /// This function requires the `std` feature of the `backtrace` crate to be | |
78 | /// enabled, and the `std` feature is enabled by default. | |
79 | /// | |
80 | /// # Panics | |
81 | /// | |
82 | /// This function strives to never panic, but if the `cb` provided panics then | |
83 | /// some platforms will force a double panic to abort the process. Some | |
84 | /// platforms use a C library which internally uses callbacks which cannot be | |
85 | /// unwound through, so panicking from `cb` may trigger a process abort. | |
86 | /// | |
87 | /// # Example | |
88 | /// | |
89 | /// ``` | |
90 | /// extern crate backtrace; | |
91 | /// | |
92 | /// fn main() { | |
93 | /// backtrace::trace(|frame| { | |
94 | /// backtrace::resolve_frame(frame, |symbol| { | |
95 | /// // ... | |
96 | /// }); | |
97 | /// | |
98 | /// false // only look at the top frame | |
99 | /// }); | |
100 | /// } | |
101 | /// ``` | |
102 | #[cfg(feature = "std")] | |
103 | pub fn resolve_frame<F: FnMut(&Symbol)>(frame: &Frame, cb: F) { | |
416331ca | 104 | let _guard = crate::lock::lock(); |
dc9dc135 XL |
105 | unsafe { resolve_frame_unsynchronized(frame, cb) } |
106 | } | |
107 | ||
108 | pub enum ResolveWhat<'a> { | |
109 | Address(*mut c_void), | |
110 | Frame(&'a Frame), | |
111 | } | |
112 | ||
113 | impl<'a> ResolveWhat<'a> { | |
114 | #[allow(dead_code)] | |
115 | fn address_or_ip(&self) -> *mut c_void { | |
416331ca XL |
116 | match self { |
117 | ResolveWhat::Address(a) => adjust_ip(*a), | |
118 | ResolveWhat::Frame(f) => adjust_ip(f.ip()), | |
dc9dc135 XL |
119 | } |
120 | } | |
121 | } | |
122 | ||
416331ca XL |
123 | // IP values from stack frames are typically (always?) the instruction |
124 | // *after* the call that's the actual stack trace. Symbolizing this on | |
125 | // causes the filename/line number to be one ahead and perhaps into | |
126 | // the void if it's near the end of the function. | |
127 | // | |
128 | // This appears to basically always be the case on all platforms, so we always | |
129 | // subtract one from a resolved ip to resolve it to the previous call | |
130 | // instruction instead of the instruction being returned to. | |
131 | // | |
132 | // Ideally we would not do this. Ideally we would require callers of the | |
133 | // `resolve` APIs here to manually do the -1 and account that they want location | |
134 | // information for the *previous* instruction, not the current. Ideally we'd | |
135 | // also expose on `Frame` if we are indeed the address of the next instruction | |
136 | // or the current. | |
137 | // | |
138 | // For now though this is a pretty niche concern so we just internally always | |
139 | // subtract one. Consumers should keep working and getting pretty good results, | |
140 | // so we should be good enough. | |
141 | fn adjust_ip(a: *mut c_void) -> *mut c_void { | |
142 | if a.is_null() { | |
143 | a | |
144 | } else { | |
145 | (a as usize - 1) as *mut c_void | |
146 | } | |
147 | } | |
148 | ||
0731742a XL |
149 | /// Same as `resolve`, only unsafe as it's unsynchronized. |
150 | /// | |
151 | /// This function does not have synchronization guarentees but is available when | |
152 | /// the `std` feature of this crate isn't compiled in. See the `resolve` | |
153 | /// function for more documentation and examples. | |
dc9dc135 XL |
154 | /// |
155 | /// # Panics | |
156 | /// | |
157 | /// See information on `resolve` for caveats on `cb` panicking. | |
0731742a | 158 | pub unsafe fn resolve_unsynchronized<F>(addr: *mut c_void, mut cb: F) |
dc9dc135 XL |
159 | where |
160 | F: FnMut(&Symbol), | |
0731742a | 161 | { |
dc9dc135 | 162 | resolve_imp(ResolveWhat::Address(addr), &mut cb) |
7cac9316 XL |
163 | } |
164 | ||
dc9dc135 XL |
165 | /// Same as `resolve_frame`, only unsafe as it's unsynchronized. |
166 | /// | |
167 | /// This function does not have synchronization guarentees but is available | |
168 | /// when the `std` feature of this crate isn't compiled in. See the | |
169 | /// `resolve_frame` function for more documentation and examples. | |
170 | /// | |
171 | /// # Panics | |
172 | /// | |
173 | /// See information on `resolve_frame` for caveats on `cb` panicking. | |
174 | pub unsafe fn resolve_frame_unsynchronized<F>(frame: &Frame, mut cb: F) | |
175 | where | |
176 | F: FnMut(&Symbol), | |
177 | { | |
178 | resolve_imp(ResolveWhat::Frame(frame), &mut cb) | |
179 | } | |
0731742a | 180 | |
7cac9316 XL |
181 | /// A trait representing the resolution of a symbol in a file. |
182 | /// | |
183 | /// This trait is yielded as a trait object to the closure given to the | |
184 | /// `backtrace::resolve` function, and it is virtually dispatched as it's | |
185 | /// unknown which implementation is behind it. | |
186 | /// | |
ea8adc8c | 187 | /// A symbol can give contextual information about a function, for example the |
7cac9316 XL |
188 | /// name, filename, line number, precise address, etc. Not all information is |
189 | /// always available in a symbol, however, so all methods return an `Option`. | |
190 | pub struct Symbol { | |
416331ca XL |
191 | // TODO: this lifetime bound needs to be persisted eventually to `Symbol`, |
192 | // but that's currently a breaking change. For now this is safe since | |
193 | // `Symbol` is only ever handed out by reference and can't be cloned. | |
194 | inner: SymbolImp<'static>, | |
7cac9316 XL |
195 | } |
196 | ||
197 | impl Symbol { | |
198 | /// Returns the name of this function. | |
199 | /// | |
200 | /// The returned structure can be used to query various properties about the | |
201 | /// symbol name: | |
202 | /// | |
203 | /// * The `Display` implementation will print out the demangled symbol. | |
204 | /// * The raw `str` value of the symbol can be accessed (if it's valid | |
205 | /// utf-8). | |
206 | /// * The raw bytes for the symbol name can be accessed. | |
f035d41b | 207 | pub fn name(&self) -> Option<SymbolName<'_>> { |
7cac9316 XL |
208 | self.inner.name() |
209 | } | |
210 | ||
211 | /// Returns the starting address of this function. | |
212 | pub fn addr(&self) -> Option<*mut c_void> { | |
0731742a | 213 | self.inner.addr().map(|p| p as *mut _) |
7cac9316 XL |
214 | } |
215 | ||
0731742a XL |
216 | /// Returns the raw filename as a slice. This is mainly useful for `no_std` |
217 | /// environments. | |
f035d41b | 218 | pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> { |
0731742a | 219 | self.inner.filename_raw() |
7cac9316 XL |
220 | } |
221 | ||
29967ef6 XL |
222 | /// Returns the column number for where this symbol is currently executing. |
223 | /// | |
224 | /// Only gimli currently provides a value here and even then only if `filename` | |
225 | /// returns `Some`, and so it is then consequently subject to similar caveats. | |
226 | pub fn colno(&self) -> Option<u32> { | |
227 | self.inner.colno() | |
228 | } | |
229 | ||
7cac9316 XL |
230 | /// Returns the line number for where this symbol is currently executing. |
231 | /// | |
232 | /// This return value is typically `Some` if `filename` returns `Some`, and | |
233 | /// is consequently subject to similar caveats. | |
234 | pub fn lineno(&self) -> Option<u32> { | |
235 | self.inner.lineno() | |
236 | } | |
0731742a XL |
237 | |
238 | /// Returns the file name where this function was defined. | |
239 | /// | |
29967ef6 XL |
240 | /// This is currently only available when libbacktrace or gimli is being |
241 | /// used (e.g. unix platforms other) and when a binary is compiled with | |
0731742a XL |
242 | /// debuginfo. If neither of these conditions is met then this will likely |
243 | /// return `None`. | |
244 | /// | |
dc9dc135 XL |
245 | /// # Required features |
246 | /// | |
247 | /// This function requires the `std` feature of the `backtrace` crate to be | |
248 | /// enabled, and the `std` feature is enabled by default. | |
0731742a | 249 | #[cfg(feature = "std")] |
dc9dc135 | 250 | #[allow(unreachable_code)] |
0731742a | 251 | pub fn filename(&self) -> Option<&Path> { |
dc9dc135 | 252 | self.inner.filename() |
0731742a | 253 | } |
7cac9316 XL |
254 | } |
255 | ||
256 | impl fmt::Debug for Symbol { | |
f035d41b | 257 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
7cac9316 XL |
258 | let mut d = f.debug_struct("Symbol"); |
259 | if let Some(name) = self.name() { | |
260 | d.field("name", &name); | |
261 | } | |
262 | if let Some(addr) = self.addr() { | |
263 | d.field("addr", &addr); | |
264 | } | |
0731742a | 265 | |
dc9dc135 XL |
266 | #[cfg(feature = "std")] |
267 | { | |
0731742a XL |
268 | if let Some(filename) = self.filename() { |
269 | d.field("filename", &filename); | |
270 | } | |
7cac9316 | 271 | } |
0731742a | 272 | |
7cac9316 XL |
273 | if let Some(lineno) = self.lineno() { |
274 | d.field("lineno", &lineno); | |
275 | } | |
276 | d.finish() | |
277 | } | |
278 | } | |
279 | ||
416331ca | 280 | cfg_if::cfg_if! { |
041b39d2 XL |
281 | if #[cfg(feature = "cpp_demangle")] { |
282 | // Maybe a parsed C++ symbol, if parsing the mangled symbol as Rust | |
283 | // failed. | |
284 | struct OptionCppSymbol<'a>(Option<::cpp_demangle::BorrowedSymbol<'a>>); | |
285 | ||
286 | impl<'a> OptionCppSymbol<'a> { | |
287 | fn parse(input: &'a [u8]) -> OptionCppSymbol<'a> { | |
288 | OptionCppSymbol(::cpp_demangle::BorrowedSymbol::new(input).ok()) | |
289 | } | |
290 | ||
291 | fn none() -> OptionCppSymbol<'a> { | |
292 | OptionCppSymbol(None) | |
293 | } | |
294 | } | |
295 | } else { | |
0731742a XL |
296 | use core::marker::PhantomData; |
297 | ||
041b39d2 XL |
298 | // Make sure to keep this zero-sized, so that the `cpp_demangle` feature |
299 | // has no cost when disabled. | |
300 | struct OptionCppSymbol<'a>(PhantomData<&'a ()>); | |
301 | ||
302 | impl<'a> OptionCppSymbol<'a> { | |
303 | fn parse(_: &'a [u8]) -> OptionCppSymbol<'a> { | |
304 | OptionCppSymbol(PhantomData) | |
305 | } | |
306 | ||
307 | fn none() -> OptionCppSymbol<'a> { | |
308 | OptionCppSymbol(PhantomData) | |
309 | } | |
310 | } | |
311 | } | |
312 | } | |
313 | ||
7cac9316 XL |
314 | /// A wrapper around a symbol name to provide ergonomic accessors to the |
315 | /// demangled name, the raw bytes, the raw string, etc. | |
041b39d2 XL |
316 | // Allow dead code for when the `cpp_demangle` feature is not enabled. |
317 | #[allow(dead_code)] | |
7cac9316 XL |
318 | pub struct SymbolName<'a> { |
319 | bytes: &'a [u8], | |
320 | demangled: Option<Demangle<'a>>, | |
041b39d2 | 321 | cpp_demangled: OptionCppSymbol<'a>, |
7cac9316 XL |
322 | } |
323 | ||
324 | impl<'a> SymbolName<'a> { | |
325 | /// Creates a new symbol name from the raw underlying bytes. | |
326 | pub fn new(bytes: &'a [u8]) -> SymbolName<'a> { | |
041b39d2 XL |
327 | let str_bytes = str::from_utf8(bytes).ok(); |
328 | let demangled = str_bytes.and_then(|s| try_demangle(s).ok()); | |
329 | ||
330 | let cpp = if demangled.is_none() { | |
331 | OptionCppSymbol::parse(bytes) | |
332 | } else { | |
333 | OptionCppSymbol::none() | |
334 | }; | |
335 | ||
7cac9316 XL |
336 | SymbolName { |
337 | bytes: bytes, | |
338 | demangled: demangled, | |
041b39d2 | 339 | cpp_demangled: cpp, |
7cac9316 XL |
340 | } |
341 | } | |
342 | ||
dc9dc135 XL |
343 | /// Returns the raw (mangled) symbol name as a `str` if the symbol is valid utf-8. |
344 | /// | |
345 | /// Use the `Display` implementation if you want the demangled version. | |
7cac9316 | 346 | pub fn as_str(&self) -> Option<&'a str> { |
041b39d2 XL |
347 | self.demangled |
348 | .as_ref() | |
349 | .map(|s| s.as_str()) | |
dc9dc135 | 350 | .or_else(|| str::from_utf8(self.bytes).ok()) |
7cac9316 XL |
351 | } |
352 | ||
353 | /// Returns the raw symbol name as a list of bytes | |
354 | pub fn as_bytes(&self) -> &'a [u8] { | |
355 | self.bytes | |
356 | } | |
357 | } | |
358 | ||
dc9dc135 | 359 | fn format_symbol_name( |
f035d41b | 360 | fmt: fn(&str, &mut fmt::Formatter<'_>) -> fmt::Result, |
dc9dc135 | 361 | mut bytes: &[u8], |
f035d41b | 362 | f: &mut fmt::Formatter<'_>, |
dc9dc135 | 363 | ) -> fmt::Result { |
0731742a XL |
364 | while bytes.len() > 0 { |
365 | match str::from_utf8(bytes) { | |
366 | Ok(name) => { | |
367 | fmt(name, f)?; | |
dc9dc135 | 368 | break; |
0731742a XL |
369 | } |
370 | Err(err) => { | |
371 | fmt("\u{FFFD}", f)?; | |
372 | ||
373 | match err.error_len() { | |
374 | Some(len) => bytes = &bytes[err.valid_up_to() + len..], | |
375 | None => break, | |
376 | } | |
377 | } | |
378 | } | |
379 | } | |
380 | Ok(()) | |
381 | } | |
382 | ||
416331ca | 383 | cfg_if::cfg_if! { |
041b39d2 XL |
384 | if #[cfg(feature = "cpp_demangle")] { |
385 | impl<'a> fmt::Display for SymbolName<'a> { | |
386 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
387 | if let Some(ref s) = self.demangled { | |
388 | s.fmt(f) | |
389 | } else if let Some(ref cpp) = self.cpp_demangled.0 { | |
390 | cpp.fmt(f) | |
391 | } else { | |
0731742a | 392 | format_symbol_name(fmt::Display::fmt, self.bytes, f) |
041b39d2 XL |
393 | } |
394 | } | |
395 | } | |
396 | } else { | |
397 | impl<'a> fmt::Display for SymbolName<'a> { | |
f035d41b | 398 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
041b39d2 XL |
399 | if let Some(ref s) = self.demangled { |
400 | s.fmt(f) | |
401 | } else { | |
0731742a | 402 | format_symbol_name(fmt::Display::fmt, self.bytes, f) |
041b39d2 XL |
403 | } |
404 | } | |
7cac9316 XL |
405 | } |
406 | } | |
407 | } | |
408 | ||
416331ca | 409 | cfg_if::cfg_if! { |
0731742a | 410 | if #[cfg(all(feature = "std", feature = "cpp_demangle"))] { |
041b39d2 XL |
411 | impl<'a> fmt::Debug for SymbolName<'a> { |
412 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
413 | use std::fmt::Write; | |
414 | ||
415 | if let Some(ref s) = self.demangled { | |
416 | return s.fmt(f) | |
417 | } | |
418 | ||
419 | // This may to print if the demangled symbol isn't actually | |
420 | // valid, so handle the error here gracefully by not propagating | |
421 | // it outwards. | |
422 | if let Some(ref cpp) = self.cpp_demangled.0 { | |
423 | let mut s = String::new(); | |
424 | if write!(s, "{}", cpp).is_ok() { | |
425 | return s.fmt(f) | |
426 | } | |
427 | } | |
428 | ||
0731742a | 429 | format_symbol_name(fmt::Debug::fmt, self.bytes, f) |
041b39d2 XL |
430 | } |
431 | } | |
432 | } else { | |
433 | impl<'a> fmt::Debug for SymbolName<'a> { | |
f035d41b | 434 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
041b39d2 XL |
435 | if let Some(ref s) = self.demangled { |
436 | s.fmt(f) | |
437 | } else { | |
0731742a | 438 | format_symbol_name(fmt::Debug::fmt, self.bytes, f) |
041b39d2 XL |
439 | } |
440 | } | |
7cac9316 XL |
441 | } |
442 | } | |
443 | } | |
444 | ||
416331ca XL |
445 | /// Attempt to reclaim that cached memory used to symbolicate addresses. |
446 | /// | |
447 | /// This method will attempt to release any global data structures that have | |
448 | /// otherwise been cached globally or in the thread which typically represent | |
449 | /// parsed DWARF information or similar. | |
450 | /// | |
451 | /// # Caveats | |
452 | /// | |
453 | /// While this function is always available it doesn't actually do anything on | |
454 | /// most implementations. Libraries like dbghelp or libbacktrace do not provide | |
455 | /// facilities to deallocate state and manage the allocated memory. For now the | |
456 | /// `gimli-symbolize` feature of this crate is the only feature where this | |
457 | /// function has any effect. | |
458 | #[cfg(feature = "std")] | |
459 | pub fn clear_symbol_cache() { | |
460 | let _guard = crate::lock::lock(); | |
461 | unsafe { | |
462 | clear_symbol_cache_imp(); | |
463 | } | |
464 | } | |
465 | ||
416331ca | 466 | cfg_if::cfg_if! { |
3dfed10e | 467 | if #[cfg(miri)] { |
29967ef6 XL |
468 | mod miri; |
469 | use self::miri::resolve as resolve_imp; | |
470 | use self::miri::Symbol as SymbolImp; | |
3dfed10e XL |
471 | unsafe fn clear_symbol_cache_imp() {} |
472 | } else if #[cfg(all(windows, target_env = "msvc", not(target_vendor = "uwp")))] { | |
7cac9316 XL |
473 | mod dbghelp; |
474 | use self::dbghelp::resolve as resolve_imp; | |
475 | use self::dbghelp::Symbol as SymbolImp; | |
416331ca XL |
476 | unsafe fn clear_symbol_cache_imp() {} |
477 | } else if #[cfg(all( | |
f035d41b XL |
478 | feature = "libbacktrace", |
479 | any(unix, all(windows, not(target_vendor = "uwp"), target_env = "gnu")), | |
480 | not(target_os = "fuchsia"), | |
481 | not(target_os = "emscripten"), | |
482 | not(target_env = "uclibc"), | |
29967ef6 | 483 | not(target_env = "libnx"), |
f035d41b XL |
484 | ))] { |
485 | mod libbacktrace; | |
486 | use self::libbacktrace::resolve as resolve_imp; | |
487 | use self::libbacktrace::Symbol as SymbolImp; | |
488 | unsafe fn clear_symbol_cache_imp() {} | |
489 | } else if #[cfg(all( | |
416331ca | 490 | feature = "gimli-symbolize", |
f035d41b | 491 | any(unix, windows), |
3dfed10e | 492 | not(target_vendor = "uwp"), |
f035d41b | 493 | not(target_os = "emscripten"), |
416331ca | 494 | ))] { |
ea8adc8c XL |
495 | mod gimli; |
496 | use self::gimli::resolve as resolve_imp; | |
497 | use self::gimli::Symbol as SymbolImp; | |
416331ca | 498 | use self::gimli::clear_symbol_cache as clear_symbol_cache_imp; |
7cac9316 XL |
499 | } else { |
500 | mod noop; | |
501 | use self::noop::resolve as resolve_imp; | |
502 | use self::noop::Symbol as SymbolImp; | |
e1599b0c | 503 | #[allow(unused)] |
416331ca | 504 | unsafe fn clear_symbol_cache_imp() {} |
7cac9316 XL |
505 | } |
506 | } |