]> git.proxmox.com Git - rustc.git/blame - library/std/src/sys_common/backtrace.rs
New upstream version 1.60.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};
29967ef6 10use crate::sys_common::mutex::StaticMutex;
532ac7d7 11
8bb4bdeb
XL
12/// Max number of frames to print.
13const MAX_NB_FRAMES: usize = 100;
14
29967ef6
XL
15// SAFETY: Don't attempt to lock this reentrantly.
16pub unsafe fn lock() -> impl Drop {
17 static LOCK: StaticMutex = StaticMutex::new();
18 LOCK.lock()
e1599b0c
XL
19}
20
21/// Prints the current backtrace.
22pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
0731742a
XL
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) {
dc9dc135 28 return Ok(());
0731742a
XL
29 }
30
8bb4bdeb
XL
31 // Use a lock to prevent mixed output in multithreading context.
32 // Some platforms also requires it, like `SymFromAddr` on Windows.
33 unsafe {
e1599b0c
XL
34 let _lock = lock();
35 _print(w, format)
36 }
37}
38
39unsafe fn _print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
40 struct DisplayBacktrace {
41 format: PrintFmt,
8bb4bdeb 42 }
e1599b0c
XL
43 impl fmt::Display for DisplayBacktrace {
44 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
dfeec247 45 unsafe { _print_fmt(fmt, self.format) }
e1599b0c
XL
46 }
47 }
48 write!(w, "{}", DisplayBacktrace { format })
8bb4bdeb
XL
49}
50
e1599b0c 51unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result {
60c5eb7d
XL
52 // Always 'fail' to get the cwd when running under Miri -
53 // this allows Miri to display backtraces in isolation mode
dfeec247 54 let cwd = if !cfg!(miri) { env::current_dir().ok() } else { None };
60c5eb7d 55
e1599b0c
XL
56 let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| {
57 output_filename(fmt, bows, print_fmt, cwd.as_ref())
58 };
ba9703b0 59 writeln!(fmt, "stack backtrace:")?;
e1599b0c
XL
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(());
3dfed10e
XL
64 // Start immediately if we're not using a short backtrace.
65 let mut start = print_fmt != PrintFmt::Short;
e1599b0c
XL
66 backtrace_rs::trace_unsynchronized(|frame| {
67 if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
68 return false;
69 }
8bb4bdeb 70
e1599b0c
XL
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()) {
94222f64 77 if start && sym.contains("__rust_begin_short_backtrace") {
e1599b0c
XL
78 stop = true;
79 return;
80 }
3dfed10e
XL
81 if sym.contains("__rust_end_short_backtrace") {
82 start = true;
83 return;
84 }
e1599b0c 85 }
7cac9316 86 }
e1599b0c 87
3dfed10e
XL
88 if start {
89 res = bt_fmt.frame().symbol(frame, symbol);
90 }
dc9dc135 91 });
e1599b0c
XL
92 if stop {
93 return false;
94 }
c295e0f8
XL
95 if !hit && start {
96 res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
e1599b0c
XL
97 }
98
99 idx += 1;
100 res.is_ok()
101 });
102 res?;
103 bt_fmt.finish()?;
104 if print_fmt == PrintFmt::Short {
dc9dc135 105 writeln!(
e1599b0c 106 fmt,
dc9dc135
XL
107 "note: Some details are omitted, \
108 run with `RUST_BACKTRACE=full` for a verbose backtrace."
109 )?;
110 }
111 Ok(())
7cac9316
XL
112}
113
e74abb32
XL
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))]
7cac9316 118pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
dc9dc135
XL
119where
120 F: FnOnce() -> T,
7cac9316 121{
3dfed10e
XL
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))]
134pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
135where
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
8bb4bdeb
XL
144}
145
e1599b0c
XL
146/// Prints the filename of the backtrace frame.
147///
148/// See also `output`.
149pub 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()
8bb4bdeb 160 }
e1599b0c
XL
161 #[cfg(not(unix))]
162 BytesOrWideString::Bytes(bytes) => {
163 Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>")).into()
dc9dc135 164 }
dc9dc135 165 #[cfg(windows)]
e1599b0c
XL
166 BytesOrWideString::Wide(wide) => {
167 use crate::os::windows::prelude::*;
168 Cow::Owned(crate::ffi::OsString::from_wide(wide).into())
dc9dc135 169 }
e1599b0c 170 #[cfg(not(windows))]
dfeec247 171 BytesOrWideString::Wide(_wide) => Path::new("<unknown>").into(),
e1599b0c
XL
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, ".{}{}", path::MAIN_SEPARATOR, s);
dc9dc135
XL
178 }
179 }
180 }
dc9dc135 181 }
e1599b0c 182 fmt::Display::fmt(&file.display(), fmt)
e9174d1e 183}