summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys_common/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 /library/std/src/sys_common/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 'library/std/src/sys_common/backtrace.rs')
-rw-r--r--library/std/src/sys_common/backtrace.rs183
1 files changed, 183 insertions, 0 deletions
diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs
new file mode 100644
index 000000000..31164afdc
--- /dev/null
+++ b/library/std/src/sys_common/backtrace.rs
@@ -0,0 +1,183 @@
+use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt};
+use crate::borrow::Cow;
+/// Common code for printing the backtrace in the same way across the different
+/// supported platforms.
+use crate::env;
+use crate::fmt;
+use crate::io;
+use crate::io::prelude::*;
+use crate::path::{self, Path, PathBuf};
+use crate::sys_common::mutex::StaticMutex;
+
+/// Max number of frames to print.
+const MAX_NB_FRAMES: usize = 100;
+
+// SAFETY: Don't attempt to lock this reentrantly.
+pub unsafe fn lock() -> impl Drop {
+ static LOCK: StaticMutex = StaticMutex::new();
+ LOCK.lock()
+}
+
+/// Prints the current backtrace.
+pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
+ // There are issues currently linking libbacktrace into tests, and in
+ // general during libstd's own unit tests we're not testing this path. In
+ // test mode immediately return here to optimize away any references to the
+ // libbacktrace symbols
+ if cfg!(test) {
+ return Ok(());
+ }
+
+ // Use a lock to prevent mixed output in multithreading context.
+ // Some platforms also requires it, like `SymFromAddr` on Windows.
+ unsafe {
+ let _lock = lock();
+ _print(w, format)
+ }
+}
+
+unsafe fn _print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
+ struct DisplayBacktrace {
+ format: PrintFmt,
+ }
+ impl fmt::Display for DisplayBacktrace {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ unsafe { _print_fmt(fmt, self.format) }
+ }
+ }
+ write!(w, "{}", DisplayBacktrace { format })
+}
+
+unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result {
+ // Always 'fail' to get the cwd when running under Miri -
+ // this allows Miri to display backtraces in isolation mode
+ let cwd = if !cfg!(miri) { env::current_dir().ok() } else { None };
+
+ let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| {
+ output_filename(fmt, bows, print_fmt, cwd.as_ref())
+ };
+ writeln!(fmt, "stack backtrace:")?;
+ let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path);
+ bt_fmt.add_context()?;
+ let mut idx = 0;
+ let mut res = Ok(());
+ // Start immediately if we're not using a short backtrace.
+ let mut start = print_fmt != PrintFmt::Short;
+ backtrace_rs::trace_unsynchronized(|frame| {
+ if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
+ return false;
+ }
+
+ let mut hit = false;
+ let mut stop = false;
+ backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
+ hit = true;
+ if print_fmt == PrintFmt::Short {
+ if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
+ if start && sym.contains("__rust_begin_short_backtrace") {
+ stop = true;
+ return;
+ }
+ if sym.contains("__rust_end_short_backtrace") {
+ start = true;
+ return;
+ }
+ }
+ }
+
+ if start {
+ res = bt_fmt.frame().symbol(frame, symbol);
+ }
+ });
+ if stop {
+ return false;
+ }
+ if !hit && start {
+ res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
+ }
+
+ idx += 1;
+ res.is_ok()
+ });
+ res?;
+ bt_fmt.finish()?;
+ if print_fmt == PrintFmt::Short {
+ writeln!(
+ fmt,
+ "note: Some details are omitted, \
+ run with `RUST_BACKTRACE=full` for a verbose backtrace."
+ )?;
+ }
+ Ok(())
+}
+
+/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
+/// this is only inline(never) when backtraces in libstd are enabled, otherwise
+/// it's fine to optimize away.
+#[cfg_attr(feature = "backtrace", inline(never))]
+pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
+where
+ F: FnOnce() -> T,
+{
+ let result = f();
+
+ // prevent this frame from being tail-call optimised away
+ crate::hint::black_box(());
+
+ result
+}
+
+/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
+/// this is only inline(never) when backtraces in libstd are enabled, otherwise
+/// it's fine to optimize away.
+#[cfg_attr(feature = "backtrace", inline(never))]
+pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
+where
+ F: FnOnce() -> T,
+{
+ let result = f();
+
+ // prevent this frame from being tail-call optimised away
+ crate::hint::black_box(());
+
+ result
+}
+
+/// Prints the filename of the backtrace frame.
+///
+/// See also `output`.
+pub 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 crate::os::unix::prelude::*;
+ Path::new(crate::ffi::OsStr::from_bytes(bytes)).into()
+ }
+ #[cfg(not(unix))]
+ BytesOrWideString::Bytes(bytes) => {
+ Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>")).into()
+ }
+ #[cfg(windows)]
+ BytesOrWideString::Wide(wide) => {
+ use crate::os::windows::prelude::*;
+ Cow::Owned(crate::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, ".{}{s}", path::MAIN_SEPARATOR);
+ }
+ }
+ }
+ }
+ fmt::Display::fmt(&file.display(), fmt)
+}