#[cfg(backtrace)] pub(crate) use std::backtrace::{Backtrace, BacktraceStatus}; #[cfg(all(not(backtrace), feature = "backtrace"))] pub(crate) use self::capture::{Backtrace, BacktraceStatus}; #[cfg(not(any(backtrace, feature = "backtrace")))] pub(crate) enum Backtrace {} #[cfg(backtrace)] macro_rules! impl_backtrace { () => { std::backtrace::Backtrace }; } #[cfg(all(not(backtrace), feature = "backtrace"))] macro_rules! impl_backtrace { () => { impl core::fmt::Debug + core::fmt::Display }; } #[cfg(any(backtrace, feature = "backtrace"))] macro_rules! backtrace { () => { Some(crate::backtrace::Backtrace::capture()) }; } #[cfg(not(any(backtrace, feature = "backtrace")))] macro_rules! backtrace { () => { None }; } #[cfg(backtrace)] macro_rules! backtrace_if_absent { ($err:expr) => { match ($err as &dyn std::error::Error).request_ref::() { Some(_) => None, None => backtrace!(), } }; } #[cfg(all(feature = "std", not(backtrace), feature = "backtrace"))] macro_rules! backtrace_if_absent { ($err:expr) => { backtrace!() }; } #[cfg(all(feature = "std", not(backtrace), not(feature = "backtrace")))] macro_rules! backtrace_if_absent { ($err:expr) => { None }; } #[cfg(all(not(backtrace), feature = "backtrace"))] mod capture { use backtrace::{BacktraceFmt, BytesOrWideString, Frame, PrintFmt, SymbolName}; use core::cell::UnsafeCell; use core::fmt::{self, Debug, Display}; use core::sync::atomic::{AtomicUsize, Ordering}; use std::borrow::Cow; use std::env; use std::path::{self, Path, PathBuf}; use std::sync::Once; pub(crate) struct Backtrace { inner: Inner, } pub(crate) enum BacktraceStatus { Unsupported, Disabled, Captured, } enum Inner { Unsupported, Disabled, Captured(LazilyResolvedCapture), } struct Capture { actual_start: usize, resolved: bool, frames: Vec, } struct BacktraceFrame { frame: Frame, symbols: Vec, } struct BacktraceSymbol { name: Option>, filename: Option, lineno: Option, colno: Option, } enum BytesOrWide { Bytes(Vec), Wide(Vec), } impl Debug for Backtrace { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let capture = match &self.inner { Inner::Unsupported => return fmt.write_str(""), Inner::Disabled => return fmt.write_str(""), Inner::Captured(c) => c.force(), }; let frames = &capture.frames[capture.actual_start..]; write!(fmt, "Backtrace ")?; let mut dbg = fmt.debug_list(); for frame in frames { if frame.frame.ip().is_null() { continue; } dbg.entries(&frame.symbols); } dbg.finish() } } impl Debug for BacktraceFrame { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let mut dbg = fmt.debug_list(); dbg.entries(&self.symbols); dbg.finish() } } impl Debug for BacktraceSymbol { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{{ ")?; if let Some(fn_name) = self.name.as_ref().map(|b| SymbolName::new(b)) { write!(fmt, "fn: \"{:#}\"", fn_name)?; } else { write!(fmt, "fn: ")?; } if let Some(fname) = self.filename.as_ref() { write!(fmt, ", file: \"{:?}\"", fname)?; } if let Some(line) = self.lineno { write!(fmt, ", line: {:?}", line)?; } write!(fmt, " }}") } } impl Debug for BytesOrWide { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { output_filename( fmt, match self { BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w), BytesOrWide::Wide(w) => BytesOrWideString::Wide(w), }, PrintFmt::Short, env::current_dir().as_ref().ok(), ) } } impl Backtrace { fn enabled() -> bool { static ENABLED: AtomicUsize = AtomicUsize::new(0); match ENABLED.load(Ordering::Relaxed) { 0 => {} 1 => return false, _ => return true, } let enabled = match env::var_os("RUST_LIB_BACKTRACE") { Some(s) => s != "0", None => match env::var_os("RUST_BACKTRACE") { Some(s) => s != "0", None => false, }, }; ENABLED.store(enabled as usize + 1, Ordering::Relaxed); enabled } #[inline(never)] // want to make sure there's a frame here to remove pub(crate) fn capture() -> Backtrace { if Backtrace::enabled() { Backtrace::create(Backtrace::capture as usize) } else { let inner = Inner::Disabled; Backtrace { inner } } } // Capture a backtrace which starts just before the function addressed // by `ip` fn create(ip: usize) -> Backtrace { let mut frames = Vec::new(); let mut actual_start = None; backtrace::trace(|frame| { frames.push(BacktraceFrame { frame: frame.clone(), symbols: Vec::new(), }); if frame.symbol_address() as usize == ip && actual_start.is_none() { actual_start = Some(frames.len() + 1); } true }); // If no frames came out assume that this is an unsupported platform // since `backtrace` doesn't provide a way of learning this right // now, and this should be a good enough approximation. let inner = if frames.is_empty() { Inner::Unsupported } else { Inner::Captured(LazilyResolvedCapture::new(Capture { actual_start: actual_start.unwrap_or(0), frames, resolved: false, })) }; Backtrace { inner } } pub(crate) fn status(&self) -> BacktraceStatus { match self.inner { Inner::Unsupported => BacktraceStatus::Unsupported, Inner::Disabled => BacktraceStatus::Disabled, Inner::Captured(_) => BacktraceStatus::Captured, } } } impl Display for Backtrace { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let capture = match &self.inner { Inner::Unsupported => return fmt.write_str("unsupported backtrace"), Inner::Disabled => return fmt.write_str("disabled backtrace"), Inner::Captured(c) => c.force(), }; let full = fmt.alternate(); let (frames, style) = if full { (&capture.frames[..], PrintFmt::Full) } else { (&capture.frames[capture.actual_start..], PrintFmt::Short) }; // When printing paths we try to strip the cwd if it exists, // otherwise we just print the path as-is. Note that we also only do // this for the short format, because if it's full we presumably // want to print everything. let cwd = env::current_dir(); let mut print_path = move |fmt: &mut fmt::Formatter, path: BytesOrWideString| { output_filename(fmt, path, style, cwd.as_ref().ok()) }; let mut f = BacktraceFmt::new(fmt, style, &mut print_path); f.add_context()?; for frame in frames { let mut f = f.frame(); if frame.symbols.is_empty() { f.print_raw(frame.frame.ip(), None, None, None)?; } else { for symbol in frame.symbols.iter() { f.print_raw_with_column( frame.frame.ip(), symbol.name.as_ref().map(|b| SymbolName::new(b)), symbol.filename.as_ref().map(|b| match b { BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w), BytesOrWide::Wide(w) => BytesOrWideString::Wide(w), }), symbol.lineno, symbol.colno, )?; } } } f.finish()?; Ok(()) } } struct LazilyResolvedCapture { sync: Once, capture: UnsafeCell, } impl LazilyResolvedCapture { fn new(capture: Capture) -> Self { LazilyResolvedCapture { sync: Once::new(), capture: UnsafeCell::new(capture), } } fn force(&self) -> &Capture { self.sync.call_once(|| { // Safety: This exclusive reference can't overlap with any // others. `Once` guarantees callers will block until this // closure returns. `Once` also guarantees only a single caller // will enter this closure. unsafe { &mut *self.capture.get() }.resolve(); }); // Safety: This shared reference can't overlap with the exclusive // reference above. unsafe { &*self.capture.get() } } } // Safety: Access to the inner value is synchronized using a thread-safe // `Once`. So long as `Capture` is `Sync`, `LazilyResolvedCapture` is too unsafe impl Sync for LazilyResolvedCapture where Capture: Sync {} impl Capture { fn resolve(&mut self) { // If we're already resolved, nothing to do! if self.resolved { return; } self.resolved = true; for frame in self.frames.iter_mut() { let symbols = &mut frame.symbols; let frame = &frame.frame; backtrace::resolve_frame(frame, |symbol| { symbols.push(BacktraceSymbol { name: symbol.name().map(|m| m.as_bytes().to_vec()), filename: symbol.filename_raw().map(|b| match b { BytesOrWideString::Bytes(b) => BytesOrWide::Bytes(b.to_owned()), BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()), }), lineno: symbol.lineno(), colno: symbol.colno(), }); }); } } } // Prints the filename of the backtrace frame. fn output_filename( fmt: &mut fmt::Formatter, bows: BytesOrWideString, print_fmt: PrintFmt, cwd: Option<&PathBuf>, ) -> fmt::Result { let file: Cow = match bows { #[cfg(unix)] BytesOrWideString::Bytes(bytes) => { use std::os::unix::ffi::OsStrExt; Path::new(std::ffi::OsStr::from_bytes(bytes)).into() } #[cfg(not(unix))] BytesOrWideString::Bytes(bytes) => { Path::new(std::str::from_utf8(bytes).unwrap_or("")).into() } #[cfg(windows)] BytesOrWideString::Wide(wide) => { use std::os::windows::ffi::OsStringExt; Cow::Owned(std::ffi::OsString::from_wide(wide).into()) } #[cfg(not(windows))] BytesOrWideString::Wide(_wide) => Path::new("").into(), }; if print_fmt == PrintFmt::Short && file.is_absolute() { if let Some(cwd) = cwd { if let Ok(stripped) = file.strip_prefix(&cwd) { if let Some(s) = stripped.to_str() { return write!(fmt, ".{}{}", path::MAIN_SEPARATOR, s); } } } } Display::fmt(&file.display(), fmt) } } fn _assert_send_sync() { fn _assert() {} _assert::(); }