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