std/
backtrace.rs

1//! Support for capturing a stack backtrace of an OS thread
2//!
3//! This module contains the support necessary to capture a stack backtrace of a
4//! running OS thread from the OS thread itself. The `Backtrace` type supports
5//! capturing a stack trace via the `Backtrace::capture` and
6//! `Backtrace::force_capture` functions.
7//!
8//! A backtrace is typically quite handy to attach to errors (e.g. types
9//! implementing `std::error::Error`) to get a causal chain of where an error
10//! was generated.
11//!
12//! ## Accuracy
13//!
14//! Backtraces are attempted to be as accurate as possible, but no guarantees
15//! are provided about the exact accuracy of a backtrace. Instruction pointers,
16//! symbol names, filenames, line numbers, etc, may all be incorrect when
17//! reported. Accuracy is attempted on a best-effort basis, however, any bug
18//! reports are always welcome to indicate areas of improvement!
19//!
20//! For most platforms a backtrace with a filename/line number requires that
21//! programs be compiled with debug information. Without debug information
22//! filenames/line numbers will not be reported.
23//!
24//! ## Platform support
25//!
26//! Not all platforms that std compiles for support capturing backtraces. Some
27//! platforms simply do nothing when capturing a backtrace. To check whether the
28//! platform supports capturing backtraces you can consult the `BacktraceStatus`
29//! enum as a result of `Backtrace::status`.
30//!
31//! Like above with accuracy platform support is done on a best effort basis.
32//! Sometimes libraries might not be available at runtime or something may go
33//! wrong which would cause a backtrace to not be captured. Please feel free to
34//! report issues with platforms where a backtrace cannot be captured though!
35//!
36//! ## Environment Variables
37//!
38//! The `Backtrace::capture` function might not actually capture a backtrace by
39//! default. Its behavior is governed by two environment variables:
40//!
41//! * `RUST_LIB_BACKTRACE` - if this is set to `0` then `Backtrace::capture`
42//!   will never capture a backtrace. Any other value set will enable
43//!   `Backtrace::capture`.
44//!
45//! * `RUST_BACKTRACE` - if `RUST_LIB_BACKTRACE` is not set, then this variable
46//!   is consulted with the same rules of `RUST_LIB_BACKTRACE`.
47//!
48//! * If neither of the above env vars are set, then `Backtrace::capture` will
49//!   be disabled.
50//!
51//! Capturing a backtrace can be a quite expensive runtime operation, so the
52//! environment variables allow either forcibly disabling this runtime
53//! performance hit or allow selectively enabling it in some programs.
54//!
55//! Note that the `Backtrace::force_capture` function can be used to ignore
56//! these environment variables. Also note that the state of environment
57//! variables is cached once the first backtrace is created, so altering
58//! `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` at runtime might not actually change
59//! how backtraces are captured.
60
61#![stable(feature = "backtrace", since = "1.65.0")]
62
63#[cfg(test)]
64mod tests;
65
66// NB: A note on resolution of a backtrace:
67//
68// Backtraces primarily happen in two steps, one is where we actually capture
69// the stack backtrace, giving us a list of instruction pointers corresponding
70// to stack frames. Next we take these instruction pointers and, one-by-one,
71// turn them into a human readable name (like `main`).
72//
73// The first phase can be somewhat expensive (walking the stack), especially
74// on MSVC where debug information is consulted to return inline frames each as
75// their own frame. The second phase, however, is almost always extremely
76// expensive (on the order of milliseconds sometimes) when it's consulting debug
77// information.
78//
79// We attempt to amortize this cost as much as possible by delaying resolution
80// of an address to a human readable name for as long as possible. When
81// `Backtrace::create` is called to capture a backtrace it doesn't actually
82// perform any symbol resolution, but rather we lazily resolve symbols only just
83// before they're needed for printing. This way we can make capturing a
84// backtrace and throwing it away much cheaper, but actually printing a
85// backtrace is still basically the same cost.
86//
87// This strategy comes at the cost of some synchronization required inside of a
88// `Backtrace`, but that's a relatively small price to pay relative to capturing
89// a backtrace or actually symbolizing it.
90
91use crate::backtrace_rs::{self, BytesOrWideString};
92use crate::ffi::c_void;
93use crate::panic::UnwindSafe;
94use crate::sync::LazyLock;
95use crate::sync::atomic::Ordering::Relaxed;
96use crate::sync::atomic::{Atomic, AtomicU8};
97use crate::sys::backtrace::{lock, output_filename, set_image_base};
98use crate::{env, fmt};
99
100/// A captured OS thread stack backtrace.
101///
102/// This type represents a stack backtrace for an OS thread captured at a
103/// previous point in time. In some instances the `Backtrace` type may
104/// internally be empty due to configuration. For more information see
105/// `Backtrace::capture`.
106#[stable(feature = "backtrace", since = "1.65.0")]
107#[must_use]
108pub struct Backtrace {
109    inner: Inner,
110}
111
112/// The current status of a backtrace, indicating whether it was captured or
113/// whether it is empty for some other reason.
114#[stable(feature = "backtrace", since = "1.65.0")]
115#[non_exhaustive]
116#[derive(Debug, PartialEq, Eq)]
117pub enum BacktraceStatus {
118    /// Capturing a backtrace is not supported, likely because it's not
119    /// implemented for the current platform.
120    #[stable(feature = "backtrace", since = "1.65.0")]
121    Unsupported,
122    /// Capturing a backtrace has been disabled through either the
123    /// `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` environment variables.
124    #[stable(feature = "backtrace", since = "1.65.0")]
125    Disabled,
126    /// A backtrace has been captured and the `Backtrace` should print
127    /// reasonable information when rendered.
128    #[stable(feature = "backtrace", since = "1.65.0")]
129    Captured,
130}
131
132enum Inner {
133    Unsupported,
134    Disabled,
135    Captured(LazyLock<Capture, LazyResolve>),
136}
137
138struct Capture {
139    actual_start: usize,
140    frames: Vec<BacktraceFrame>,
141}
142
143fn _assert_send_sync() {
144    fn _assert<T: Send + Sync>() {}
145    _assert::<Backtrace>();
146}
147
148/// A single frame of a backtrace.
149#[unstable(feature = "backtrace_frames", issue = "79676")]
150pub struct BacktraceFrame {
151    frame: RawFrame,
152    symbols: Vec<BacktraceSymbol>,
153}
154
155#[derive(Debug)]
156enum RawFrame {
157    Actual(backtrace_rs::Frame),
158    #[cfg(test)]
159    Fake,
160}
161
162struct BacktraceSymbol {
163    name: Option<Vec<u8>>,
164    filename: Option<BytesOrWide>,
165    lineno: Option<u32>,
166    colno: Option<u32>,
167}
168
169enum BytesOrWide {
170    Bytes(Vec<u8>),
171    Wide(Vec<u16>),
172}
173
174#[stable(feature = "backtrace", since = "1.65.0")]
175impl fmt::Debug for Backtrace {
176    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
177        let capture = match &self.inner {
178            Inner::Unsupported => return fmt.write_str("<unsupported>"),
179            Inner::Disabled => return fmt.write_str("<disabled>"),
180            Inner::Captured(c) => &**c,
181        };
182
183        let frames = &capture.frames[capture.actual_start..];
184
185        write!(fmt, "Backtrace ")?;
186
187        let mut dbg = fmt.debug_list();
188
189        for frame in frames {
190            if frame.frame.ip().is_null() {
191                continue;
192            }
193
194            dbg.entries(&frame.symbols);
195        }
196
197        dbg.finish()
198    }
199}
200
201#[unstable(feature = "backtrace_frames", issue = "79676")]
202impl fmt::Debug for BacktraceFrame {
203    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
204        let mut dbg = fmt.debug_list();
205        dbg.entries(&self.symbols);
206        dbg.finish()
207    }
208}
209
210impl fmt::Debug for BacktraceSymbol {
211    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
212        // FIXME: improve formatting: https://github.com/rust-lang/rust/issues/65280
213        // FIXME: Also, include column numbers into the debug format as Display already has them.
214        // Until there are stable per-frame accessors, the format shouldn't be changed:
215        // https://github.com/rust-lang/rust/issues/65280#issuecomment-638966585
216        write!(fmt, "{{ ")?;
217
218        if let Some(fn_name) = self.name.as_ref().map(|b| backtrace_rs::SymbolName::new(b)) {
219            write!(fmt, "fn: \"{:#}\"", fn_name)?;
220        } else {
221            write!(fmt, "fn: <unknown>")?;
222        }
223
224        if let Some(fname) = self.filename.as_ref() {
225            write!(fmt, ", file: \"{:?}\"", fname)?;
226        }
227
228        if let Some(line) = self.lineno {
229            write!(fmt, ", line: {:?}", line)?;
230        }
231
232        write!(fmt, " }}")
233    }
234}
235
236impl fmt::Debug for BytesOrWide {
237    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
238        output_filename(
239            fmt,
240            match self {
241                BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w),
242                BytesOrWide::Wide(w) => BytesOrWideString::Wide(w),
243            },
244            backtrace_rs::PrintFmt::Short,
245            crate::env::current_dir().as_ref().ok(),
246        )
247    }
248}
249
250impl Backtrace {
251    /// Returns whether backtrace captures are enabled through environment
252    /// variables.
253    fn enabled() -> bool {
254        // Cache the result of reading the environment variables to make
255        // backtrace captures speedy, because otherwise reading environment
256        // variables every time can be somewhat slow.
257        static ENABLED: Atomic<u8> = AtomicU8::new(0);
258        match ENABLED.load(Relaxed) {
259            0 => {}
260            1 => return false,
261            _ => return true,
262        }
263        let enabled = match env::var("RUST_LIB_BACKTRACE") {
264            Ok(s) => s != "0",
265            Err(_) => match env::var("RUST_BACKTRACE") {
266                Ok(s) => s != "0",
267                Err(_) => false,
268            },
269        };
270        ENABLED.store(enabled as u8 + 1, Relaxed);
271        enabled
272    }
273
274    /// Captures a stack backtrace of the current thread.
275    ///
276    /// This function will capture a stack backtrace of the current OS thread of
277    /// execution, returning a `Backtrace` type which can be later used to print
278    /// the entire stack trace or render it to a string.
279    ///
280    /// This function will be a noop if the `RUST_BACKTRACE` or
281    /// `RUST_LIB_BACKTRACE` backtrace variables are both not set. If either
282    /// environment variable is set and enabled then this function will actually
283    /// capture a backtrace. Capturing a backtrace can be both memory intensive
284    /// and slow, so these environment variables allow liberally using
285    /// `Backtrace::capture` and only incurring a slowdown when the environment
286    /// variables are set.
287    ///
288    /// To forcibly capture a backtrace regardless of environment variables, use
289    /// the `Backtrace::force_capture` function.
290    #[stable(feature = "backtrace", since = "1.65.0")]
291    #[inline(never)] // want to make sure there's a frame here to remove
292    pub fn capture() -> Backtrace {
293        if !Backtrace::enabled() {
294            return Backtrace { inner: Inner::Disabled };
295        }
296        Backtrace::create(Backtrace::capture as usize)
297    }
298
299    /// Forcibly captures a full backtrace, regardless of environment variable
300    /// configuration.
301    ///
302    /// This function behaves the same as `capture` except that it ignores the
303    /// values of the `RUST_BACKTRACE` and `RUST_LIB_BACKTRACE` environment
304    /// variables, always capturing a backtrace.
305    ///
306    /// Note that capturing a backtrace can be an expensive operation on some
307    /// platforms, so this should be used with caution in performance-sensitive
308    /// parts of code.
309    #[stable(feature = "backtrace", since = "1.65.0")]
310    #[inline(never)] // want to make sure there's a frame here to remove
311    pub fn force_capture() -> Backtrace {
312        Backtrace::create(Backtrace::force_capture as usize)
313    }
314
315    /// Forcibly captures a disabled backtrace, regardless of environment
316    /// variable configuration.
317    #[stable(feature = "backtrace", since = "1.65.0")]
318    #[rustc_const_stable(feature = "backtrace", since = "1.65.0")]
319    pub const fn disabled() -> Backtrace {
320        Backtrace { inner: Inner::Disabled }
321    }
322
323    // Capture a backtrace which start just before the function addressed by
324    // `ip`
325    fn create(ip: usize) -> Backtrace {
326        let _lock = lock();
327        let mut frames = Vec::new();
328        let mut actual_start = None;
329        set_image_base();
330        unsafe {
331            backtrace_rs::trace_unsynchronized(|frame| {
332                frames.push(BacktraceFrame {
333                    frame: RawFrame::Actual(frame.clone()),
334                    symbols: Vec::new(),
335                });
336                if frame.symbol_address().addr() == ip && actual_start.is_none() {
337                    actual_start = Some(frames.len());
338                }
339                true
340            });
341        }
342
343        // If no frames came out assume that this is an unsupported platform
344        // since `backtrace` doesn't provide a way of learning this right now,
345        // and this should be a good enough approximation.
346        let inner = if frames.is_empty() {
347            Inner::Unsupported
348        } else {
349            Inner::Captured(LazyLock::new(lazy_resolve(Capture {
350                actual_start: actual_start.unwrap_or(0),
351                frames,
352            })))
353        };
354
355        Backtrace { inner }
356    }
357
358    /// Returns the status of this backtrace, indicating whether this backtrace
359    /// request was unsupported, disabled, or a stack trace was actually
360    /// captured.
361    #[stable(feature = "backtrace", since = "1.65.0")]
362    #[must_use]
363    pub fn status(&self) -> BacktraceStatus {
364        match self.inner {
365            Inner::Unsupported => BacktraceStatus::Unsupported,
366            Inner::Disabled => BacktraceStatus::Disabled,
367            Inner::Captured(_) => BacktraceStatus::Captured,
368        }
369    }
370}
371
372impl<'a> Backtrace {
373    /// Returns an iterator over the backtrace frames.
374    #[must_use]
375    #[unstable(feature = "backtrace_frames", issue = "79676")]
376    pub fn frames(&'a self) -> &'a [BacktraceFrame] {
377        if let Inner::Captured(c) = &self.inner { &c.frames } else { &[] }
378    }
379}
380
381#[stable(feature = "backtrace", since = "1.65.0")]
382impl fmt::Display for Backtrace {
383    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
384        let capture = match &self.inner {
385            Inner::Unsupported => return fmt.write_str("unsupported backtrace"),
386            Inner::Disabled => return fmt.write_str("disabled backtrace"),
387            Inner::Captured(c) => &**c,
388        };
389
390        let full = fmt.alternate();
391        let (frames, style) = if full {
392            (&capture.frames[..], backtrace_rs::PrintFmt::Full)
393        } else {
394            (&capture.frames[capture.actual_start..], backtrace_rs::PrintFmt::Short)
395        };
396
397        // When printing paths we try to strip the cwd if it exists, otherwise
398        // we just print the path as-is. Note that we also only do this for the
399        // short format, because if it's full we presumably want to print
400        // everything.
401        let cwd = crate::env::current_dir();
402        let mut print_path = move |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
403            output_filename(fmt, path, style, cwd.as_ref().ok())
404        };
405
406        let mut f = backtrace_rs::BacktraceFmt::new(fmt, style, &mut print_path);
407        f.add_context()?;
408        for frame in frames {
409            if frame.symbols.is_empty() {
410                f.frame().print_raw(frame.frame.ip(), None, None, None)?;
411            } else {
412                for symbol in frame.symbols.iter() {
413                    f.frame().print_raw_with_column(
414                        frame.frame.ip(),
415                        symbol.name.as_ref().map(|b| backtrace_rs::SymbolName::new(b)),
416                        symbol.filename.as_ref().map(|b| match b {
417                            BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w),
418                            BytesOrWide::Wide(w) => BytesOrWideString::Wide(w),
419                        }),
420                        symbol.lineno,
421                        symbol.colno,
422                    )?;
423                }
424            }
425        }
426        f.finish()?;
427        Ok(())
428    }
429}
430
431mod helper {
432    use super::*;
433    pub(super) type LazyResolve = impl (FnOnce() -> Capture) + Send + Sync + UnwindSafe;
434
435    #[define_opaque(LazyResolve)]
436    pub(super) fn lazy_resolve(mut capture: Capture) -> LazyResolve {
437        move || {
438            // Use the global backtrace lock to synchronize this as it's a
439            // requirement of the `backtrace` crate, and then actually resolve
440            // everything.
441            let _lock = lock();
442            for frame in capture.frames.iter_mut() {
443                let symbols = &mut frame.symbols;
444                let frame = match &frame.frame {
445                    RawFrame::Actual(frame) => frame,
446                    #[cfg(test)]
447                    RawFrame::Fake => unimplemented!(),
448                };
449                unsafe {
450                    backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
451                        symbols.push(BacktraceSymbol {
452                            name: symbol.name().map(|m| m.as_bytes().to_vec()),
453                            filename: symbol.filename_raw().map(|b| match b {
454                                BytesOrWideString::Bytes(b) => BytesOrWide::Bytes(b.to_owned()),
455                                BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()),
456                            }),
457                            lineno: symbol.lineno(),
458                            colno: symbol.colno(),
459                        });
460                    });
461                }
462            }
463
464            capture
465        }
466    }
467}
468use helper::*;
469
470impl RawFrame {
471    fn ip(&self) -> *mut c_void {
472        match self {
473            RawFrame::Actual(frame) => frame.ip(),
474            #[cfg(test)]
475            RawFrame::Fake => crate::ptr::without_provenance_mut(1),
476        }
477    }
478}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy