1#[cfg(backtrace)]
2pub(crate) use std::backtrace::{Backtrace, BacktraceStatus};
3
4#[cfg(all(not(backtrace), feature = "backtrace"))]
5pub(crate) use self::capture::{Backtrace, BacktraceStatus};
6
7#[cfg(not(any(backtrace, feature = "backtrace")))]
8pub(crate) enum Backtrace {}
9
10#[cfg(backtrace)]
11macro_rules! impl_backtrace {
12 () => {
13 std::backtrace::Backtrace
14 };
15}
16
17#[cfg(all(not(backtrace), feature = "backtrace"))]
18macro_rules! impl_backtrace {
19 () => {
20 impl core::fmt::Debug + core::fmt::Display
21 };
22}
23
24#[cfg(any(backtrace, feature = "backtrace"))]
25macro_rules! backtrace {
26 () => {
27 Some(crate::backtrace::Backtrace::capture())
28 };
29}
30
31#[cfg(not(any(backtrace, feature = "backtrace")))]
32macro_rules! backtrace {
33 () => {
34 None
35 };
36}
37
38#[cfg(backtrace)]
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(feature = "std", not(backtrace), feature = "backtrace"))]
49macro_rules! backtrace_if_absent {
50 ($err:expr) => {
51 backtrace!()
52 };
53}
54
55#[cfg(all(feature = "std", not(backtrace), not(feature = "backtrace")))]
56macro_rules! backtrace_if_absent {
57 ($err:expr) => {
58 None
59 };
60}
61
62#[cfg(all(not(backtrace), feature = "backtrace"))]
63mod 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 {
113 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 {
139 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 {
147 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 {
169 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 {
183 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
202 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`
213 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
243 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 {
253 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 {
308 fn new(capture: Capture) -> Self {
309 LazilyResolvedCapture {
310 sync: Once::new(),
311 capture: UnsafeCell::new(capture),
312 }
313 }
314
315 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 {
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.
361 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
398fn _assert_send_sync() {
399 fn _assert<T: Send + Sync>() {}
400 _assert::<Backtrace>();
401}
402