about summary refs log tree commit diff
path: root/src/libstd/sys_common
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2019-09-04 13:00:14 -0700
committerAlex Crichton <alex@alexcrichton.com>2019-09-09 08:20:34 -0700
commit34662c69614028944668f96c91ef294e7da048f0 (patch)
tree2bcb69599dfd9ad63c623f69328ee533db8ee212 /src/libstd/sys_common
parent824383d4ab66abd32abc6e19b68d78ecfddcb7d4 (diff)
downloadrust-34662c69614028944668f96c91ef294e7da048f0.tar.gz
rust-34662c69614028944668f96c91ef294e7da048f0.zip
std: Add a `backtrace` module
This commit adds a `backtrace` module to the standard library, as
designed in [RFC 2504]. The `Backtrace` type is intentionally very
conservative, effectively only allowing capturing it and printing it.

Additionally this commit also adds a `backtrace` method to the `Error`
trait which defaults to returning `None`, as specified in [RFC 2504].
More information about the design here can be found in [RFC 2504] and in
the [tracking issue].

Implementation-wise this is all based on the `backtrace` crate and very
closely mirrors the `backtrace::Backtrace` type on crates.io. Otherwise
it's pretty standard in how it handles everything internally.

[RFC 2504]: https://github.com/rust-lang/rfcs/blob/master/text/2504-fix-error.md
[tracking issue]: https://github.com/rust-lang/rust/issues/53487

cc #53487
Diffstat (limited to 'src/libstd/sys_common')
-rw-r--r--src/libstd/sys_common/backtrace.rs119
1 files changed, 65 insertions, 54 deletions
diff --git a/src/libstd/sys_common/backtrace.rs b/src/libstd/sys_common/backtrace.rs
index f434b62aced..839dc51b9e9 100644
--- a/src/libstd/sys_common/backtrace.rs
+++ b/src/libstd/sys_common/backtrace.rs
@@ -4,8 +4,9 @@
 use crate::env;
 use crate::fmt;
 use crate::io;
+use crate::borrow::Cow;
 use crate::io::prelude::*;
-use crate::path::{self, Path};
+use crate::path::{self, Path, PathBuf};
 use crate::sync::atomic::{self, Ordering};
 use crate::sys::mutex::Mutex;
 
@@ -14,10 +15,26 @@ use backtrace::{BacktraceFmt, BytesOrWideString, PrintFmt};
 /// Max number of frames to print.
 const MAX_NB_FRAMES: usize = 100;
 
-/// Prints the current backtrace.
-pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
+pub fn lock() -> impl Drop {
+    struct Guard;
     static LOCK: Mutex = Mutex::new();
 
+    impl Drop for Guard {
+        fn drop(&mut self) {
+            unsafe {
+                LOCK.unlock();
+            }
+        }
+    }
+
+    unsafe {
+        LOCK.lock();
+        return Guard;
+    }
+}
+
+/// 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
@@ -29,71 +46,67 @@ pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
     // Use a lock to prevent mixed output in multithreading context.
     // Some platforms also requires it, like `SymFromAddr` on Windows.
     unsafe {
-        LOCK.lock();
-        let res = _print(w, format);
-        LOCK.unlock();
-        res
+        let _lock = lock();
+        _print(w, format)
     }
 }
 
-fn _print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
+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 {
-            _print_fmt(fmt, self.format)
+            unsafe {
+                _print_fmt(fmt, self.format)
+            }
         }
     }
     write!(w, "{}", DisplayBacktrace { format })
 }
 
-fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result {
+unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result {
+    let cwd = env::current_dir().ok();
     let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| {
-        output_filename(fmt, bows, print_fmt)
+        output_filename(fmt, bows, print_fmt, cwd.as_ref())
     };
     let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path);
     bt_fmt.add_context()?;
-    let mut skipped = false;
-    unsafe {
-        let mut idx = 0;
-        let mut res = Ok(());
-        backtrace::trace_unsynchronized(|frame| {
-            if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
-                skipped = true;
-                return false;
-            }
+    let mut idx = 0;
+    let mut res = Ok(());
+    backtrace::trace_unsynchronized(|frame| {
+        if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
+            return false;
+        }
 
-            let mut hit = false;
-            let mut stop = false;
-            backtrace::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 sym.contains("__rust_begin_short_backtrace") {
-                            skipped = true;
-                            stop = true;
-                            return;
-                        }
+        let mut hit = false;
+        let mut stop = false;
+        backtrace::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 sym.contains("__rust_begin_short_backtrace") {
+                        stop = true;
+                        return;
                     }
                 }
-
-                res = bt_fmt.frame().symbol(frame, symbol);
-            });
-            if stop {
-                return false;
-            }
-            if !hit {
-                res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
             }
 
-            idx += 1;
-            res.is_ok()
+            res = bt_fmt.frame().symbol(frame, symbol);
         });
-        res?;
-    }
+        if stop {
+            return false;
+        }
+        if !hit {
+            res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
+        }
+
+        idx += 1;
+        res.is_ok()
+    });
+    res?;
     bt_fmt.finish()?;
-    if skipped {
+    if print_fmt == PrintFmt::Short {
         writeln!(
             fmt,
             "note: Some details are omitted, \
@@ -147,36 +160,34 @@ pub fn log_enabled() -> Option<PrintFmt> {
 /// Prints the filename of the backtrace frame.
 ///
 /// See also `output`.
-fn output_filename(
+pub fn output_filename(
     fmt: &mut fmt::Formatter<'_>,
     bows: BytesOrWideString<'_>,
     print_fmt: PrintFmt,
+    cwd: Option<&PathBuf>,
 ) -> fmt::Result {
-    #[cfg(windows)]
-    let path_buf;
-    let file = match bows {
+    let file: Cow<'_, Path> = match bows {
         #[cfg(unix)]
         BytesOrWideString::Bytes(bytes) => {
             use crate::os::unix::prelude::*;
-            Path::new(crate::ffi::OsStr::from_bytes(bytes))
+            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>"))
+            Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>")).into()
         }
         #[cfg(windows)]
         BytesOrWideString::Wide(wide) => {
             use crate::os::windows::prelude::*;
-            path_buf = crate::ffi::OsString::from_wide(wide);
-            Path::new(&path_buf)
+            Cow::Owned(crate::ffi::OsString::from_wide(wide).into())
         }
         #[cfg(not(windows))]
         BytesOrWideString::Wide(_wide) => {
-            Path::new("<unknown>")
+            Path::new("<unknown>").into()
         }
     };
     if print_fmt == PrintFmt::Short && file.is_absolute() {
-        if let Ok(cwd) = env::current_dir() {
+        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);