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