]>
Commit | Line | Data |
---|---|---|
0731742a XL |
1 | use core::{fmt, str}; |
2 | ||
3 | cfg_if! { | |
4 | if #[cfg(feature = "std")] { | |
5 | use std::path::Path; | |
6 | use std::prelude::v1::*; | |
7 | } | |
8 | } | |
9 | ||
10 | use types::{BytesOrWideString, c_void}; | |
041b39d2 | 11 | use rustc_demangle::{try_demangle, Demangle}; |
7cac9316 XL |
12 | |
13 | /// Resolve an address to a symbol, passing the symbol to the specified | |
14 | /// closure. | |
15 | /// | |
16 | /// This function will look up the given address in areas such as the local | |
17 | /// symbol table, dynamic symbol table, or DWARF debug info (depending on the | |
18 | /// activated implementation) to find symbols to yield. | |
19 | /// | |
20 | /// The closure may not be called if resolution could not be performed, and it | |
21 | /// also may be called more than once in the case of inlined functions. | |
22 | /// | |
23 | /// Symbols yielded represent the execution at the specified `addr`, returning | |
ea8adc8c | 24 | /// file/line pairs for that address (if available). |
7cac9316 | 25 | /// |
0731742a XL |
26 | /// # Required features |
27 | /// | |
28 | /// This function requires the `std` feature of the `backtrace` crate to be | |
29 | /// enabled, and the `std` feature is enabled by default. | |
30 | /// | |
7cac9316 XL |
31 | /// # Example |
32 | /// | |
33 | /// ``` | |
34 | /// extern crate backtrace; | |
35 | /// | |
36 | /// fn main() { | |
37 | /// backtrace::trace(|frame| { | |
38 | /// let ip = frame.ip(); | |
39 | /// | |
40 | /// backtrace::resolve(ip, |symbol| { | |
41 | /// // ... | |
42 | /// }); | |
43 | /// | |
44 | /// false // only look at the top frame | |
45 | /// }); | |
46 | /// } | |
47 | /// ``` | |
0731742a XL |
48 | #[cfg(feature = "std")] |
49 | pub fn resolve<F: FnMut(&Symbol)>(addr: *mut c_void, cb: F) { | |
50 | let _guard = ::lock::lock(); | |
51 | unsafe { resolve_unsynchronized(addr, cb) } | |
52 | } | |
53 | ||
54 | /// Same as `resolve`, only unsafe as it's unsynchronized. | |
55 | /// | |
56 | /// This function does not have synchronization guarentees but is available when | |
57 | /// the `std` feature of this crate isn't compiled in. See the `resolve` | |
58 | /// function for more documentation and examples. | |
59 | pub unsafe fn resolve_unsynchronized<F>(addr: *mut c_void, mut cb: F) | |
60 | where F: FnMut(&Symbol) | |
61 | { | |
62 | resolve_imp(addr as *mut _, &mut cb) | |
7cac9316 XL |
63 | } |
64 | ||
0731742a | 65 | |
7cac9316 XL |
66 | /// A trait representing the resolution of a symbol in a file. |
67 | /// | |
68 | /// This trait is yielded as a trait object to the closure given to the | |
69 | /// `backtrace::resolve` function, and it is virtually dispatched as it's | |
70 | /// unknown which implementation is behind it. | |
71 | /// | |
ea8adc8c | 72 | /// A symbol can give contextual information about a function, for example the |
7cac9316 XL |
73 | /// name, filename, line number, precise address, etc. Not all information is |
74 | /// always available in a symbol, however, so all methods return an `Option`. | |
75 | pub struct Symbol { | |
76 | inner: SymbolImp, | |
77 | } | |
78 | ||
79 | impl Symbol { | |
80 | /// Returns the name of this function. | |
81 | /// | |
82 | /// The returned structure can be used to query various properties about the | |
83 | /// symbol name: | |
84 | /// | |
85 | /// * The `Display` implementation will print out the demangled symbol. | |
86 | /// * The raw `str` value of the symbol can be accessed (if it's valid | |
87 | /// utf-8). | |
88 | /// * The raw bytes for the symbol name can be accessed. | |
89 | pub fn name(&self) -> Option<SymbolName> { | |
90 | self.inner.name() | |
91 | } | |
92 | ||
93 | /// Returns the starting address of this function. | |
94 | pub fn addr(&self) -> Option<*mut c_void> { | |
0731742a | 95 | self.inner.addr().map(|p| p as *mut _) |
7cac9316 XL |
96 | } |
97 | ||
0731742a XL |
98 | /// Returns the raw filename as a slice. This is mainly useful for `no_std` |
99 | /// environments. | |
100 | pub fn filename_raw(&self) -> Option<BytesOrWideString> { | |
101 | self.inner.filename_raw() | |
7cac9316 XL |
102 | } |
103 | ||
0731742a | 104 | |
7cac9316 XL |
105 | /// Returns the line number for where this symbol is currently executing. |
106 | /// | |
107 | /// This return value is typically `Some` if `filename` returns `Some`, and | |
108 | /// is consequently subject to similar caveats. | |
109 | pub fn lineno(&self) -> Option<u32> { | |
110 | self.inner.lineno() | |
111 | } | |
0731742a XL |
112 | |
113 | /// Returns the file name where this function was defined. | |
114 | /// | |
115 | /// This is currently only available when libbacktrace is being used (e.g. | |
116 | /// unix platforms other than OSX) and when a binary is compiled with | |
117 | /// debuginfo. If neither of these conditions is met then this will likely | |
118 | /// return `None`. | |
119 | /// | |
120 | /// This function requires the `std` feature to be enabled for this crate. | |
121 | #[cfg(feature = "std")] | |
122 | pub fn filename(&self) -> Option<&Path> { | |
123 | #[cfg(not(windows))] | |
124 | { | |
125 | use std::ffi::OsStr; | |
126 | use std::os::unix::ffi::OsStrExt; | |
127 | ||
128 | match self.filename_raw() { | |
129 | Some(BytesOrWideString::Bytes(slice)) => { | |
130 | Some(Path::new(OsStr::from_bytes(slice))) | |
131 | } | |
132 | None => None, | |
133 | _ => unreachable!(), | |
134 | } | |
135 | } | |
136 | #[cfg(windows)] | |
137 | { | |
138 | self.inner.filename().map(Path::new) | |
139 | } | |
140 | } | |
7cac9316 XL |
141 | } |
142 | ||
143 | impl fmt::Debug for Symbol { | |
144 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
145 | let mut d = f.debug_struct("Symbol"); | |
146 | if let Some(name) = self.name() { | |
147 | d.field("name", &name); | |
148 | } | |
149 | if let Some(addr) = self.addr() { | |
150 | d.field("addr", &addr); | |
151 | } | |
0731742a XL |
152 | |
153 | #[cfg(feature = "std")] { | |
154 | if let Some(filename) = self.filename() { | |
155 | d.field("filename", &filename); | |
156 | } | |
7cac9316 | 157 | } |
0731742a | 158 | |
7cac9316 XL |
159 | if let Some(lineno) = self.lineno() { |
160 | d.field("lineno", &lineno); | |
161 | } | |
162 | d.finish() | |
163 | } | |
164 | } | |
165 | ||
041b39d2 XL |
166 | cfg_if! { |
167 | if #[cfg(feature = "cpp_demangle")] { | |
168 | // Maybe a parsed C++ symbol, if parsing the mangled symbol as Rust | |
169 | // failed. | |
170 | struct OptionCppSymbol<'a>(Option<::cpp_demangle::BorrowedSymbol<'a>>); | |
171 | ||
172 | impl<'a> OptionCppSymbol<'a> { | |
173 | fn parse(input: &'a [u8]) -> OptionCppSymbol<'a> { | |
174 | OptionCppSymbol(::cpp_demangle::BorrowedSymbol::new(input).ok()) | |
175 | } | |
176 | ||
177 | fn none() -> OptionCppSymbol<'a> { | |
178 | OptionCppSymbol(None) | |
179 | } | |
180 | } | |
181 | } else { | |
0731742a XL |
182 | use core::marker::PhantomData; |
183 | ||
041b39d2 XL |
184 | // Make sure to keep this zero-sized, so that the `cpp_demangle` feature |
185 | // has no cost when disabled. | |
186 | struct OptionCppSymbol<'a>(PhantomData<&'a ()>); | |
187 | ||
188 | impl<'a> OptionCppSymbol<'a> { | |
189 | fn parse(_: &'a [u8]) -> OptionCppSymbol<'a> { | |
190 | OptionCppSymbol(PhantomData) | |
191 | } | |
192 | ||
193 | fn none() -> OptionCppSymbol<'a> { | |
194 | OptionCppSymbol(PhantomData) | |
195 | } | |
196 | } | |
197 | } | |
198 | } | |
199 | ||
7cac9316 XL |
200 | /// A wrapper around a symbol name to provide ergonomic accessors to the |
201 | /// demangled name, the raw bytes, the raw string, etc. | |
041b39d2 XL |
202 | // Allow dead code for when the `cpp_demangle` feature is not enabled. |
203 | #[allow(dead_code)] | |
7cac9316 XL |
204 | pub struct SymbolName<'a> { |
205 | bytes: &'a [u8], | |
206 | demangled: Option<Demangle<'a>>, | |
041b39d2 | 207 | cpp_demangled: OptionCppSymbol<'a>, |
7cac9316 XL |
208 | } |
209 | ||
210 | impl<'a> SymbolName<'a> { | |
211 | /// Creates a new symbol name from the raw underlying bytes. | |
212 | pub fn new(bytes: &'a [u8]) -> SymbolName<'a> { | |
041b39d2 XL |
213 | let str_bytes = str::from_utf8(bytes).ok(); |
214 | let demangled = str_bytes.and_then(|s| try_demangle(s).ok()); | |
215 | ||
216 | let cpp = if demangled.is_none() { | |
217 | OptionCppSymbol::parse(bytes) | |
218 | } else { | |
219 | OptionCppSymbol::none() | |
220 | }; | |
221 | ||
7cac9316 XL |
222 | SymbolName { |
223 | bytes: bytes, | |
224 | demangled: demangled, | |
041b39d2 | 225 | cpp_demangled: cpp, |
7cac9316 XL |
226 | } |
227 | } | |
228 | ||
041b39d2 | 229 | /// Returns the raw symbol name as a `str` if the symbols is valid utf-8. |
7cac9316 | 230 | pub fn as_str(&self) -> Option<&'a str> { |
041b39d2 XL |
231 | self.demangled |
232 | .as_ref() | |
233 | .map(|s| s.as_str()) | |
234 | .or_else(|| { | |
235 | str::from_utf8(self.bytes).ok() | |
236 | }) | |
7cac9316 XL |
237 | } |
238 | ||
239 | /// Returns the raw symbol name as a list of bytes | |
240 | pub fn as_bytes(&self) -> &'a [u8] { | |
241 | self.bytes | |
242 | } | |
243 | } | |
244 | ||
0731742a XL |
245 | fn format_symbol_name(fmt: fn(&str, &mut fmt::Formatter) -> fmt::Result, |
246 | mut bytes: &[u8], | |
247 | f: &mut fmt::Formatter) | |
248 | -> fmt::Result | |
249 | { | |
250 | while bytes.len() > 0 { | |
251 | match str::from_utf8(bytes) { | |
252 | Ok(name) => { | |
253 | fmt(name, f)?; | |
254 | break | |
255 | } | |
256 | Err(err) => { | |
257 | fmt("\u{FFFD}", f)?; | |
258 | ||
259 | match err.error_len() { | |
260 | Some(len) => bytes = &bytes[err.valid_up_to() + len..], | |
261 | None => break, | |
262 | } | |
263 | } | |
264 | } | |
265 | } | |
266 | Ok(()) | |
267 | } | |
268 | ||
041b39d2 XL |
269 | cfg_if! { |
270 | if #[cfg(feature = "cpp_demangle")] { | |
271 | impl<'a> fmt::Display for SymbolName<'a> { | |
272 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
273 | if let Some(ref s) = self.demangled { | |
274 | s.fmt(f) | |
275 | } else if let Some(ref cpp) = self.cpp_demangled.0 { | |
276 | cpp.fmt(f) | |
277 | } else { | |
0731742a | 278 | format_symbol_name(fmt::Display::fmt, self.bytes, f) |
041b39d2 XL |
279 | } |
280 | } | |
281 | } | |
282 | } else { | |
283 | impl<'a> fmt::Display for SymbolName<'a> { | |
284 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
285 | if let Some(ref s) = self.demangled { | |
286 | s.fmt(f) | |
287 | } else { | |
0731742a | 288 | format_symbol_name(fmt::Display::fmt, self.bytes, f) |
041b39d2 XL |
289 | } |
290 | } | |
7cac9316 XL |
291 | } |
292 | } | |
293 | } | |
294 | ||
041b39d2 | 295 | cfg_if! { |
0731742a | 296 | if #[cfg(all(feature = "std", feature = "cpp_demangle"))] { |
041b39d2 XL |
297 | impl<'a> fmt::Debug for SymbolName<'a> { |
298 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
299 | use std::fmt::Write; | |
300 | ||
301 | if let Some(ref s) = self.demangled { | |
302 | return s.fmt(f) | |
303 | } | |
304 | ||
305 | // This may to print if the demangled symbol isn't actually | |
306 | // valid, so handle the error here gracefully by not propagating | |
307 | // it outwards. | |
308 | if let Some(ref cpp) = self.cpp_demangled.0 { | |
309 | let mut s = String::new(); | |
310 | if write!(s, "{}", cpp).is_ok() { | |
311 | return s.fmt(f) | |
312 | } | |
313 | } | |
314 | ||
0731742a | 315 | format_symbol_name(fmt::Debug::fmt, self.bytes, f) |
041b39d2 XL |
316 | } |
317 | } | |
318 | } else { | |
319 | impl<'a> fmt::Debug for SymbolName<'a> { | |
320 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
321 | if let Some(ref s) = self.demangled { | |
322 | s.fmt(f) | |
323 | } else { | |
0731742a | 324 | format_symbol_name(fmt::Debug::fmt, self.bytes, f) |
041b39d2 XL |
325 | } |
326 | } | |
7cac9316 XL |
327 | } |
328 | } | |
329 | } | |
330 | ||
331 | cfg_if! { | |
332 | if #[cfg(all(windows, feature = "dbghelp"))] { | |
333 | mod dbghelp; | |
334 | use self::dbghelp::resolve as resolve_imp; | |
335 | use self::dbghelp::Symbol as SymbolImp; | |
0731742a XL |
336 | } else if #[cfg(all(feature = "std", |
337 | feature = "gimli-symbolize", | |
ea8adc8c XL |
338 | unix, |
339 | target_os = "linux"))] { | |
340 | mod gimli; | |
341 | use self::gimli::resolve as resolve_imp; | |
342 | use self::gimli::Symbol as SymbolImp; | |
7cac9316 XL |
343 | } else if #[cfg(all(feature = "libbacktrace", |
344 | unix, | |
abe05a73 | 345 | not(target_os = "fuchsia"), |
7cac9316 | 346 | not(target_os = "emscripten"), |
041b39d2 XL |
347 | not(target_os = "macos"), |
348 | not(target_os = "ios")))] { | |
7cac9316 XL |
349 | mod libbacktrace; |
350 | use self::libbacktrace::resolve as resolve_imp; | |
351 | use self::libbacktrace::Symbol as SymbolImp; | |
83c7162d XL |
352 | |
353 | // Note that we only enable coresymbolication on iOS when debug assertions | |
354 | // are enabled because it's helpful in debug mode but it looks like apps get | |
355 | // rejected from the app store if they use this API, see #92 for more info | |
7cac9316 | 356 | } else if #[cfg(all(feature = "coresymbolication", |
041b39d2 | 357 | any(target_os = "macos", |
83c7162d | 358 | all(target_os = "ios", debug_assertions))))] { |
7cac9316 XL |
359 | mod coresymbolication; |
360 | use self::coresymbolication::resolve as resolve_imp; | |
361 | use self::coresymbolication::Symbol as SymbolImp; | |
362 | } else if #[cfg(all(unix, | |
363 | not(target_os = "emscripten"), | |
364 | feature = "dladdr"))] { | |
365 | mod dladdr; | |
366 | use self::dladdr::resolve as resolve_imp; | |
367 | use self::dladdr::Symbol as SymbolImp; | |
368 | } else { | |
369 | mod noop; | |
370 | use self::noop::resolve as resolve_imp; | |
371 | use self::noop::Symbol as SymbolImp; | |
372 | } | |
373 | } |