anyhow/
backtrace.rs

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