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