2016-12-04 16:38:27 -05:00
|
|
|
/// Common code for printing the backtrace in the same way across the different
|
|
|
|
/// supported platforms.
|
|
|
|
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::env;
|
|
|
|
use crate::io;
|
2019-05-15 07:30:15 -07:00
|
|
|
use crate::io::prelude::*;
|
|
|
|
use crate::mem;
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::path::{self, Path};
|
|
|
|
use crate::ptr;
|
|
|
|
use crate::sync::atomic::{self, Ordering};
|
|
|
|
use crate::sys::mutex::Mutex;
|
|
|
|
|
2019-05-15 07:30:15 -07:00
|
|
|
use backtrace::{BytesOrWideString, Frame, Symbol};
|
|
|
|
|
|
|
|
pub const HEX_WIDTH: usize = 2 + 2 * mem::size_of::<usize>();
|
2016-12-04 16:38:27 -05:00
|
|
|
|
|
|
|
/// Max number of frames to print.
|
|
|
|
const MAX_NB_FRAMES: usize = 100;
|
|
|
|
|
|
|
|
/// Prints the current backtrace.
|
2018-07-10 20:35:36 +02:00
|
|
|
pub fn print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
|
2016-12-04 16:38:27 -05:00
|
|
|
static LOCK: Mutex = Mutex::new();
|
|
|
|
|
2018-12-14 16:47:18 -08:00
|
|
|
// 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) {
|
2019-05-15 07:30:15 -07:00
|
|
|
return Ok(());
|
2018-12-14 16:47:18 -08:00
|
|
|
}
|
|
|
|
|
2016-12-04 16:38:27 -05:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-10 20:35:36 +02:00
|
|
|
fn _print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
|
2016-12-04 16:38:27 -05:00
|
|
|
writeln!(w, "stack backtrace:")?;
|
|
|
|
|
2019-05-15 07:30:15 -07:00
|
|
|
let mut printer = Printer::new(format, w);
|
|
|
|
unsafe {
|
|
|
|
backtrace::trace_unsynchronized(|frame| {
|
|
|
|
let mut hit = false;
|
|
|
|
backtrace::resolve_frame_unsynchronized(frame, |symbol| {
|
|
|
|
hit = true;
|
|
|
|
printer.output(frame, Some(symbol));
|
|
|
|
});
|
|
|
|
if !hit {
|
|
|
|
printer.output(frame, None);
|
2017-03-04 10:27:52 -05:00
|
|
|
}
|
2019-05-15 07:30:15 -07:00
|
|
|
!printer.done
|
|
|
|
});
|
2017-03-04 10:27:52 -05:00
|
|
|
}
|
2019-05-15 07:30:15 -07:00
|
|
|
if printer.skipped {
|
|
|
|
writeln!(
|
|
|
|
w,
|
|
|
|
"note: Some details are omitted, \
|
|
|
|
run with `RUST_BACKTRACE=full` for a verbose backtrace."
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
2017-03-04 10:27:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`.
|
|
|
|
#[inline(never)]
|
|
|
|
pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
|
2019-05-15 07:30:15 -07:00
|
|
|
where
|
|
|
|
F: FnOnce() -> T,
|
|
|
|
F: Send,
|
|
|
|
T: Send,
|
2017-03-04 10:27:52 -05:00
|
|
|
{
|
|
|
|
f()
|
2016-12-04 16:38:27 -05:00
|
|
|
}
|
|
|
|
|
2018-02-16 15:56:50 +01:00
|
|
|
/// Controls how the backtrace should be formatted.
|
2016-12-04 16:38:27 -05:00
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
|
|
pub enum PrintFormat {
|
|
|
|
/// Show only relevant data from the backtrace.
|
2018-03-29 14:59:13 -07:00
|
|
|
Short = 2,
|
|
|
|
/// Show all the frames with absolute path for files.
|
|
|
|
Full = 3,
|
2016-12-04 16:38:27 -05:00
|
|
|
}
|
|
|
|
|
2015-09-08 15:53:46 -07:00
|
|
|
// 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.
|
2016-12-04 16:38:27 -05:00
|
|
|
pub fn log_enabled() -> Option<PrintFormat> {
|
2015-09-08 15:53:46 -07:00
|
|
|
static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
|
|
|
|
match ENABLED.load(Ordering::SeqCst) {
|
2018-03-29 14:59:13 -07:00
|
|
|
0 => {}
|
2016-12-04 16:38:27 -05:00
|
|
|
1 => return None,
|
2018-03-29 14:59:13 -07:00
|
|
|
2 => return Some(PrintFormat::Short),
|
|
|
|
_ => return Some(PrintFormat::Full),
|
2015-09-08 15:53:46 -07:00
|
|
|
}
|
|
|
|
|
2019-05-15 07:30:15 -07:00
|
|
|
let val = env::var_os("RUST_BACKTRACE").and_then(|x| {
|
2018-07-23 22:00:51 -07:00
|
|
|
if &x == "0" {
|
2016-12-04 16:38:27 -05:00
|
|
|
None
|
|
|
|
} else if &x == "full" {
|
|
|
|
Some(PrintFormat::Full)
|
|
|
|
} else {
|
|
|
|
Some(PrintFormat::Short)
|
2018-07-23 22:00:51 -07:00
|
|
|
}
|
2019-05-15 07:30:15 -07:00
|
|
|
});
|
|
|
|
ENABLED.store(
|
|
|
|
match val {
|
|
|
|
Some(v) => v as isize,
|
|
|
|
None => 1,
|
|
|
|
},
|
|
|
|
Ordering::SeqCst,
|
2018-07-23 22:00:51 -07:00
|
|
|
);
|
2016-12-04 16:38:27 -05:00
|
|
|
val
|
2015-09-08 15:53:46 -07:00
|
|
|
}
|
2015-08-26 01:44:55 +01:00
|
|
|
|
2019-05-15 07:30:15 -07:00
|
|
|
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 }
|
2016-12-04 16:38:27 -05:00
|
|
|
}
|
2019-05-15 07:30:15 -07:00
|
|
|
|
|
|
|
/// 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;
|
2015-08-26 01:44:55 +01:00
|
|
|
}
|
2019-05-15 07:30:15 -07:00
|
|
|
|
|
|
|
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(());
|
2018-12-14 14:37:51 -08:00
|
|
|
}
|
|
|
|
}
|
2015-08-26 01:44:55 +01:00
|
|
|
|
2019-05-15 07:30:15 -07:00
|
|
|
match self.format {
|
|
|
|
PrintFormat::Full => {
|
|
|
|
write!(self.out, " {:2}: {:2$?} - ", self.idx, frame.ip(), HEX_WIDTH)?
|
|
|
|
}
|
|
|
|
PrintFormat::Short => write!(self.out, " {:2}: ", self.idx)?,
|
|
|
|
}
|
2016-12-04 16:38:27 -05:00
|
|
|
|
2019-05-15 07:30:15 -07:00
|
|
|
match symbol.and_then(|s| s.name()) {
|
|
|
|
Some(symbol) => {
|
|
|
|
match self.format {
|
|
|
|
PrintFormat::Full => write!(self.out, "{}", symbol)?,
|
2019-02-28 22:43:53 +00:00
|
|
|
// Strip the trailing hash if short mode.
|
2019-05-15 07:30:15 -07:00
|
|
|
PrintFormat::Short => write!(self.out, "{:#}", symbol)?,
|
2016-12-04 16:38:27 -05:00
|
|
|
}
|
|
|
|
}
|
2019-05-15 07:30:15 -07:00
|
|
|
None => self.out.write_all(b"<unknown>")?,
|
2016-12-04 16:38:27 -05:00
|
|
|
}
|
2019-05-15 07:30:15 -07:00
|
|
|
self.out.write_all(b"\n")?;
|
|
|
|
if let Some(sym) = symbol {
|
|
|
|
self.output_fileline(sym)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
2016-12-04 16:38:27 -05:00
|
|
|
}
|
|
|
|
|
2019-05-15 07:30:15 -07:00
|
|
|
/// 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, " ")?,
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !already_printed {
|
|
|
|
write!(self.out, " at {}:{}", file.display(), line)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.out.write_all(b"\n")
|
|
|
|
}
|
2015-08-26 01:44:55 +01:00
|
|
|
}
|