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