]> git.proxmox.com Git - rustc.git/blame - library/std/src/sys_common/backtrace.rs
New upstream version 1.55.0+dfsg1
[rustc.git] / library / std / src / sys_common / backtrace.rs
CommitLineData
3dfed10e 1use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt};
dfeec247 2use crate::borrow::Cow;
8bb4bdeb
XL
3/// Common code for printing the backtrace in the same way across the different
4/// supported platforms.
532ac7d7 5use crate::env;
e1599b0c 6use crate::fmt;
532ac7d7 7use crate::io;
dc9dc135 8use crate::io::prelude::*;
e1599b0c 9use crate::path::{self, Path, PathBuf};
e74abb32 10use crate::sync::atomic::{self, Ordering};
29967ef6 11use crate::sys_common::mutex::StaticMutex;
532ac7d7 12
8bb4bdeb
XL
13/// Max number of frames to print.
14const MAX_NB_FRAMES: usize = 100;
15
29967ef6
XL
16// SAFETY: Don't attempt to lock this reentrantly.
17pub unsafe fn lock() -> impl Drop {
18 static LOCK: StaticMutex = StaticMutex::new();
19 LOCK.lock()
e1599b0c
XL
20}
21
22/// Prints the current backtrace.
23pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
0731742a
XL
24 // There are issues currently linking libbacktrace into tests, and in
25 // general during libstd'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) {
dc9dc135 29 return Ok(());
0731742a
XL
30 }
31
8bb4bdeb
XL
32 // Use a lock to prevent mixed output in multithreading context.
33 // Some platforms also requires it, like `SymFromAddr` on Windows.
34 unsafe {
e1599b0c
XL
35 let _lock = lock();
36 _print(w, format)
37 }
38}
39
40unsafe fn _print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
41 struct DisplayBacktrace {
42 format: PrintFmt,
8bb4bdeb 43 }
e1599b0c
XL
44 impl fmt::Display for DisplayBacktrace {
45 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
dfeec247 46 unsafe { _print_fmt(fmt, self.format) }
e1599b0c
XL
47 }
48 }
49 write!(w, "{}", DisplayBacktrace { format })
8bb4bdeb
XL
50}
51
e1599b0c 52unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result {
60c5eb7d
XL
53 // Always 'fail' to get the cwd when running under Miri -
54 // this allows Miri to display backtraces in isolation mode
dfeec247 55 let cwd = if !cfg!(miri) { env::current_dir().ok() } else { None };
60c5eb7d 56
e1599b0c
XL
57 let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| {
58 output_filename(fmt, bows, print_fmt, cwd.as_ref())
59 };
ba9703b0 60 writeln!(fmt, "stack backtrace:")?;
e1599b0c
XL
61 let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path);
62 bt_fmt.add_context()?;
63 let mut idx = 0;
64 let mut res = Ok(());
3dfed10e
XL
65 // Start immediately if we're not using a short backtrace.
66 let mut start = print_fmt != PrintFmt::Short;
e1599b0c
XL
67 backtrace_rs::trace_unsynchronized(|frame| {
68 if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
69 return false;
70 }
8bb4bdeb 71
e1599b0c
XL
72 let mut hit = false;
73 let mut stop = false;
74 backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
75 hit = true;
76 if print_fmt == PrintFmt::Short {
77 if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
78 if sym.contains("__rust_begin_short_backtrace") {
79 stop = true;
80 return;
81 }
3dfed10e
XL
82 if sym.contains("__rust_end_short_backtrace") {
83 start = true;
84 return;
85 }
e1599b0c 86 }
7cac9316 87 }
e1599b0c 88
3dfed10e
XL
89 if start {
90 res = bt_fmt.frame().symbol(frame, symbol);
91 }
dc9dc135 92 });
e1599b0c
XL
93 if stop {
94 return false;
95 }
96 if !hit {
3dfed10e
XL
97 if start {
98 res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
99 }
e1599b0c
XL
100 }
101
102 idx += 1;
103 res.is_ok()
104 });
105 res?;
106 bt_fmt.finish()?;
107 if print_fmt == PrintFmt::Short {
dc9dc135 108 writeln!(
e1599b0c 109 fmt,
dc9dc135
XL
110 "note: Some details are omitted, \
111 run with `RUST_BACKTRACE=full` for a verbose backtrace."
112 )?;
113 }
114 Ok(())
7cac9316
XL
115}
116
e74abb32
XL
117/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
118/// this is only inline(never) when backtraces in libstd are enabled, otherwise
119/// it's fine to optimize away.
120#[cfg_attr(feature = "backtrace", inline(never))]
7cac9316 121pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
dc9dc135
XL
122where
123 F: FnOnce() -> T,
7cac9316 124{
3dfed10e
XL
125 let result = f();
126
127 // prevent this frame from being tail-call optimised away
128 crate::hint::black_box(());
129
130 result
131}
132
133/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
134/// this is only inline(never) when backtraces in libstd are enabled, otherwise
135/// it's fine to optimize away.
136#[cfg_attr(feature = "backtrace", inline(never))]
137pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
138where
139 F: FnOnce() -> T,
140{
141 let result = f();
142
143 // prevent this frame from being tail-call optimised away
144 crate::hint::black_box(());
145
146 result
8bb4bdeb
XL
147}
148
e74abb32
XL
149pub enum RustBacktrace {
150 Print(PrintFmt),
151 Disabled,
152 RuntimeDisabled,
153}
154
e9174d1e
SL
155// For now logging is turned off by default, and this function checks to see
156// whether the magical environment variable is present to see if it's turned on.
e74abb32
XL
157pub fn rust_backtrace_env() -> RustBacktrace {
158 // If the `backtrace` feature of this crate isn't enabled quickly return
159 // `None` so this can be constant propagated all over the place to turn
160 // optimize away callers.
161 if !cfg!(feature = "backtrace") {
162 return RustBacktrace::Disabled;
163 }
e1599b0c
XL
164
165 // Setting environment variables for Fuchsia components isn't a standard
166 // or easily supported workflow. For now, always display backtraces.
167 if cfg!(target_os = "fuchsia") {
e74abb32 168 return RustBacktrace::Print(PrintFmt::Full);
e1599b0c
XL
169 }
170
e9174d1e
SL
171 static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
172 match ENABLED.load(Ordering::SeqCst) {
83c7162d 173 0 => {}
e74abb32
XL
174 1 => return RustBacktrace::RuntimeDisabled,
175 2 => return RustBacktrace::Print(PrintFmt::Short),
176 _ => return RustBacktrace::Print(PrintFmt::Full),
e9174d1e
SL
177 }
178
e74abb32
XL
179 let (format, cache) = env::var_os("RUST_BACKTRACE")
180 .map(|x| {
181 if &x == "0" {
182 (RustBacktrace::RuntimeDisabled, 1)
183 } else if &x == "full" {
184 (RustBacktrace::Print(PrintFmt::Full), 3)
185 } else {
186 (RustBacktrace::Print(PrintFmt::Short), 2)
187 }
188 })
189 .unwrap_or((RustBacktrace::RuntimeDisabled, 1));
190 ENABLED.store(cache, Ordering::SeqCst);
191 format
e9174d1e
SL
192}
193
e1599b0c
XL
194/// Prints the filename of the backtrace frame.
195///
196/// See also `output`.
197pub fn output_filename(
198 fmt: &mut fmt::Formatter<'_>,
199 bows: BytesOrWideString<'_>,
200 print_fmt: PrintFmt,
201 cwd: Option<&PathBuf>,
202) -> fmt::Result {
203 let file: Cow<'_, Path> = match bows {
204 #[cfg(unix)]
205 BytesOrWideString::Bytes(bytes) => {
206 use crate::os::unix::prelude::*;
207 Path::new(crate::ffi::OsStr::from_bytes(bytes)).into()
8bb4bdeb 208 }
e1599b0c
XL
209 #[cfg(not(unix))]
210 BytesOrWideString::Bytes(bytes) => {
211 Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>")).into()
dc9dc135 212 }
dc9dc135 213 #[cfg(windows)]
e1599b0c
XL
214 BytesOrWideString::Wide(wide) => {
215 use crate::os::windows::prelude::*;
216 Cow::Owned(crate::ffi::OsString::from_wide(wide).into())
dc9dc135 217 }
e1599b0c 218 #[cfg(not(windows))]
dfeec247 219 BytesOrWideString::Wide(_wide) => Path::new("<unknown>").into(),
e1599b0c
XL
220 };
221 if print_fmt == PrintFmt::Short && file.is_absolute() {
222 if let Some(cwd) = cwd {
223 if let Ok(stripped) = file.strip_prefix(&cwd) {
224 if let Some(s) = stripped.to_str() {
225 return write!(fmt, ".{}{}", path::MAIN_SEPARATOR, s);
dc9dc135
XL
226 }
227 }
228 }
dc9dc135 229 }
e1599b0c 230 fmt::Display::fmt(&file.display(), fmt)
e9174d1e 231}