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