]>
Commit | Line | Data |
---|---|---|
dfeec247 | 1 | use crate::borrow::Cow; |
8bb4bdeb XL |
2 | /// Common code for printing the backtrace in the same way across the different |
3 | /// supported platforms. | |
532ac7d7 | 4 | use crate::env; |
e1599b0c | 5 | use crate::fmt; |
532ac7d7 | 6 | use crate::io; |
dc9dc135 | 7 | use crate::io::prelude::*; |
e1599b0c | 8 | use crate::path::{self, Path, PathBuf}; |
e74abb32 | 9 | use crate::sync::atomic::{self, Ordering}; |
532ac7d7 XL |
10 | use crate::sys::mutex::Mutex; |
11 | ||
e1599b0c | 12 | use backtrace_rs::{BacktraceFmt, BytesOrWideString, PrintFmt}; |
8bb4bdeb XL |
13 | |
14 | /// Max number of frames to print. | |
15 | const MAX_NB_FRAMES: usize = 100; | |
16 | ||
e1599b0c XL |
17 | pub 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. | |
36 | pub 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 | ||
53 | unsafe 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 | 65 | unsafe 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 | 124 | pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T |
dc9dc135 XL |
125 | where |
126 | F: FnOnce() -> T, | |
127 | F: Send, | |
128 | T: Send, | |
7cac9316 XL |
129 | { |
130 | f() | |
8bb4bdeb XL |
131 | } |
132 | ||
e74abb32 XL |
133 | pub 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 |
141 | pub 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`. | |
181 | pub 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 | } |