Use backtrace formatting from the backtrace crate
This commit is contained in:
parent
6c18a3d775
commit
290f5b2275
|
@ -109,9 +109,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.35"
|
version = "0.3.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1371048253fa3bac6704bfd6bbfc922ee9bdcee8881330d40f308b81cc5adc55"
|
checksum = "5180c5a20655b14a819b652fd2378fa5f1697b6c9ddad3e695c2f9cedf6df4e2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace-sys",
|
"backtrace-sys",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
|
|
@ -26,7 +26,7 @@ unwind = { path = "../libunwind" }
|
||||||
hashbrown = { version = "0.5.0", features = ['rustc-dep-of-std'] }
|
hashbrown = { version = "0.5.0", features = ['rustc-dep-of-std'] }
|
||||||
|
|
||||||
[dependencies.backtrace]
|
[dependencies.backtrace]
|
||||||
version = "0.3.35"
|
version = "0.3.37"
|
||||||
default-features = false # don't use coresymbolication on OSX
|
default-features = false # don't use coresymbolication on OSX
|
||||||
features = [
|
features = [
|
||||||
"rustc-dep-of-std", # enable build support for integrating into libstd
|
"rustc-dep-of-std", # enable build support for integrating into libstd
|
||||||
|
|
|
@ -158,7 +158,7 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
|
||||||
|
|
||||||
fn default_hook(info: &PanicInfo<'_>) {
|
fn default_hook(info: &PanicInfo<'_>) {
|
||||||
#[cfg(feature = "backtrace")]
|
#[cfg(feature = "backtrace")]
|
||||||
use crate::sys_common::backtrace;
|
use crate::sys_common::{backtrace as backtrace_mod};
|
||||||
|
|
||||||
// If this is a double panic, make sure that we print a backtrace
|
// If this is a double panic, make sure that we print a backtrace
|
||||||
// for this panic. Otherwise only print it if logging is enabled.
|
// for this panic. Otherwise only print it if logging is enabled.
|
||||||
|
@ -167,9 +167,9 @@ fn default_hook(info: &PanicInfo<'_>) {
|
||||||
let panics = update_panic_count(0);
|
let panics = update_panic_count(0);
|
||||||
|
|
||||||
if panics >= 2 {
|
if panics >= 2 {
|
||||||
Some(backtrace::PrintFormat::Full)
|
Some(backtrace::PrintFmt::Full)
|
||||||
} else {
|
} else {
|
||||||
backtrace::log_enabled()
|
backtrace_mod::log_enabled()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ fn default_hook(info: &PanicInfo<'_>) {
|
||||||
static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
|
static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
|
||||||
|
|
||||||
if let Some(format) = log_backtrace {
|
if let Some(format) = log_backtrace {
|
||||||
let _ = backtrace::print(err, format);
|
let _ = backtrace_mod::print(err, format);
|
||||||
} else if FIRST_PANIC.compare_and_swap(true, false, Ordering::SeqCst) {
|
} else if FIRST_PANIC.compare_and_swap(true, false, Ordering::SeqCst) {
|
||||||
let _ = writeln!(err, "note: run with `RUST_BACKTRACE=1` \
|
let _ = writeln!(err, "note: run with `RUST_BACKTRACE=1` \
|
||||||
environment variable to display a backtrace.");
|
environment variable to display a backtrace.");
|
||||||
|
|
|
@ -2,23 +2,20 @@
|
||||||
/// supported platforms.
|
/// supported platforms.
|
||||||
|
|
||||||
use crate::env;
|
use crate::env;
|
||||||
|
use crate::fmt;
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::io::prelude::*;
|
use crate::io::prelude::*;
|
||||||
use crate::mem;
|
|
||||||
use crate::path::{self, Path};
|
use crate::path::{self, Path};
|
||||||
use crate::ptr;
|
|
||||||
use crate::sync::atomic::{self, Ordering};
|
use crate::sync::atomic::{self, Ordering};
|
||||||
use crate::sys::mutex::Mutex;
|
use crate::sys::mutex::Mutex;
|
||||||
|
|
||||||
use backtrace::{BytesOrWideString, Frame, Symbol};
|
use backtrace::{BacktraceFmt, BytesOrWideString, PrintFmt};
|
||||||
|
|
||||||
pub const HEX_WIDTH: usize = 2 + 2 * mem::size_of::<usize>();
|
|
||||||
|
|
||||||
/// Max number of frames to print.
|
/// Max number of frames to print.
|
||||||
const MAX_NB_FRAMES: usize = 100;
|
const MAX_NB_FRAMES: usize = 100;
|
||||||
|
|
||||||
/// Prints the current backtrace.
|
/// Prints the current backtrace.
|
||||||
pub fn print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
|
pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
|
||||||
static LOCK: Mutex = Mutex::new();
|
static LOCK: Mutex = Mutex::new();
|
||||||
|
|
||||||
// There are issues currently linking libbacktrace into tests, and in
|
// There are issues currently linking libbacktrace into tests, and in
|
||||||
|
@ -39,26 +36,66 @@ pub fn print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
|
fn _print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
|
||||||
writeln!(w, "stack backtrace:")?;
|
struct DisplayBacktrace {
|
||||||
|
format: PrintFmt,
|
||||||
|
}
|
||||||
|
impl fmt::Display for DisplayBacktrace {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
_print_fmt(fmt, self.format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write!(w, "{}", DisplayBacktrace { format })
|
||||||
|
}
|
||||||
|
|
||||||
let mut printer = Printer::new(format, w);
|
fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result {
|
||||||
|
let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| {
|
||||||
|
output_filename(fmt, bows, print_fmt)
|
||||||
|
};
|
||||||
|
let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path);
|
||||||
|
bt_fmt.add_context()?;
|
||||||
|
let mut skipped = false;
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let mut idx = 0;
|
||||||
|
let mut res = Ok(());
|
||||||
backtrace::trace_unsynchronized(|frame| {
|
backtrace::trace_unsynchronized(|frame| {
|
||||||
|
if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
|
||||||
|
skipped = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let mut hit = false;
|
let mut hit = false;
|
||||||
|
let mut stop = false;
|
||||||
backtrace::resolve_frame_unsynchronized(frame, |symbol| {
|
backtrace::resolve_frame_unsynchronized(frame, |symbol| {
|
||||||
hit = true;
|
hit = true;
|
||||||
printer.output(frame, Some(symbol));
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res = bt_fmt.frame().symbol(frame, symbol);
|
||||||
});
|
});
|
||||||
if !hit {
|
if stop {
|
||||||
printer.output(frame, None);
|
return false;
|
||||||
}
|
}
|
||||||
!printer.done
|
if !hit {
|
||||||
|
res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
idx += 1;
|
||||||
|
res.is_ok()
|
||||||
});
|
});
|
||||||
|
res?;
|
||||||
}
|
}
|
||||||
if printer.skipped {
|
bt_fmt.finish()?;
|
||||||
|
if skipped {
|
||||||
writeln!(
|
writeln!(
|
||||||
w,
|
fmt,
|
||||||
"note: Some details are omitted, \
|
"note: Some details are omitted, \
|
||||||
run with `RUST_BACKTRACE=full` for a verbose backtrace."
|
run with `RUST_BACKTRACE=full` for a verbose backtrace."
|
||||||
)?;
|
)?;
|
||||||
|
@ -77,33 +114,24 @@ where
|
||||||
f()
|
f()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Controls how the backtrace should be formatted.
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
pub enum PrintFormat {
|
|
||||||
/// Show only relevant data from the backtrace.
|
|
||||||
Short = 2,
|
|
||||||
/// Show all the frames with absolute path for files.
|
|
||||||
Full = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
// For now logging is turned off by default, and this function checks to see
|
// For now logging is turned off by default, and this function checks to see
|
||||||
// whether the magical environment variable is present to see if it's turned on.
|
// whether the magical environment variable is present to see if it's turned on.
|
||||||
pub fn log_enabled() -> Option<PrintFormat> {
|
pub fn log_enabled() -> Option<PrintFmt> {
|
||||||
static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
|
static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
|
||||||
match ENABLED.load(Ordering::SeqCst) {
|
match ENABLED.load(Ordering::SeqCst) {
|
||||||
0 => {}
|
0 => {}
|
||||||
1 => return None,
|
1 => return None,
|
||||||
2 => return Some(PrintFormat::Short),
|
2 => return Some(PrintFmt::Short),
|
||||||
_ => return Some(PrintFormat::Full),
|
_ => return Some(PrintFmt::Full),
|
||||||
}
|
}
|
||||||
|
|
||||||
let val = env::var_os("RUST_BACKTRACE").and_then(|x| {
|
let val = env::var_os("RUST_BACKTRACE").and_then(|x| {
|
||||||
if &x == "0" {
|
if &x == "0" {
|
||||||
None
|
None
|
||||||
} else if &x == "full" {
|
} else if &x == "full" {
|
||||||
Some(PrintFormat::Full)
|
Some(PrintFmt::Full)
|
||||||
} else {
|
} else {
|
||||||
Some(PrintFormat::Short)
|
Some(PrintFmt::Short)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ENABLED.store(
|
ENABLED.store(
|
||||||
|
@ -116,130 +144,45 @@ pub fn log_enabled() -> Option<PrintFormat> {
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Printer<'a, 'b> {
|
/// Prints the filename of the backtrace frame.
|
||||||
format: PrintFormat,
|
///
|
||||||
done: bool,
|
/// See also `output`.
|
||||||
skipped: bool,
|
fn output_filename(
|
||||||
idx: usize,
|
fmt: &mut fmt::Formatter<'_>,
|
||||||
out: &'a mut (dyn Write + 'b),
|
bows: BytesOrWideString<'_>,
|
||||||
}
|
print_fmt: PrintFmt,
|
||||||
|
) -> fmt::Result {
|
||||||
impl<'a, 'b> Printer<'a, 'b> {
|
#[cfg(windows)]
|
||||||
fn new(format: PrintFormat, out: &'a mut (dyn Write + 'b)) -> Printer<'a, 'b> {
|
let path_buf;
|
||||||
Printer { format, done: false, skipped: false, idx: 0, out }
|
let file = match bows {
|
||||||
}
|
#[cfg(unix)]
|
||||||
|
BytesOrWideString::Bytes(bytes) => {
|
||||||
/// Prints the symbol of the backtrace frame.
|
use crate::os::unix::prelude::*;
|
||||||
///
|
Path::new(crate::ffi::OsStr::from_bytes(bytes))
|
||||||
/// These output functions should now be used everywhere to ensure consistency.
|
|
||||||
/// You may want to also use `output_fileline`.
|
|
||||||
fn output(&mut self, frame: &Frame, symbol: Option<&Symbol>) {
|
|
||||||
if self.idx > MAX_NB_FRAMES {
|
|
||||||
self.done = true;
|
|
||||||
self.skipped = true;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if self._output(frame, symbol).is_err() {
|
#[cfg(not(unix))]
|
||||||
self.done = true;
|
BytesOrWideString::Bytes(bytes) => {
|
||||||
|
Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>"))
|
||||||
}
|
}
|
||||||
self.idx += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _output(&mut self, frame: &Frame, symbol: Option<&Symbol>) -> io::Result<()> {
|
|
||||||
if self.format == PrintFormat::Short {
|
|
||||||
if let Some(sym) = symbol.and_then(|s| s.name()).and_then(|s| s.as_str()) {
|
|
||||||
if sym.contains("__rust_begin_short_backtrace") {
|
|
||||||
self.skipped = true;
|
|
||||||
self.done = true;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the `17: 0x0 - <unknown>` line.
|
|
||||||
if self.format == PrintFormat::Short && frame.ip() == ptr::null_mut() {
|
|
||||||
self.skipped = true;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.format {
|
|
||||||
PrintFormat::Full => {
|
|
||||||
write!(self.out, " {:2}: {:2$?} - ", self.idx, frame.ip(), HEX_WIDTH)?
|
|
||||||
}
|
|
||||||
PrintFormat::Short => write!(self.out, " {:2}: ", self.idx)?,
|
|
||||||
}
|
|
||||||
|
|
||||||
match symbol.and_then(|s| s.name()) {
|
|
||||||
Some(symbol) => {
|
|
||||||
match self.format {
|
|
||||||
PrintFormat::Full => write!(self.out, "{}", symbol)?,
|
|
||||||
// Strip the trailing hash if short mode.
|
|
||||||
PrintFormat::Short => write!(self.out, "{:#}", symbol)?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => self.out.write_all(b"<unknown>")?,
|
|
||||||
}
|
|
||||||
self.out.write_all(b"\n")?;
|
|
||||||
if let Some(sym) = symbol {
|
|
||||||
self.output_fileline(sym)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prints the filename and line number of the backtrace frame.
|
|
||||||
///
|
|
||||||
/// See also `output`.
|
|
||||||
fn output_fileline(&mut self, symbol: &Symbol) -> io::Result<()> {
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let path_buf;
|
BytesOrWideString::Wide(wide) => {
|
||||||
let file = match symbol.filename_raw() {
|
use crate::os::windows::prelude::*;
|
||||||
#[cfg(unix)]
|
path_buf = crate::ffi::OsString::from_wide(wide);
|
||||||
Some(BytesOrWideString::Bytes(bytes)) => {
|
Path::new(&path_buf)
|
||||||
use crate::os::unix::prelude::*;
|
|
||||||
Path::new(crate::ffi::OsStr::from_bytes(bytes))
|
|
||||||
}
|
|
||||||
#[cfg(not(unix))]
|
|
||||||
Some(BytesOrWideString::Bytes(bytes)) => {
|
|
||||||
Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>"))
|
|
||||||
}
|
|
||||||
#[cfg(windows)]
|
|
||||||
Some(BytesOrWideString::Wide(wide)) => {
|
|
||||||
use crate::os::windows::prelude::*;
|
|
||||||
path_buf = crate::ffi::OsString::from_wide(wide);
|
|
||||||
Path::new(&path_buf)
|
|
||||||
}
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
Some(BytesOrWideString::Wide(_wide)) => {
|
|
||||||
Path::new("<unknown>")
|
|
||||||
}
|
|
||||||
None => return Ok(()),
|
|
||||||
};
|
|
||||||
let line = match symbol.lineno() {
|
|
||||||
Some(line) => line,
|
|
||||||
None => return Ok(()),
|
|
||||||
};
|
|
||||||
// prior line: " ##: {:2$} - func"
|
|
||||||
self.out.write_all(b"")?;
|
|
||||||
match self.format {
|
|
||||||
PrintFormat::Full => write!(self.out, " {:1$}", "", HEX_WIDTH)?,
|
|
||||||
PrintFormat::Short => write!(self.out, " ")?,
|
|
||||||
}
|
}
|
||||||
|
#[cfg(not(windows))]
|
||||||
let mut already_printed = false;
|
BytesOrWideString::Wide(_wide) => {
|
||||||
if self.format == PrintFormat::Short && file.is_absolute() {
|
Path::new("<unknown>")
|
||||||
if let Ok(cwd) = env::current_dir() {
|
}
|
||||||
if let Ok(stripped) = file.strip_prefix(&cwd) {
|
};
|
||||||
if let Some(s) = stripped.to_str() {
|
if print_fmt == PrintFmt::Short && file.is_absolute() {
|
||||||
write!(self.out, " at .{}{}:{}", path::MAIN_SEPARATOR, s, line)?;
|
if let Ok(cwd) = env::current_dir() {
|
||||||
already_printed = true;
|
if let Ok(stripped) = file.strip_prefix(&cwd) {
|
||||||
}
|
if let Some(s) = stripped.to_str() {
|
||||||
|
return write!(fmt, ".{}{}", path::MAIN_SEPARATOR, s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !already_printed {
|
|
||||||
write!(self.out, " at {}:{}", file.display(), line)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.out.write_all(b"\n")
|
|
||||||
}
|
}
|
||||||
|
fmt::Display::fmt(&file.display(), fmt)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue