std/sys/
backtrace.rs

1//! Common code for printing backtraces.
2#![forbid(unsafe_op_in_unsafe_fn)]
3
4use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt};
5use crate::borrow::Cow;
6use crate::io::prelude::*;
7use crate::path::{self, Path, PathBuf};
8use crate::sync::{Mutex, MutexGuard, PoisonError};
9use crate::{env, fmt, io};
10
11/// Max number of frames to print.
12const MAX_NB_FRAMES: usize = 100;
13
14pub(crate) struct BacktraceLock<'a>(#[allow(dead_code)] MutexGuard<'a, ()>);
15
16pub(crate) fn lock<'a>() -> BacktraceLock<'a> {
17    static LOCK: Mutex<()> = Mutex::new(());
18    BacktraceLock(LOCK.lock().unwrap_or_else(PoisonError::into_inner))
19}
20
21impl BacktraceLock<'_> {
22    /// Prints the current backtrace.
23    ///
24    /// NOTE: this function is not Sync. The caller must hold a mutex lock, or there must be only one thread in the program.
25    pub(crate) fn print(&mut self, w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
26        // There are issues currently linking libbacktrace into tests, and in
27        // general during std's own unit tests we're not testing this path. In
28        // test mode immediately return here to optimize away any references to the
29        // libbacktrace symbols
30        if cfg!(test) {
31            return Ok(());
32        }
33
34        struct DisplayBacktrace {
35            format: PrintFmt,
36        }
37        impl fmt::Display for DisplayBacktrace {
38            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
39                unsafe { _print_fmt(fmt, self.format) }
40            }
41        }
42        write!(w, "{}", DisplayBacktrace { format })
43    }
44}
45
46unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result {
47    // Always 'fail' to get the cwd when running under Miri -
48    // this allows Miri to display backtraces in isolation mode
49    let cwd = if !cfg!(miri) { env::current_dir().ok() } else { None };
50
51    let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| {
52        output_filename(fmt, bows, print_fmt, cwd.as_ref())
53    };
54    writeln!(fmt, "stack backtrace:")?;
55    let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path);
56    bt_fmt.add_context()?;
57    let mut idx = 0;
58    let mut res = Ok(());
59    let mut omitted_count: usize = 0;
60    let mut first_omit = true;
61    // If we're using a short backtrace, ignore all frames until we're told to start printing.
62    let mut print = print_fmt != PrintFmt::Short;
63    set_image_base();
64    // SAFETY: we roll our own locking in this town
65    unsafe {
66        backtrace_rs::trace_unsynchronized(|frame| {
67            if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
68                return false;
69            }
70
71            let mut hit = false;
72            backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
73                hit = true;
74
75                // `__rust_end_short_backtrace` means we are done hiding symbols
76                // for now. Print until we see `__rust_begin_short_backtrace`.
77                if print_fmt == PrintFmt::Short {
78                    if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
79                        if sym.contains("__rust_end_short_backtrace") {
80                            print = true;
81                            return;
82                        }
83                        if print && sym.contains("__rust_begin_short_backtrace") {
84                            print = false;
85                            return;
86                        }
87                        if !print {
88                            omitted_count += 1;
89                        }
90                    }
91                }
92
93                if print {
94                    if omitted_count > 0 {
95                        debug_assert!(print_fmt == PrintFmt::Short);
96                        // only print the message between the middle of frames
97                        if !first_omit {
98                            let _ = writeln!(
99                                bt_fmt.formatter(),
100                                "      [... omitted {} frame{} ...]",
101                                omitted_count,
102                                if omitted_count > 1 { "s" } else { "" }
103                            );
104                        }
105                        first_omit = false;
106                        omitted_count = 0;
107                    }
108                    res = bt_fmt.frame().symbol(frame, symbol);
109                }
110            });
111            #[cfg(target_os = "nto")]
112            if libc::__my_thread_exit as *mut libc::c_void == frame.ip() {
113                if !hit && print {
114                    use crate::backtrace_rs::SymbolName;
115                    res = bt_fmt.frame().print_raw(
116                        frame.ip(),
117                        Some(SymbolName::new("__my_thread_exit".as_bytes())),
118                        None,
119                        None,
120                    );
121                }
122                return false;
123            }
124            if !hit && print {
125                res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
126            }
127
128            idx += 1;
129            res.is_ok()
130        })
131    };
132    res?;
133    bt_fmt.finish()?;
134    if print_fmt == PrintFmt::Short {
135        writeln!(
136            fmt,
137            "note: Some details are omitted, \
138             run with `RUST_BACKTRACE=full` for a verbose backtrace."
139        )?;
140    }
141    Ok(())
142}
143
144/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
145/// this is only inline(never) when backtraces in std are enabled, otherwise
146/// it's fine to optimize away.
147#[cfg_attr(feature = "backtrace", inline(never))]
148pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
149where
150    F: FnOnce() -> T,
151{
152    let result = f();
153
154    // prevent this frame from being tail-call optimised away
155    crate::hint::black_box(());
156
157    result
158}
159
160/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
161/// this is only inline(never) when backtraces in std are enabled, otherwise
162/// it's fine to optimize away.
163#[cfg_attr(feature = "backtrace", inline(never))]
164pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
165where
166    F: FnOnce() -> T,
167{
168    let result = f();
169
170    // prevent this frame from being tail-call optimised away
171    crate::hint::black_box(());
172
173    result
174}
175
176/// Prints the filename of the backtrace frame.
177///
178/// See also `output`.
179pub fn output_filename(
180    fmt: &mut fmt::Formatter<'_>,
181    bows: BytesOrWideString<'_>,
182    print_fmt: PrintFmt,
183    cwd: Option<&PathBuf>,
184) -> fmt::Result {
185    let file: Cow<'_, Path> = match bows {
186        #[cfg(unix)]
187        BytesOrWideString::Bytes(bytes) => {
188            use crate::os::unix::prelude::*;
189            Path::new(crate::ffi::OsStr::from_bytes(bytes)).into()
190        }
191        #[cfg(not(unix))]
192        BytesOrWideString::Bytes(bytes) => {
193            Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>")).into()
194        }
195        #[cfg(windows)]
196        BytesOrWideString::Wide(wide) => {
197            use crate::os::windows::prelude::*;
198            Cow::Owned(crate::ffi::OsString::from_wide(wide).into())
199        }
200        #[cfg(not(windows))]
201        BytesOrWideString::Wide(_wide) => Path::new("<unknown>").into(),
202    };
203    if print_fmt == PrintFmt::Short && file.is_absolute() {
204        if let Some(cwd) = cwd {
205            if let Ok(stripped) = file.strip_prefix(&cwd) {
206                if let Some(s) = stripped.to_str() {
207                    return write!(fmt, ".{}{s}", path::MAIN_SEPARATOR);
208                }
209            }
210        }
211    }
212    fmt::Display::fmt(&file.display(), fmt)
213}
214
215#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
216pub fn set_image_base() {
217    let image_base = crate::os::fortanix_sgx::mem::image_base();
218    backtrace_rs::set_image_base(crate::ptr::without_provenance_mut(image_base as _));
219}
220
221#[cfg(not(all(target_vendor = "fortanix", target_env = "sgx")))]
222pub fn set_image_base() {
223    // nothing to do for platforms other than SGX
224}