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