1 #[cfg(std_backtrace)]
2 pub(crate) use std::backtrace::{Backtrace, BacktraceStatus};
3 
4 #[cfg(all(not(std_backtrace), feature = "backtrace"))]
5 pub(crate) use self::capture::{Backtrace, BacktraceStatus};
6 
7 #[cfg(not(any(std_backtrace, feature = "backtrace")))]
8 pub(crate) enum Backtrace {}
9 
10 #[cfg(std_backtrace)]
11 macro_rules! impl_backtrace {
12     () => {
13         std::backtrace::Backtrace
14     };
15 }
16 
17 #[cfg(all(not(std_backtrace), feature = "backtrace"))]
18 macro_rules! impl_backtrace {
19     () => {
20         impl core::fmt::Debug + core::fmt::Display
21     };
22 }
23 
24 #[cfg(any(std_backtrace, feature = "backtrace"))]
25 macro_rules! backtrace {
26     () => {
27         Some(crate::backtrace::Backtrace::capture())
28     };
29 }
30 
31 #[cfg(not(any(std_backtrace, feature = "backtrace")))]
32 macro_rules! backtrace {
33     () => {
34         None
35     };
36 }
37 
38 #[cfg(error_generic_member_access)]
39 macro_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 ))]
53 macro_rules! backtrace_if_absent {
54     ($err:expr) => {
55         backtrace!()
56     };
57 }
58 
59 #[cfg(all(feature = "std", not(std_backtrace), not(feature = "backtrace")))]
60 macro_rules! backtrace_if_absent {
61     ($err:expr) => {
62         None
63     };
64 }
65 
66 #[cfg(all(not(std_backtrace), feature = "backtrace"))]
67 mod capture {
68     use backtrace::{BacktraceFmt, BytesOrWideString, Frame, PrintFmt, SymbolName};
69     use core::cell::UnsafeCell;
70     use core::fmt::{self, Debug, Display};
71     use core::sync::atomic::{AtomicUsize, Ordering};
72     use std::borrow::Cow;
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 {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result117         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 {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result143         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 {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result151         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 {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result173         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 {
enabled() -> bool187         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
capture() -> Backtrace206         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`
create(ip: usize) -> Backtrace217         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 
status(&self) -> BacktraceStatus247         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 {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result257         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 {
new(capture: Capture) -> Self312         fn new(capture: Capture) -> Self {
313             LazilyResolvedCapture {
314                 sync: Once::new(),
315                 capture: UnsafeCell::new(capture),
316             }
317         }
318 
force(&self) -> &Capture319         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 {
resolve(&mut self)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.
output_filename( fmt: &mut fmt::Formatter, bows: BytesOrWideString, print_fmt: PrintFmt, cwd: Option<&PathBuf>, ) -> fmt::Result365     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 
_assert_send_sync()402 fn _assert_send_sync() {
403     fn _assert<T: Send + Sync>() {}
404     _assert::<Backtrace>();
405 }
406