]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys_common/backtrace.rs
Rollup merge of #98385 - m-ou-se:llvm-12-memory-order, r=petrochenkov
[rust.git] / library / std / src / sys_common / backtrace.rs
1 use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt};
2 use crate::borrow::Cow;
3 /// Common code for printing the backtrace in the same way across the different
4 /// supported platforms.
5 use crate::env;
6 use crate::fmt;
7 use crate::io;
8 use crate::io::prelude::*;
9 use crate::path::{self, Path, PathBuf};
10 use crate::sys_common::mutex::StaticMutex;
11
12 /// Max number of frames to print.
13 const MAX_NB_FRAMES: usize = 100;
14
15 // SAFETY: Don't attempt to lock this reentrantly.
16 pub unsafe fn lock() -> impl Drop {
17     static LOCK: StaticMutex = StaticMutex::new();
18     LOCK.lock()
19 }
20
21 /// Prints the current backtrace.
22 pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
23     // There are issues currently linking libbacktrace into tests, and in
24     // general during libstd's own unit tests we're not testing this path. In
25     // test mode immediately return here to optimize away any references to the
26     // libbacktrace symbols
27     if cfg!(test) {
28         return Ok(());
29     }
30
31     // Use a lock to prevent mixed output in multithreading context.
32     // Some platforms also requires it, like `SymFromAddr` on Windows.
33     unsafe {
34         let _lock = lock();
35         _print(w, format)
36     }
37 }
38
39 unsafe fn _print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
40     struct DisplayBacktrace {
41         format: PrintFmt,
42     }
43     impl fmt::Display for DisplayBacktrace {
44         fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
45             unsafe { _print_fmt(fmt, self.format) }
46         }
47     }
48     write!(w, "{}", DisplayBacktrace { format })
49 }
50
51 unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result {
52     // Always 'fail' to get the cwd when running under Miri -
53     // this allows Miri to display backtraces in isolation mode
54     let cwd = if !cfg!(miri) { env::current_dir().ok() } else { None };
55
56     let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| {
57         output_filename(fmt, bows, print_fmt, cwd.as_ref())
58     };
59     writeln!(fmt, "stack backtrace:")?;
60     let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path);
61     bt_fmt.add_context()?;
62     let mut idx = 0;
63     let mut res = Ok(());
64     // Start immediately if we're not using a short backtrace.
65     let mut start = print_fmt != PrintFmt::Short;
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         let mut stop = false;
73         backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
74             hit = true;
75             if print_fmt == PrintFmt::Short {
76                 if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
77                     if start && sym.contains("__rust_begin_short_backtrace") {
78                         stop = true;
79                         return;
80                     }
81                     if sym.contains("__rust_end_short_backtrace") {
82                         start = true;
83                         return;
84                     }
85                 }
86             }
87
88             if start {
89                 res = bt_fmt.frame().symbol(frame, symbol);
90             }
91         });
92         if stop {
93             return false;
94         }
95         if !hit && start {
96             res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
97         }
98
99         idx += 1;
100         res.is_ok()
101     });
102     res?;
103     bt_fmt.finish()?;
104     if print_fmt == PrintFmt::Short {
105         writeln!(
106             fmt,
107             "note: Some details are omitted, \
108              run with `RUST_BACKTRACE=full` for a verbose backtrace."
109         )?;
110     }
111     Ok(())
112 }
113
114 /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
115 /// this is only inline(never) when backtraces in libstd are enabled, otherwise
116 /// it's fine to optimize away.
117 #[cfg_attr(feature = "backtrace", inline(never))]
118 pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
119 where
120     F: FnOnce() -> T,
121 {
122     let result = f();
123
124     // prevent this frame from being tail-call optimised away
125     crate::hint::black_box(());
126
127     result
128 }
129
130 /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
131 /// this is only inline(never) when backtraces in libstd are enabled, otherwise
132 /// it's fine to optimize away.
133 #[cfg_attr(feature = "backtrace", inline(never))]
134 pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
135 where
136     F: FnOnce() -> T,
137 {
138     let result = f();
139
140     // prevent this frame from being tail-call optimised away
141     crate::hint::black_box(());
142
143     result
144 }
145
146 /// Prints the filename of the backtrace frame.
147 ///
148 /// See also `output`.
149 pub fn output_filename(
150     fmt: &mut fmt::Formatter<'_>,
151     bows: BytesOrWideString<'_>,
152     print_fmt: PrintFmt,
153     cwd: Option<&PathBuf>,
154 ) -> fmt::Result {
155     let file: Cow<'_, Path> = match bows {
156         #[cfg(unix)]
157         BytesOrWideString::Bytes(bytes) => {
158             use crate::os::unix::prelude::*;
159             Path::new(crate::ffi::OsStr::from_bytes(bytes)).into()
160         }
161         #[cfg(not(unix))]
162         BytesOrWideString::Bytes(bytes) => {
163             Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>")).into()
164         }
165         #[cfg(windows)]
166         BytesOrWideString::Wide(wide) => {
167             use crate::os::windows::prelude::*;
168             Cow::Owned(crate::ffi::OsString::from_wide(wide).into())
169         }
170         #[cfg(not(windows))]
171         BytesOrWideString::Wide(_wide) => Path::new("<unknown>").into(),
172     };
173     if print_fmt == PrintFmt::Short && file.is_absolute() {
174         if let Some(cwd) = cwd {
175             if let Ok(stripped) = file.strip_prefix(&cwd) {
176                 if let Some(s) = stripped.to_str() {
177                     return write!(fmt, ".{}{s}", path::MAIN_SEPARATOR);
178                 }
179             }
180         }
181     }
182     fmt::Display::fmt(&file.display(), fmt)
183 }