Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use backtrace formatting from the backtrace crate #64152

Merged
merged 1 commit into from Sep 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock
Expand Up @@ -109,9 +109,9 @@ dependencies = [

[[package]]
name = "backtrace"
version = "0.3.35"
version = "0.3.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1371048253fa3bac6704bfd6bbfc922ee9bdcee8881330d40f308b81cc5adc55"
checksum = "5180c5a20655b14a819b652fd2378fa5f1697b6c9ddad3e695c2f9cedf6df4e2"
dependencies = [
"backtrace-sys",
"cfg-if",
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/Cargo.toml
Expand Up @@ -26,7 +26,7 @@ unwind = { path = "../libunwind" }
hashbrown = { version = "0.5.0", features = ['rustc-dep-of-std'] }

[dependencies.backtrace]
version = "0.3.35"
version = "0.3.37"
default-features = false # don't use coresymbolication on OSX
features = [
"rustc-dep-of-std", # enable build support for integrating into libstd
Expand Down
8 changes: 4 additions & 4 deletions src/libstd/panicking.rs
Expand Up @@ -158,7 +158,7 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {

fn default_hook(info: &PanicInfo<'_>) {
#[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
// for this panic. Otherwise only print it if logging is enabled.
Expand All @@ -167,9 +167,9 @@ fn default_hook(info: &PanicInfo<'_>) {
let panics = update_panic_count(0);

if panics >= 2 {
Some(backtrace::PrintFormat::Full)
Some(backtrace::PrintFmt::Full)
cramertj marked this conversation as resolved.
Show resolved Hide resolved
} else {
backtrace::log_enabled()
backtrace_mod::log_enabled()
}
};

Expand Down Expand Up @@ -197,7 +197,7 @@ fn default_hook(info: &PanicInfo<'_>) {
static FIRST_PANIC: AtomicBool = AtomicBool::new(true);

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) {
let _ = writeln!(err, "note: run with `RUST_BACKTRACE=1` \
environment variable to display a backtrace.");
Expand Down
235 changes: 89 additions & 146 deletions src/libstd/sys_common/backtrace.rs
Expand Up @@ -2,23 +2,20 @@
/// supported platforms.

use crate::env;
use crate::fmt;
use crate::io;
use crate::io::prelude::*;
use crate::mem;
use crate::path::{self, Path};
use crate::ptr;
use crate::sync::atomic::{self, Ordering};
use crate::sys::mutex::Mutex;

use backtrace::{BytesOrWideString, Frame, Symbol};

pub const HEX_WIDTH: usize = 2 + 2 * mem::size_of::<usize>();
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: PrintFormat) -> io::Result<()> {
pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
static LOCK: Mutex = Mutex::new();

// There are issues currently linking libbacktrace into tests, and in
Expand All @@ -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<()> {
writeln!(w, "stack backtrace:")?;
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)
}
}
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 {
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 hit = false;
let mut stop = false;
backtrace::resolve_frame_unsynchronized(frame, |symbol| {
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 stop {
return false;
}
if !hit {
printer.output(frame, None);
res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
}
!printer.done

idx += 1;
res.is_ok()
});
res?;
}
if printer.skipped {
bt_fmt.finish()?;
if skipped {
writeln!(
w,
fmt,
"note: Some details are omitted, \
run with `RUST_BACKTRACE=full` for a verbose backtrace."
)?;
Expand All @@ -77,33 +114,24 @@ where
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
// 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);
match ENABLED.load(Ordering::SeqCst) {
0 => {}
1 => return None,
2 => return Some(PrintFormat::Short),
_ => return Some(PrintFormat::Full),
2 => return Some(PrintFmt::Short),
_ => return Some(PrintFmt::Full),
}

let val = env::var_os("RUST_BACKTRACE").and_then(|x| {
if &x == "0" {
None
} else if &x == "full" {
Some(PrintFormat::Full)
Some(PrintFmt::Full)
} else {
Some(PrintFormat::Short)
Some(PrintFmt::Short)
}
});
ENABLED.store(
Expand All @@ -116,130 +144,45 @@ pub fn log_enabled() -> Option<PrintFormat> {
val
}

struct Printer<'a, 'b> {
format: PrintFormat,
done: bool,
skipped: bool,
idx: usize,
out: &'a mut (dyn Write + 'b),
}

impl<'a, 'b> Printer<'a, 'b> {
fn new(format: PrintFormat, out: &'a mut (dyn Write + 'b)) -> Printer<'a, 'b> {
Printer { format, done: false, skipped: false, idx: 0, out }
}

/// Prints the symbol of the backtrace frame.
///
/// 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() {
self.done = true;
}
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>")?,
/// Prints the filename of the backtrace frame.
///
/// See also `output`.
fn output_filename(
fmt: &mut fmt::Formatter<'_>,
bows: BytesOrWideString<'_>,
print_fmt: PrintFmt,
) -> fmt::Result {
#[cfg(windows)]
let path_buf;
let file = match bows {
#[cfg(unix)]
BytesOrWideString::Bytes(bytes) => {
use crate::os::unix::prelude::*;
Path::new(crate::ffi::OsStr::from_bytes(bytes))
}
self.out.write_all(b"\n")?;
if let Some(sym) = symbol {
self.output_fileline(sym)?;
#[cfg(not(unix))]
BytesOrWideString::Bytes(bytes) => {
Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>"))
}
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)]
let path_buf;
let file = match symbol.filename_raw() {
#[cfg(unix)]
Some(BytesOrWideString::Bytes(bytes)) => {
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, " ")?,
BytesOrWideString::Wide(wide) => {
use crate::os::windows::prelude::*;
path_buf = crate::ffi::OsString::from_wide(wide);
Path::new(&path_buf)
}

let mut already_printed = false;
if self.format == PrintFormat::Short && file.is_absolute() {
if let Ok(cwd) = env::current_dir() {
if let Ok(stripped) = file.strip_prefix(&cwd) {
if let Some(s) = stripped.to_str() {
write!(self.out, " at .{}{}:{}", path::MAIN_SEPARATOR, s, line)?;
already_printed = true;
}
#[cfg(not(windows))]
BytesOrWideString::Wide(_wide) => {
Path::new("<unknown>")
}
};
if print_fmt == PrintFmt::Short && file.is_absolute() {
if let Ok(cwd) = env::current_dir() {
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)
}