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