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