std/backtrace/src/
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//! # // 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]
101extern 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)]
106extern crate alloc;
107
108pub use self::backtrace::{trace_unsynchronized, Frame};
109mod backtrace;
110
111pub use self::symbolize::resolve_frame_unsynchronized;
112pub use self::symbolize::{resolve_unsynchronized, Symbol, SymbolName};
113mod symbolize;
114
115pub use self::types::BytesOrWideString;
116mod types;
117
118#[cfg(feature = "std")]
119pub use self::symbolize::clear_symbol_cache;
120
121mod print;
122pub use print::{BacktraceFmt, BacktraceFrameFmt, PrintFmt};
123
124cfg_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
133cfg_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")]
140mod 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))]
248mod dbghelp;
249// Auto-generated by windows-bindgen/riddle
250#[cfg(windows)]
251mod windows_sys;