backtrace/backtrace/
mod.rs

1use core::ffi::c_void;
2use core::fmt;
3
4/// Inspects the current call-stack, passing all active frames into the closure
5/// provided to calculate a stack trace.
6///
7/// This function is the workhorse of this library in calculating the stack
8/// traces for a program. The given closure `cb` is yielded instances of a
9/// `Frame` which represent information about that call frame on the stack. The
10/// closure is yielded frames in a top-down fashion (most recently called
11/// functions first).
12///
13/// The closure's return value is an indication of whether the backtrace should
14/// continue. A return value of `false` will terminate the backtrace and return
15/// immediately.
16///
17/// Once a `Frame` is acquired you will likely want to call `backtrace::resolve`
18/// to convert the `ip` (instruction pointer) or symbol address to a `Symbol`
19/// through which the name and/or filename/line number can be learned.
20///
21/// Note that this is a relatively low-level function and if you'd like to, for
22/// example, capture a backtrace to be inspected later, then the `Backtrace`
23/// type may be more appropriate.
24///
25/// # Required features
26///
27/// This function requires the `std` feature of the `backtrace` crate to be
28/// enabled, and the `std` feature is enabled by default.
29///
30/// # Panics
31///
32/// This function strives to never panic, but if the `cb` provided panics then
33/// some platforms will force a double panic to abort the process. Some
34/// platforms use a C library which internally uses callbacks which cannot be
35/// unwound through, so panicking from `cb` may trigger a process abort.
36///
37/// # Example
38///
39/// ```
40/// extern crate backtrace;
41///
42/// fn main() {
43///     backtrace::trace(|frame| {
44///         // ...
45///
46///         true // continue the backtrace
47///     });
48/// }
49/// ```
50#[cfg(feature = "std")]
51pub fn trace<F: FnMut(&Frame) -> bool>(cb: F) {
52    let _guard = crate::lock::lock();
53    unsafe { trace_unsynchronized(cb) }
54}
55
56/// Same as `trace`, only unsafe as it's unsynchronized.
57///
58/// This function does not have synchronization guarantees but is available
59/// when the `std` feature of this crate isn't compiled in. See the `trace`
60/// function for more documentation and examples.
61///
62/// # Panics
63///
64/// See information on `trace` for caveats on `cb` panicking.
65pub unsafe fn trace_unsynchronized<F: FnMut(&Frame) -> bool>(mut cb: F) {
66    trace_imp(&mut cb)
67}
68
69/// A trait representing one frame of a backtrace, yielded to the `trace`
70/// function of this crate.
71///
72/// The tracing function's closure will be yielded frames, and the frame is
73/// virtually dispatched as the underlying implementation is not always known
74/// until runtime.
75#[derive(Clone)]
76pub struct Frame {
77    pub(crate) inner: FrameImp,
78}
79
80impl Frame {
81    /// Returns the current instruction pointer of this frame.
82    ///
83    /// This is normally the next instruction to execute in the frame, but not
84    /// all implementations list this with 100% accuracy (but it's generally
85    /// pretty close).
86    ///
87    /// It is recommended to pass this value to `backtrace::resolve` to turn it
88    /// into a symbol name.
89    pub fn ip(&self) -> *mut c_void {
90        self.inner.ip()
91    }
92
93    /// Returns the current stack pointer of this frame.
94    ///
95    /// In the case that a backend cannot recover the stack pointer for this
96    /// frame, a null pointer is returned.
97    pub fn sp(&self) -> *mut c_void {
98        self.inner.sp()
99    }
100
101    /// Returns the starting symbol address of the frame of this function.
102    ///
103    /// This will attempt to rewind the instruction pointer returned by `ip` to
104    /// the start of the function, returning that value. In some cases, however,
105    /// backends will just return `ip` from this function.
106    ///
107    /// The returned value can sometimes be used if `backtrace::resolve` failed
108    /// on the `ip` given above.
109    pub fn symbol_address(&self) -> *mut c_void {
110        self.inner.symbol_address()
111    }
112
113    /// Returns the base address of the module to which the frame belongs.
114    pub fn module_base_address(&self) -> Option<*mut c_void> {
115        self.inner.module_base_address()
116    }
117}
118
119impl fmt::Debug for Frame {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        f.debug_struct("Frame")
122            .field("ip", &self.ip())
123            .field("symbol_address", &self.symbol_address())
124            .finish()
125    }
126}
127
128#[cfg(all(target_env = "sgx", target_vendor = "fortanix"))]
129mod sgx_image_base {
130
131    #[cfg(not(feature = "std"))]
132    pub(crate) mod imp {
133        use core::ffi::c_void;
134        use core::sync::atomic::{AtomicUsize, Ordering::SeqCst};
135
136        static IMAGE_BASE: AtomicUsize = AtomicUsize::new(0);
137
138        /// Set the image base address. This is only available for Fortanix SGX
139        /// target when the `std` feature is not enabled. This can be used in the
140        /// standard library to set the correct base address.
141        #[doc(hidden)]
142        pub fn set_image_base(base_addr: *mut c_void) {
143            IMAGE_BASE.store(base_addr as _, SeqCst);
144        }
145
146        pub(crate) fn get_image_base() -> *mut c_void {
147            IMAGE_BASE.load(SeqCst) as _
148        }
149    }
150
151    #[cfg(feature = "std")]
152    mod imp {
153        use core::ffi::c_void;
154
155        pub(crate) fn get_image_base() -> *mut c_void {
156            std::os::fortanix_sgx::mem::image_base() as _
157        }
158    }
159
160    pub(crate) use imp::get_image_base;
161}
162
163#[cfg(all(target_env = "sgx", target_vendor = "fortanix", not(feature = "std")))]
164pub use sgx_image_base::imp::set_image_base;
165
166cfg_if::cfg_if! {
167    // This needs to come first, to ensure that
168    // Miri takes priority over the host platform
169    if #[cfg(miri)] {
170        pub(crate) mod miri;
171        use self::miri::trace as trace_imp;
172        pub(crate) use self::miri::Frame as FrameImp;
173    } else if #[cfg(
174        any(
175            all(
176                unix,
177                not(target_os = "emscripten"),
178                not(all(target_os = "ios", target_arch = "arm")),
179            ),
180            all(
181                target_env = "sgx",
182                target_vendor = "fortanix",
183            ),
184        )
185    )] {
186        mod libunwind;
187        use self::libunwind::trace as trace_imp;
188        pub(crate) use self::libunwind::Frame as FrameImp;
189    } else if #[cfg(all(windows, not(target_vendor = "uwp")))] {
190        cfg_if::cfg_if! {
191            if #[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "arm64ec"))] {
192                mod dbghelp64;
193                use dbghelp64 as dbghelp;
194            } else if #[cfg(any(target_arch = "x86", target_arch = "arm"))] {
195                mod dbghelp32;
196                use dbghelp32 as dbghelp;
197            }
198        }
199        use self::dbghelp::trace as trace_imp;
200        pub(crate) use self::dbghelp::Frame as FrameImp;
201    } else {
202        mod noop;
203        use self::noop::trace as trace_imp;
204        pub(crate) use self::noop::Frame as FrameImp;
205    }
206}