Auto merge of #64154 - alexcrichton:std-backtrace, r=sfackler
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
This commit is contained in:
commit
fe6d05a8b3
5 changed files with 508 additions and 54 deletions
|
@ -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, \
|
||||
|
@ -153,36 +166,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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue