backtrace/lib.rs
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]
103extern 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)]
108extern crate alloc;
109
110pub use self::backtrace::{trace_unsynchronized, Frame};
111mod backtrace;
112
113pub use self::symbolize::resolve_frame_unsynchronized;
114pub use self::symbolize::{resolve_unsynchronized, Symbol, SymbolName};
115mod symbolize;
116
117pub use self::types::BytesOrWideString;
118mod types;
119
120#[cfg(feature = "std")]
121pub use self::symbolize::clear_symbol_cache;
122
123mod print;
124pub use print::{BacktraceFmt, BacktraceFrameFmt, PrintFmt};
125
126cfg_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
135cfg_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#[cfg(feature = "std")]
142mod lock {
143 use std::boxed::Box;
144 use std::cell::Cell;
145 use std::ptr;
146 use std::sync::{Mutex, MutexGuard, Once};
147
148 /// A "Maybe" LockGuard
149 pub struct LockGuard(Option<MutexGuard<'static, ()>>);
150
151 /// The global lock, lazily allocated on first use
152 static mut LOCK: *mut Mutex<()> = ptr::null_mut();
153 static INIT: Once = Once::new();
154 // Whether this thread is the one that holds the lock
155 thread_local!(static LOCK_HELD: Cell<bool> = Cell::new(false));
156
157 impl Drop for LockGuard {
158 fn drop(&mut self) {
159 // Don't do anything if we're a LockGuard(None)
160 if self.0.is_some() {
161 LOCK_HELD.with(|slot| {
162 // Immediately crash if we somehow aren't the thread holding this lock
163 assert!(slot.get());
164 // We are no longer the thread holding this lock
165 slot.set(false);
166 });
167 }
168 // lock implicitly released here, if we're a LockGuard(Some(..))
169 }
170 }
171
172 /// Acquire a partially unsound(!!!) global re-entrant lock over
173 /// backtrace's internals.
174 ///
175 /// That is, this lock can be acquired as many times as you want
176 /// on a single thread without deadlocking, allowing one thread
177 /// to acquire exclusive access to the ability to make backtraces.
178 /// Calls to this locking function are freely sprinkled in every place
179 /// where that needs to be enforced.
180 ///
181 ///
182 /// # Why
183 ///
184 /// This was first introduced to guard uses of Windows' dbghelp API,
185 /// which isn't threadsafe. It's unclear if other things now rely on
186 /// this locking.
187 ///
188 ///
189 /// # How
190 ///
191 /// The basic idea is to have a single global mutex, and a thread_local
192 /// boolean saying "yep this is the thread that acquired the mutex".
193 ///
194 /// The first time a thread acquires the lock, it is handed a
195 /// `LockGuard(Some(..))` that will actually release the lock on Drop.
196 /// All subsequence attempts to lock on the same thread will see
197 /// that their thread acquired the lock, and get `LockGuard(None)`
198 /// which will do nothing when dropped.
199 ///
200 ///
201 /// # Safety
202 ///
203 /// As long as you only ever assign the returned LockGuard to a freshly
204 /// declared local variable, it will do its job correctly, as the "first"
205 /// LockGuard will strictly outlive all subsequent LockGuards and
206 /// properly release the lock when the thread is done with backtracing.
207 ///
208 /// However if you ever attempt to store a LockGuard beyond the scope
209 /// it was acquired in, it might actually be a `LockGuard(None)` that
210 /// doesn't actually hold the lock! In this case another thread might
211 /// acquire the lock and you'll get races this system was intended to
212 /// avoid!
213 ///
214 /// This is why this is "partially unsound". As a public API this would
215 /// be unacceptable, but this is crate-private, and if you use this in
216 /// the most obvious and simplistic way it Just Works™.
217 ///
218 /// Note however that std specifically bypasses this lock, and uses
219 /// the `*_unsynchronized` backtrace APIs. This is "fine" because
220 /// it wraps its own calls to backtrace in a non-reentrant Mutex
221 /// that prevents two backtraces from getting interleaved during printing.
222 pub fn lock() -> LockGuard {
223 // If we're the thread holding this lock, pretend to acquire the lock
224 // again by returning a LockGuard(None)
225 if LOCK_HELD.with(|l| l.get()) {
226 return LockGuard(None);
227 }
228 // Insist that we totally are the thread holding the lock
229 // (our thread will block until we are)
230 LOCK_HELD.with(|s| s.set(true));
231 unsafe {
232 // lazily allocate the lock if necessary
233 INIT.call_once(|| {
234 LOCK = Box::into_raw(Box::new(Mutex::new(())));
235 });
236 // ok *actually* try to acquire the lock, blocking as necessary
237 LockGuard(Some((*LOCK).lock().unwrap()))
238 }
239 }
240}
241
242#[cfg(all(
243 windows,
244 any(
245 target_env = "msvc",
246 all(target_env = "gnu", any(target_arch = "x86", target_arch = "arm"))
247 ),
248 not(target_vendor = "uwp")
249))]
250mod dbghelp;
251// Auto-generated by windows-bindgen/riddle
252#[cfg(windows)]
253mod windows_sys;