| 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 | //! # // Unsafe here so test passes on no_std. |
| 22 | //! # #[cfg (feature = "std" )] { |
| 23 | //! backtrace::trace(|frame| { |
| 24 | //! let ip = frame.ip(); |
| 25 | //! let symbol_address = frame.symbol_address(); |
| 26 | //! |
| 27 | //! // Resolve this instruction pointer to a symbol name |
| 28 | //! backtrace::resolve_frame(frame, |symbol| { |
| 29 | //! if let Some(name) = symbol.name() { |
| 30 | //! // ... |
| 31 | //! } |
| 32 | //! if let Some(filename) = symbol.filename() { |
| 33 | //! // ... |
| 34 | //! } |
| 35 | //! }); |
| 36 | //! |
| 37 | //! true // keep going to the next frame |
| 38 | //! }); |
| 39 | //! # } |
| 40 | //! ``` |
| 41 | //! |
| 42 | //! # Backtrace accuracy |
| 43 | //! |
| 44 | //! This crate implements best-effort attempts to get the native backtrace. This |
| 45 | //! is not always guaranteed to work, and some platforms don't return any |
| 46 | //! backtrace at all. If your application requires accurate backtraces then it's |
| 47 | //! recommended to closely evaluate this crate to see whether it's suitable |
| 48 | //! for your use case on your target platforms. |
| 49 | //! |
| 50 | //! Even on supported platforms, there's a number of reasons that backtraces may |
| 51 | //! be less-than-accurate, including but not limited to: |
| 52 | //! |
| 53 | //! * Unwind information may not be available. This crate primarily implements |
| 54 | //! backtraces by unwinding the stack, but not all functions may have |
| 55 | //! unwinding information (e.g. DWARF unwinding information). |
| 56 | //! |
| 57 | //! * Rust code may be compiled without unwinding information for some |
| 58 | //! functions. This can also happen for Rust code compiled with |
| 59 | //! `-Cpanic=abort`. You can remedy this, however, with |
| 60 | //! `-Cforce-unwind-tables` as a compiler option. |
| 61 | //! |
| 62 | //! * Unwind information may be inaccurate or corrupt. In the worst case |
| 63 | //! inaccurate unwind information can lead this library to segfault. In the |
| 64 | //! best case inaccurate information will result in a truncated stack trace. |
| 65 | //! |
| 66 | //! * Backtraces may not report filenames/line numbers correctly due to missing |
| 67 | //! or corrupt debug information. This won't lead to segfaults unlike corrupt |
| 68 | //! unwinding information, but missing or malformed debug information will |
| 69 | //! mean that filenames and line numbers will not be available. This may be |
| 70 | //! because debug information wasn't generated by the compiler, or it's just |
| 71 | //! missing on the filesystem. |
| 72 | //! |
| 73 | //! * Not all platforms are supported. For example there's no way to get a |
| 74 | //! backtrace on WebAssembly at the moment. |
| 75 | //! |
| 76 | //! * Crate features may be disabled. Currently this crate supports using Gimli |
| 77 | //! libbacktrace on non-Windows platforms for reading debuginfo for |
| 78 | //! backtraces. If both crate features are disabled, however, then these |
| 79 | //! platforms will generate a backtrace but be unable to generate symbols for |
| 80 | //! it. |
| 81 | //! |
| 82 | //! In most standard workflows for most standard platforms you generally don't |
| 83 | //! need to worry about these caveats. We'll try to fix ones where we can over |
| 84 | //! time, but otherwise it's important to be aware of the limitations of |
| 85 | //! unwinding-based backtraces! |
| 86 | |
| 87 | #![deny (missing_docs)] |
| 88 | #![no_std ] |
| 89 | #![cfg_attr ( |
| 90 | all(feature = "std" , target_env = "sgx" , target_vendor = "fortanix" ), |
| 91 | feature(sgx_platform) |
| 92 | )] |
| 93 | #![warn (rust_2018_idioms)] |
| 94 | // When we're building as part of libstd, silence all warnings since they're |
| 95 | // irrelevant as this crate is developed out-of-tree. |
| 96 | #![cfg_attr (backtrace_in_libstd, allow(warnings))] |
| 97 | #![cfg_attr (not(feature = "std" ), allow(dead_code))] |
| 98 | |
| 99 | #[cfg (feature = "std" )] |
| 100 | #[macro_use ] |
| 101 | extern crate std; |
| 102 | |
| 103 | // This is only used for gimli right now, which is only used on some platforms, and miri |
| 104 | // so don't worry if it's unused in other configurations. |
| 105 | #[allow (unused_extern_crates)] |
| 106 | extern crate alloc; |
| 107 | |
| 108 | pub use self::backtrace::{trace_unsynchronized, Frame}; |
| 109 | mod backtrace; |
| 110 | |
| 111 | pub use self::symbolize::resolve_frame_unsynchronized; |
| 112 | pub use self::symbolize::{resolve_unsynchronized, Symbol, SymbolName}; |
| 113 | mod symbolize; |
| 114 | |
| 115 | pub use self::types::BytesOrWideString; |
| 116 | mod types; |
| 117 | |
| 118 | #[cfg (feature = "std" )] |
| 119 | pub use self::symbolize::clear_symbol_cache; |
| 120 | |
| 121 | mod print; |
| 122 | pub use print::{BacktraceFmt, BacktraceFrameFmt, PrintFmt}; |
| 123 | |
| 124 | cfg_if::cfg_if! { |
| 125 | if #[cfg(feature = "std" )] { |
| 126 | pub use self::backtrace::trace; |
| 127 | pub use self::symbolize::{resolve, resolve_frame}; |
| 128 | pub use self::capture::{Backtrace, BacktraceFrame, BacktraceSymbol}; |
| 129 | mod capture; |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | cfg_if::cfg_if! { |
| 134 | if #[cfg(all(target_env = "sgx" , target_vendor = "fortanix" , not(feature = "std" )))] { |
| 135 | pub use self::backtrace::set_image_base; |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | #[cfg (feature = "std" )] |
| 140 | mod lock { |
| 141 | use std::boxed::Box; |
| 142 | use std::cell::Cell; |
| 143 | use std::ptr; |
| 144 | use std::sync::{Mutex, MutexGuard, Once}; |
| 145 | |
| 146 | /// A "Maybe" LockGuard |
| 147 | pub struct LockGuard(Option<MutexGuard<'static, ()>>); |
| 148 | |
| 149 | /// The global lock, lazily allocated on first use |
| 150 | static mut LOCK: *mut Mutex<()> = ptr::null_mut(); |
| 151 | static INIT: Once = Once::new(); |
| 152 | // Whether this thread is the one that holds the lock |
| 153 | thread_local!(static LOCK_HELD: Cell<bool> = Cell::new(false)); |
| 154 | |
| 155 | impl Drop for LockGuard { |
| 156 | fn drop(&mut self) { |
| 157 | // Don't do anything if we're a LockGuard(None) |
| 158 | if self.0.is_some() { |
| 159 | LOCK_HELD.with(|slot| { |
| 160 | // Immediately crash if we somehow aren't the thread holding this lock |
| 161 | assert!(slot.get()); |
| 162 | // We are no longer the thread holding this lock |
| 163 | slot.set(false); |
| 164 | }); |
| 165 | } |
| 166 | // lock implicitly released here, if we're a LockGuard(Some(..)) |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | /// Acquire a partially unsound(!!!) global re-entrant lock over |
| 171 | /// backtrace's internals. |
| 172 | /// |
| 173 | /// That is, this lock can be acquired as many times as you want |
| 174 | /// on a single thread without deadlocking, allowing one thread |
| 175 | /// to acquire exclusive access to the ability to make backtraces. |
| 176 | /// Calls to this locking function are freely sprinkled in every place |
| 177 | /// where that needs to be enforced. |
| 178 | /// |
| 179 | /// |
| 180 | /// # Why |
| 181 | /// |
| 182 | /// This was first introduced to guard uses of Windows' dbghelp API, |
| 183 | /// which isn't threadsafe. It's unclear if other things now rely on |
| 184 | /// this locking. |
| 185 | /// |
| 186 | /// |
| 187 | /// # How |
| 188 | /// |
| 189 | /// The basic idea is to have a single global mutex, and a thread_local |
| 190 | /// boolean saying "yep this is the thread that acquired the mutex". |
| 191 | /// |
| 192 | /// The first time a thread acquires the lock, it is handed a |
| 193 | /// `LockGuard(Some(..))` that will actually release the lock on Drop. |
| 194 | /// All subsequence attempts to lock on the same thread will see |
| 195 | /// that their thread acquired the lock, and get `LockGuard(None)` |
| 196 | /// which will do nothing when dropped. |
| 197 | /// |
| 198 | /// |
| 199 | /// # Safety |
| 200 | /// |
| 201 | /// As long as you only ever assign the returned LockGuard to a freshly |
| 202 | /// declared local variable, it will do its job correctly, as the "first" |
| 203 | /// LockGuard will strictly outlive all subsequent LockGuards and |
| 204 | /// properly release the lock when the thread is done with backtracing. |
| 205 | /// |
| 206 | /// However if you ever attempt to store a LockGuard beyond the scope |
| 207 | /// it was acquired in, it might actually be a `LockGuard(None)` that |
| 208 | /// doesn't actually hold the lock! In this case another thread might |
| 209 | /// acquire the lock and you'll get races this system was intended to |
| 210 | /// avoid! |
| 211 | /// |
| 212 | /// This is why this is "partially unsound". As a public API this would |
| 213 | /// be unacceptable, but this is crate-private, and if you use this in |
| 214 | /// the most obvious and simplistic way it Just Works™. |
| 215 | /// |
| 216 | /// Note however that std specifically bypasses this lock, and uses |
| 217 | /// the `*_unsynchronized` backtrace APIs. This is "fine" because |
| 218 | /// it wraps its own calls to backtrace in a non-reentrant Mutex |
| 219 | /// that prevents two backtraces from getting interleaved during printing. |
| 220 | pub fn lock() -> LockGuard { |
| 221 | // If we're the thread holding this lock, pretend to acquire the lock |
| 222 | // again by returning a LockGuard(None) |
| 223 | if LOCK_HELD.with(|l| l.get()) { |
| 224 | return LockGuard(None); |
| 225 | } |
| 226 | // Insist that we totally are the thread holding the lock |
| 227 | // (our thread will block until we are) |
| 228 | LOCK_HELD.with(|s| s.set(true)); |
| 229 | unsafe { |
| 230 | // lazily allocate the lock if necessary |
| 231 | INIT.call_once(|| { |
| 232 | LOCK = Box::into_raw(Box::new(Mutex::new(()))); |
| 233 | }); |
| 234 | // ok *actually* try to acquire the lock, blocking as necessary |
| 235 | LockGuard(Some((*LOCK).lock().unwrap())) |
| 236 | } |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | #[cfg (all( |
| 241 | windows, |
| 242 | any( |
| 243 | target_env = "msvc" , |
| 244 | all(target_env = "gnu" , any(target_arch = "x86" , target_arch = "arm" )) |
| 245 | ), |
| 246 | not(target_vendor = "uwp" ) |
| 247 | ))] |
| 248 | mod dbghelp; |
| 249 | // Auto-generated by windows-bindgen/riddle |
| 250 | #[cfg (windows)] |
| 251 | mod windows_sys; |
| 252 | |