1#[cfg(std_backtrace)]
2pub(crate) use std::backtrace::{Backtrace, BacktraceStatus};
3
4#[cfg(all(not(std_backtrace), feature = "backtrace"))]
5pub(crate) use self::capture::{Backtrace, BacktraceStatus};
6
7#[cfg(not(any(std_backtrace, feature = "backtrace")))]
8pub(crate) enum Backtrace {}
9
10#[cfg(std_backtrace)]
11macro_rules! impl_backtrace {
12 () => {
13 std::backtrace::Backtrace
14 };
15}
16
17#[cfg(all(not(std_backtrace), feature = "backtrace"))]
18macro_rules! impl_backtrace {
19 () => {
20 impl core::fmt::Debug + core::fmt::Display
21 };
22}
23
24#[cfg(any(std_backtrace, feature = "backtrace"))]
25macro_rules! backtrace {
26 () => {
27 Some(crate::backtrace::Backtrace::capture())
28 };
29}
30
31#[cfg(not(any(std_backtrace, feature = "backtrace")))]
32macro_rules! backtrace {
33 () => {
34 None
35 };
36}
37
38#[cfg(error_generic_member_access)]
39macro_rules! backtrace_if_absent {
40 ($err:expr) => {
41 match std::error::request_ref::<std::backtrace::Backtrace>($err as &dyn std::error::Error) {
42 Some(_) => None,
43 None => backtrace!(),
44 }
45 };
46}
47
48#[cfg(all(
49 feature = "std",
50 not(error_generic_member_access),
51 any(std_backtrace, feature = "backtrace")
52))]
53macro_rules! backtrace_if_absent {
54 ($err:expr) => {
55 backtrace!()
56 };
57}
58
59#[cfg(all(feature = "std", not(std_backtrace), not(feature = "backtrace")))]
60macro_rules! backtrace_if_absent {
61 ($err:expr) => {
62 None
63 };
64}
65
66#[cfg(all(not(std_backtrace), feature = "backtrace"))]
67mod capture {
68 use alloc::borrow::Cow;
69 use backtrace::{BacktraceFmt, BytesOrWideString, Frame, PrintFmt, SymbolName};
70 use core::cell::UnsafeCell;
71 use core::fmt::{self, Debug, Display};
72 use core::sync::atomic::{AtomicUsize, Ordering};
73 use std::env;
74 use std::path::{self, Path, PathBuf};
75 use std::sync::Once;
76
77 pub(crate) struct Backtrace {
78 inner: Inner,
79 }
80
81 pub(crate) enum BacktraceStatus {
82 Unsupported,
83 Disabled,
84 Captured,
85 }
86
87 enum Inner {
88 Unsupported,
89 Disabled,
90 Captured(LazilyResolvedCapture),
91 }
92
93 struct Capture {
94 actual_start: usize,
95 resolved: bool,
96 frames: Vec<BacktraceFrame>,
97 }
98
99 struct BacktraceFrame {
100 frame: Frame,
101 symbols: Vec<BacktraceSymbol>,
102 }
103
104 struct BacktraceSymbol {
105 name: Option<Vec<u8>>,
106 filename: Option<BytesOrWide>,
107 lineno: Option<u32>,
108 colno: Option<u32>,
109 }
110
111 enum BytesOrWide {
112 Bytes(Vec<u8>),
113 Wide(Vec<u16>),
114 }
115
116 impl Debug for Backtrace {
117 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
118 let capture = match &self.inner {
119 Inner::Unsupported => return fmt.write_str("<unsupported>"),
120 Inner::Disabled => return fmt.write_str("<disabled>"),
121 Inner::Captured(c) => c.force(),
122 };
123
124 let frames = &capture.frames[capture.actual_start..];
125
126 write!(fmt, "Backtrace ")?;
127
128 let mut dbg = fmt.debug_list();
129
130 for frame in frames {
131 if frame.frame.ip().is_null() {
132 continue;
133 }
134
135 dbg.entries(&frame.symbols);
136 }
137
138 dbg.finish()
139 }
140 }
141
142 impl Debug for BacktraceFrame {
143 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
144 let mut dbg = fmt.debug_list();
145 dbg.entries(&self.symbols);
146 dbg.finish()
147 }
148 }
149
150 impl Debug for BacktraceSymbol {
151 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
152 write!(fmt, "{{ ")?;
153
154 if let Some(fn_name) = self.name.as_ref().map(|b| SymbolName::new(b)) {
155 write!(fmt, "fn: \"{:#}\"", fn_name)?;
156 } else {
157 write!(fmt, "fn: <unknown>")?;
158 }
159
160 if let Some(fname) = self.filename.as_ref() {
161 write!(fmt, ", file: \"{:?}\"", fname)?;
162 }
163
164 if let Some(line) = self.lineno {
165 write!(fmt, ", line: {:?}", line)?;
166 }
167
168 write!(fmt, " }}")
169 }
170 }
171
172 impl Debug for BytesOrWide {
173 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
174 output_filename(
175 fmt,
176 match self {
177 BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w),
178 BytesOrWide::Wide(w) => BytesOrWideString::Wide(w),
179 },
180 PrintFmt::Short,
181 env::current_dir().as_ref().ok(),
182 )
183 }
184 }
185
186 impl Backtrace {
187 fn enabled() -> bool {
188 static ENABLED: AtomicUsize = AtomicUsize::new(0);
189 match ENABLED.load(Ordering::Relaxed) {
190 0 => {}
191 1 => return false,
192 _ => return true,
193 }
194 let enabled = match env::var_os("RUST_LIB_BACKTRACE") {
195 Some(s) => s != "0",
196 None => match env::var_os("RUST_BACKTRACE") {
197 Some(s) => s != "0",
198 None => false,
199 },
200 };
201 ENABLED.store(enabled as usize + 1, Ordering::Relaxed);
202 enabled
203 }
204
205 #[inline(never)] // want to make sure there's a frame here to remove
206 pub(crate) fn capture() -> Backtrace {
207 if Backtrace::enabled() {
208 Backtrace::create(Backtrace::capture as usize)
209 } else {
210 let inner = Inner::Disabled;
211 Backtrace { inner }
212 }
213 }
214
215 // Capture a backtrace which starts just before the function addressed
216 // by `ip`
217 fn create(ip: usize) -> Backtrace {
218 let mut frames = Vec::new();
219 let mut actual_start = None;
220 backtrace::trace(|frame| {
221 frames.push(BacktraceFrame {
222 frame: frame.clone(),
223 symbols: Vec::new(),
224 });
225 if frame.symbol_address() as usize == ip && actual_start.is_none() {
226 actual_start = Some(frames.len() + 1);
227 }
228 true
229 });
230
231 // If no frames came out assume that this is an unsupported platform
232 // since `backtrace` doesn't provide a way of learning this right
233 // now, and this should be a good enough approximation.
234 let inner = if frames.is_empty() {
235 Inner::Unsupported
236 } else {
237 Inner::Captured(LazilyResolvedCapture::new(Capture {
238 actual_start: actual_start.unwrap_or(0),
239 frames,
240 resolved: false,
241 }))
242 };
243
244 Backtrace { inner }
245 }
246
247 pub(crate) fn status(&self) -> BacktraceStatus {
248 match self.inner {
249 Inner::Unsupported => BacktraceStatus::Unsupported,
250 Inner::Disabled => BacktraceStatus::Disabled,
251 Inner::Captured(_) => BacktraceStatus::Captured,
252 }
253 }
254 }
255
256 impl Display for Backtrace {
257 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
258 let capture = match &self.inner {
259 Inner::Unsupported => return fmt.write_str("unsupported backtrace"),
260 Inner::Disabled => return fmt.write_str("disabled backtrace"),
261 Inner::Captured(c) => c.force(),
262 };
263
264 let full = fmt.alternate();
265 let (frames, style) = if full {
266 (&capture.frames[..], PrintFmt::Full)
267 } else {
268 (&capture.frames[capture.actual_start..], PrintFmt::Short)
269 };
270
271 // When printing paths we try to strip the cwd if it exists,
272 // otherwise we just print the path as-is. Note that we also only do
273 // this for the short format, because if it's full we presumably
274 // want to print everything.
275 let cwd = env::current_dir();
276 let mut print_path = move |fmt: &mut fmt::Formatter, path: BytesOrWideString| {
277 output_filename(fmt, path, style, cwd.as_ref().ok())
278 };
279
280 let mut f = BacktraceFmt::new(fmt, style, &mut print_path);
281 f.add_context()?;
282 for frame in frames {
283 let mut f = f.frame();
284 if frame.symbols.is_empty() {
285 f.print_raw(frame.frame.ip(), None, None, None)?;
286 } else {
287 for symbol in frame.symbols.iter() {
288 f.print_raw_with_column(
289 frame.frame.ip(),
290 symbol.name.as_ref().map(|b| SymbolName::new(b)),
291 symbol.filename.as_ref().map(|b| match b {
292 BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w),
293 BytesOrWide::Wide(w) => BytesOrWideString::Wide(w),
294 }),
295 symbol.lineno,
296 symbol.colno,
297 )?;
298 }
299 }
300 }
301 f.finish()?;
302 Ok(())
303 }
304 }
305
306 struct LazilyResolvedCapture {
307 sync: Once,
308 capture: UnsafeCell<Capture>,
309 }
310
311 impl LazilyResolvedCapture {
312 fn new(capture: Capture) -> Self {
313 LazilyResolvedCapture {
314 sync: Once::new(),
315 capture: UnsafeCell::new(capture),
316 }
317 }
318
319 fn force(&self) -> &Capture {
320 self.sync.call_once(|| {
321 // Safety: This exclusive reference can't overlap with any
322 // others. `Once` guarantees callers will block until this
323 // closure returns. `Once` also guarantees only a single caller
324 // will enter this closure.
325 unsafe { &mut *self.capture.get() }.resolve();
326 });
327
328 // Safety: This shared reference can't overlap with the exclusive
329 // reference above.
330 unsafe { &*self.capture.get() }
331 }
332 }
333
334 // Safety: Access to the inner value is synchronized using a thread-safe
335 // `Once`. So long as `Capture` is `Sync`, `LazilyResolvedCapture` is too
336 unsafe impl Sync for LazilyResolvedCapture where Capture: Sync {}
337
338 impl Capture {
339 fn resolve(&mut self) {
340 // If we're already resolved, nothing to do!
341 if self.resolved {
342 return;
343 }
344 self.resolved = true;
345
346 for frame in self.frames.iter_mut() {
347 let symbols = &mut frame.symbols;
348 let frame = &frame.frame;
349 backtrace::resolve_frame(frame, |symbol| {
350 symbols.push(BacktraceSymbol {
351 name: symbol.name().map(|m| m.as_bytes().to_vec()),
352 filename: symbol.filename_raw().map(|b| match b {
353 BytesOrWideString::Bytes(b) => BytesOrWide::Bytes(b.to_owned()),
354 BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()),
355 }),
356 lineno: symbol.lineno(),
357 colno: symbol.colno(),
358 });
359 });
360 }
361 }
362 }
363
364 // Prints the filename of the backtrace frame.
365 fn output_filename(
366 fmt: &mut fmt::Formatter,
367 bows: BytesOrWideString,
368 print_fmt: PrintFmt,
369 cwd: Option<&PathBuf>,
370 ) -> fmt::Result {
371 let file: Cow<Path> = match bows {
372 #[cfg(unix)]
373 BytesOrWideString::Bytes(bytes) => {
374 use std::os::unix::ffi::OsStrExt;
375 Path::new(std::ffi::OsStr::from_bytes(bytes)).into()
376 }
377 #[cfg(not(unix))]
378 BytesOrWideString::Bytes(bytes) => {
379 Path::new(std::str::from_utf8(bytes).unwrap_or("<unknown>")).into()
380 }
381 #[cfg(windows)]
382 BytesOrWideString::Wide(wide) => {
383 use std::os::windows::ffi::OsStringExt;
384 Cow::Owned(std::ffi::OsString::from_wide(wide).into())
385 }
386 #[cfg(not(windows))]
387 BytesOrWideString::Wide(_wide) => Path::new("<unknown>").into(),
388 };
389 if print_fmt == PrintFmt::Short && file.is_absolute() {
390 if let Some(cwd) = cwd {
391 if let Ok(stripped) = file.strip_prefix(&cwd) {
392 if let Some(s) = stripped.to_str() {
393 return write!(fmt, ".{}{}", path::MAIN_SEPARATOR, s);
394 }
395 }
396 }
397 }
398 Display::fmt(&file.display(), fmt)
399 }
400}
401
402fn _assert_send_sync() {
403 fn _assert<T: Send + Sync>() {}
404 _assert::<Backtrace>();
405}
406