summaryrefslogtreecommitdiffstats
path: root/vendor/anyhow/src/backtrace.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /vendor/anyhow/src/backtrace.rs
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/anyhow/src/backtrace.rs')
-rw-r--r--vendor/anyhow/src/backtrace.rs401
1 files changed, 401 insertions, 0 deletions
diff --git a/vendor/anyhow/src/backtrace.rs b/vendor/anyhow/src/backtrace.rs
new file mode 100644
index 000000000..e4c907641
--- /dev/null
+++ b/vendor/anyhow/src/backtrace.rs
@@ -0,0 +1,401 @@
+#[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.backtrace() {
+ 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<BacktraceFrame>,
+ }
+
+ struct BacktraceFrame {
+ frame: Frame,
+ symbols: Vec<BacktraceSymbol>,
+ }
+
+ struct BacktraceSymbol {
+ name: Option<Vec<u8>>,
+ filename: Option<BytesOrWide>,
+ lineno: Option<u32>,
+ colno: Option<u32>,
+ }
+
+ enum BytesOrWide {
+ Bytes(Vec<u8>),
+ Wide(Vec<u16>),
+ }
+
+ impl Debug for Backtrace {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let capture = match &self.inner {
+ Inner::Unsupported => return fmt.write_str("<unsupported>"),
+ Inner::Disabled => return fmt.write_str("<disabled>"),
+ 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: <unknown>")?;
+ }
+
+ 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<Capture>,
+ }
+
+ 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<Path> = 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("<unknown>")).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("<unknown>").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<T: Send + Sync>() {}
+ _assert::<Backtrace>();
+}