1 | //! A library for acquiring a backtrace at runtime |
2 | //! |
3 | //! This library is meant to supplement the `RUST_BACKTRACE=1` support of the |
4 | //! standard library by allowing an acquisition of a backtrace at runtime |
5 | //! programmatically. The backtraces generated by this library do not need to be |
6 | //! parsed, for example, and expose the functionality of multiple backend |
7 | //! implementations. |
8 | //! |
9 | //! # Usage |
10 | //! |
11 | //! First, add this to your Cargo.toml |
12 | //! |
13 | //! ```toml |
14 | //! [dependencies] |
15 | //! backtrace = "0.3" |
16 | //! ``` |
17 | //! |
18 | //! Next: |
19 | //! |
20 | //! ``` |
21 | //! fn main() { |
22 | //! # // Unsafe here so test passes on no_std. |
23 | //! # #[cfg (feature = "std" )] { |
24 | //! backtrace::trace(|frame| { |
25 | //! let ip = frame.ip(); |
26 | //! let symbol_address = frame.symbol_address(); |
27 | //! |
28 | //! // Resolve this instruction pointer to a symbol name |
29 | //! backtrace::resolve_frame(frame, |symbol| { |
30 | //! if let Some(name) = symbol.name() { |
31 | //! // ... |
32 | //! } |
33 | //! if let Some(filename) = symbol.filename() { |
34 | //! // ... |
35 | //! } |
36 | //! }); |
37 | //! |
38 | //! true // keep going to the next frame |
39 | //! }); |
40 | //! } |
41 | //! # } |
42 | //! ``` |
43 | //! |
44 | //! # Backtrace accuracy |
45 | //! |
46 | //! This crate implements best-effort attempts to get the native backtrace. This |
47 | //! is not always guaranteed to work, and some platforms don't return any |
48 | //! backtrace at all. If your application requires accurate backtraces then it's |
49 | //! recommended to closely evaluate this crate to see whether it's suitable |
50 | //! for your use case on your target platforms. |
51 | //! |
52 | //! Even on supported platforms, there's a number of reasons that backtraces may |
53 | //! be less-than-accurate, including but not limited to: |
54 | //! |
55 | //! * Unwind information may not be available. This crate primarily implements |
56 | //! backtraces by unwinding the stack, but not all functions may have |
57 | //! unwinding information (e.g. DWARF unwinding information). |
58 | //! |
59 | //! * Rust code may be compiled without unwinding information for some |
60 | //! functions. This can also happen for Rust code compiled with |
61 | //! `-Cpanic=abort`. You can remedy this, however, with |
62 | //! `-Cforce-unwind-tables` as a compiler option. |
63 | //! |
64 | //! * Unwind information may be inaccurate or corrupt. In the worst case |
65 | //! inaccurate unwind information can lead this library to segfault. In the |
66 | //! best case inaccurate information will result in a truncated stack trace. |
67 | //! |
68 | //! * Backtraces may not report filenames/line numbers correctly due to missing |
69 | //! or corrupt debug information. This won't lead to segfaults unlike corrupt |
70 | //! unwinding information, but missing or malformed debug information will |
71 | //! mean that filenames and line numbers will not be available. This may be |
72 | //! because debug information wasn't generated by the compiler, or it's just |
73 | //! missing on the filesystem. |
74 | //! |
75 | //! * Not all platforms are supported. For example there's no way to get a |
76 | //! backtrace on WebAssembly at the moment. |
77 | //! |
78 | //! * Crate features may be disabled. Currently this crate supports using Gimli |
79 | //! libbacktrace on non-Windows platforms for reading debuginfo for |
80 | //! backtraces. If both crate features are disabled, however, then these |
81 | //! platforms will generate a backtrace but be unable to generate symbols for |
82 | //! it. |
83 | //! |
84 | //! In most standard workflows for most standard platforms you generally don't |
85 | //! need to worry about these caveats. We'll try to fix ones where we can over |
86 | //! time, but otherwise it's important to be aware of the limitations of |
87 | //! unwinding-based backtraces! |
88 | |
89 | #![deny (missing_docs)] |
90 | #![no_std ] |
91 | #![cfg_attr ( |
92 | all(feature = "std" , target_env = "sgx" , target_vendor = "fortanix" ), |
93 | feature(sgx_platform) |
94 | )] |
95 | #![warn (rust_2018_idioms)] |
96 | // When we're building as part of libstd, silence all warnings since they're |
97 | // irrelevant as this crate is developed out-of-tree. |
98 | #![cfg_attr (backtrace_in_libstd, allow(warnings))] |
99 | #![cfg_attr (not(feature = "std" ), allow(dead_code))] |
100 | |
101 | #[cfg (feature = "std" )] |
102 | #[macro_use ] |
103 | extern crate std; |
104 | |
105 | // This is only used for gimli right now, which is only used on some platforms, and miri |
106 | // so don't worry if it's unused in other configurations. |
107 | #[allow (unused_extern_crates)] |
108 | extern crate alloc; |
109 | |
110 | pub use self::backtrace::{trace_unsynchronized, Frame}; |
111 | mod backtrace; |
112 | |
113 | pub use self::symbolize::resolve_frame_unsynchronized; |
114 | pub use self::symbolize::{resolve_unsynchronized, Symbol, SymbolName}; |
115 | mod symbolize; |
116 | |
117 | pub use self::types::BytesOrWideString; |
118 | mod types; |
119 | |
120 | #[cfg (feature = "std" )] |
121 | pub use self::symbolize::clear_symbol_cache; |
122 | |
123 | mod print; |
124 | pub use print::{BacktraceFmt, BacktraceFrameFmt, PrintFmt}; |
125 | |
126 | cfg_if::cfg_if! { |
127 | if #[cfg(feature = "std" )] { |
128 | pub use self::backtrace::trace; |
129 | pub use self::symbolize::{resolve, resolve_frame}; |
130 | pub use self::capture::{Backtrace, BacktraceFrame, BacktraceSymbol}; |
131 | mod capture; |
132 | } |
133 | } |
134 | |
135 | cfg_if::cfg_if! { |
136 | if #[cfg(all(target_env = "sgx" , target_vendor = "fortanix" , not(feature = "std" )))] { |
137 | pub use self::backtrace::set_image_base; |
138 | } |
139 | } |
140 | |
141 | #[allow (dead_code)] |
142 | struct Bomb { |
143 | enabled: bool, |
144 | } |
145 | |
146 | #[allow (dead_code)] |
147 | impl Drop for Bomb { |
148 | fn drop(&mut self) { |
149 | if self.enabled { |
150 | panic!("cannot panic during the backtrace function" ); |
151 | } |
152 | } |
153 | } |
154 | |
155 | #[allow (dead_code)] |
156 | #[cfg (feature = "std" )] |
157 | mod lock { |
158 | use std::boxed::Box; |
159 | use std::cell::Cell; |
160 | use std::ptr; |
161 | use std::sync::{Mutex, MutexGuard, Once}; |
162 | |
163 | pub struct LockGuard(Option<MutexGuard<'static, ()>>); |
164 | |
165 | static mut LOCK: *mut Mutex<()> = ptr::null_mut(); |
166 | static INIT: Once = Once::new(); |
167 | thread_local!(static LOCK_HELD: Cell<bool> = Cell::new(false)); |
168 | |
169 | impl Drop for LockGuard { |
170 | fn drop(&mut self) { |
171 | if self.0.is_some() { |
172 | LOCK_HELD.with(|slot| { |
173 | assert!(slot.get()); |
174 | slot.set(false); |
175 | }); |
176 | } |
177 | } |
178 | } |
179 | |
180 | pub fn lock() -> LockGuard { |
181 | if LOCK_HELD.with(|l| l.get()) { |
182 | return LockGuard(None); |
183 | } |
184 | LOCK_HELD.with(|s| s.set(true)); |
185 | unsafe { |
186 | INIT.call_once(|| { |
187 | LOCK = Box::into_raw(Box::new(Mutex::new(()))); |
188 | }); |
189 | LockGuard(Some((*LOCK).lock().unwrap())) |
190 | } |
191 | } |
192 | } |
193 | |
194 | #[cfg (all( |
195 | windows, |
196 | any( |
197 | target_env = "msvc" , |
198 | all(target_env = "gnu" , any(target_arch = "x86" , target_arch = "arm" )) |
199 | ), |
200 | not(target_vendor = "uwp" ) |
201 | ))] |
202 | mod dbghelp; |
203 | #[cfg (windows)] |
204 | mod windows; |
205 | |