backtrace/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
//! A library for acquiring a backtrace at runtime
//!
//! This library is meant to supplement the `RUST_BACKTRACE=1` support of the
//! standard library by allowing an acquisition of a backtrace at runtime
//! programmatically. The backtraces generated by this library do not need to be
//! parsed, for example, and expose the functionality of multiple backend
//! implementations.
//!
//! # Usage
//!
//! First, add this to your Cargo.toml
//!
//! ```toml
//! [dependencies]
//! backtrace = "0.3"
//! ```
//!
//! Next:
//!
//! ```
//! fn main() {
//! # // Unsafe here so test passes on no_std.
//! # #[cfg(feature = "std")] {
//!     backtrace::trace(|frame| {
//!         let ip = frame.ip();
//!         let symbol_address = frame.symbol_address();
//!
//!         // Resolve this instruction pointer to a symbol name
//!         backtrace::resolve_frame(frame, |symbol| {
//!             if let Some(name) = symbol.name() {
//!                 // ...
//!             }
//!             if let Some(filename) = symbol.filename() {
//!                 // ...
//!             }
//!         });
//!
//!         true // keep going to the next frame
//!     });
//! }
//! # }
//! ```
//!
//! # Backtrace accuracy
//!
//! This crate implements best-effort attempts to get the native backtrace. This
//! is not always guaranteed to work, and some platforms don't return any
//! backtrace at all. If your application requires accurate backtraces then it's
//! recommended to closely evaluate this crate to see whether it's suitable
//! for your use case on your target platforms.
//!
//! Even on supported platforms, there's a number of reasons that backtraces may
//! be less-than-accurate, including but not limited to:
//!
//! * Unwind information may not be available. This crate primarily implements
//!   backtraces by unwinding the stack, but not all functions may have
//!   unwinding information (e.g. DWARF unwinding information).
//!
//! * Rust code may be compiled without unwinding information for some
//!   functions. This can also happen for Rust code compiled with
//!   `-Cpanic=abort`. You can remedy this, however, with
//!   `-Cforce-unwind-tables` as a compiler option.
//!
//! * Unwind information may be inaccurate or corrupt. In the worst case
//!   inaccurate unwind information can lead this library to segfault. In the
//!   best case inaccurate information will result in a truncated stack trace.
//!
//! * Backtraces may not report filenames/line numbers correctly due to missing
//!   or corrupt debug information. This won't lead to segfaults unlike corrupt
//!   unwinding information, but missing or malformed debug information will
//!   mean that filenames and line numbers will not be available. This may be
//!   because debug information wasn't generated by the compiler, or it's just
//!   missing on the filesystem.
//!
//! * Not all platforms are supported. For example there's no way to get a
//!   backtrace on WebAssembly at the moment.
//!
//! * Crate features may be disabled. Currently this crate supports using Gimli
//!   libbacktrace on non-Windows platforms for reading debuginfo for
//!   backtraces. If both crate features are disabled, however, then these
//!   platforms will generate a backtrace but be unable to generate symbols for
//!   it.
//!
//! In most standard workflows for most standard platforms you generally don't
//! need to worry about these caveats. We'll try to fix ones where we can over
//! time, but otherwise it's important to be aware of the limitations of
//! unwinding-based backtraces!

#![deny(missing_docs)]
#![no_std]
#![cfg_attr(
    all(feature = "std", target_env = "sgx", target_vendor = "fortanix"),
    feature(sgx_platform)
)]
#![warn(rust_2018_idioms)]
// When we're building as part of libstd, silence all warnings since they're
// irrelevant as this crate is developed out-of-tree.
#![cfg_attr(backtrace_in_libstd, allow(warnings))]
#![cfg_attr(not(feature = "std"), allow(dead_code))]

#[cfg(feature = "std")]
#[macro_use]
extern crate std;

// This is only used for gimli right now, which is only used on some platforms, and miri
// so don't worry if it's unused in other configurations.
#[allow(unused_extern_crates)]
extern crate alloc;

pub use self::backtrace::{trace_unsynchronized, Frame};
mod backtrace;

pub use self::symbolize::resolve_frame_unsynchronized;
pub use self::symbolize::{resolve_unsynchronized, Symbol, SymbolName};
mod symbolize;

pub use self::types::BytesOrWideString;
mod types;

#[cfg(feature = "std")]
pub use self::symbolize::clear_symbol_cache;

mod print;
pub use print::{BacktraceFmt, BacktraceFrameFmt, PrintFmt};

