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