cfg_if::cfg_if! {
    if #[cfg(feature = "std")] {
        pub use self::backtrace::trace;
        pub use self::symbolize::{resolve, resolve_frame};
        pub use self::capture::{Backtrace, BacktraceFrame, BacktraceSymbol};
        mod capture;
    }
}

cfg_if::cfg_if! {
    if #[cfg(all(target_env = "sgx", target_vendor = "fortanix", not(feature = "std")))] {
        pub use self::backtrace::set_image_base;
    }
}

#[cfg(feature = "std")]
mod lock {
    use std::boxed::Box;
    use std::cell::Cell;
    use std::ptr;
    use std::sync::{Mutex, MutexGuard, Once};

    /// A "Maybe" LockGuard
    pub struct LockGuard(Option<MutexGuard<'static, ()>>);

    /// The global lock, lazily allocated on first use
    static mut LOCK: *mut Mutex<()> = ptr::null_mut();
    static INIT: Once = Once::new();
    // Whether this thread is the one that holds the lock
    thread_local!(static LOCK_HELD: Cell<bool> = Cell::new(false));

    impl Drop for LockGuard {
        fn drop(&mut self) {
            // Don't do anything if we're a LockGuard(None)
            if self.0.is_some() {
                LOCK_HELD.with(|slot| {
                    // Immediately crash if we somehow aren't the thread holding this lock
                    assert!(slot.get());
                    // We are no longer the thread holding this lock
                    slot.set(false);
                });
            }
            // lock implicitly released here, if we're a LockGuard(Some(..))
        }
    }

    /// Acquire a partially unsound(!!!) global re-entrant lock over
    /// backtrace's internals.
    ///
    /// That is, this lock can be acquired as many times as you want
    /// on a single thread without deadlocking, allowing one thread
    /// to acquire exclusive access to the ability to make backtraces.
    /// Calls to this locking function are freely sprinkled in every place
    /// where that needs to be enforced.
    ///
    ///
    /// # Why
    ///
    /// This was first introduced to guard uses of Windows' dbghelp API,
    /// which isn't threadsafe. It's unclear if other things now rely on
    /// this locking.
    ///
    ///
    /// # How
    ///
    /// The basic idea is to have a single global mutex, and a thread_local
    /// boolean saying "yep this is the thread that acquired the mutex".
    ///
    /// The first time a thread acquires the lock, it is handed a
    /// `LockGuard(Some(..))` that will actually release the lock on Drop.
    /// All subsequence attempts to lock on the same thread will see
    /// that their thread acquired the lock, and get `LockGuard(None)`
    /// which will do nothing when dropped.
    ///
    ///
    /// # Safety
    ///
    /// As long as you only ever assign the returned LockGuard to a freshly
    /// declared local variable, it will do its job correctly, as the "first"
    /// LockGuard will strictly outlive all subsequent LockGuards and
    /// properly release the lock when the thread is done with backtracing.
    ///
    /// However if you ever attempt to store a LockGuard beyond the scope
    /// it was acquired in, it might actually be a `LockGuard(None)` that
    /// doesn't actually hold the lock! In this case another thread might
    /// acquire the lock and you'll get races this system was intended to
    /// avoid!
    ///
    /// This is why this is "partially unsound". As a public API this would
    /// be unacceptable, but this is crate-private, and if you use this in
    /// the most obvious and simplistic way it Just Works™.
    ///
    /// Note however that std specifically bypasses this lock, and uses
    /// the `*_unsynchronized` backtrace APIs. This is "fine" because
    /// it wraps its own calls to backtrace in a non-reentrant Mutex
    /// that prevents two backtraces from getting interleaved during printing.
    pub fn lock() -> LockGuard {
        // If we're the thread holding this lock, pretend to acquire the lock
        // again by returning a LockGuard(None)
        if LOCK_HELD.with(|l| l.get()) {
            return LockGuard(None);
        }
        // Insist that we totally are the thread holding the lock
        // (our thread will block until we are)
        LOCK_HELD.with(|s| s.set(true));
        unsafe {
            // lazily allocate the lock if necessary
            INIT.call_once(|| {
                LOCK = Box::into_raw(Box::new(Mutex::new(())));
            });
            // ok *actually* try to acquire the lock, blocking as necessary
            LockGuard(Some((*LOCK).lock().unwrap()))
        }
    }
}

#[cfg(all(
    windows,
    any(
        target_env = "msvc",
        all(target_env = "gnu", any(target_arch = "x86", target_arch = "arm"))
    ),
    not(target_vendor = "uwp")
))]
mod dbghelp;
// Auto-generated by windows-bindgen/riddle
#[cfg(windows)]
mod windows_sys;