2016-12-04 22:38:27 +01:00
|
|
|
/// Common code for printing the backtrace in the same way across the different
|
|
|
|
/// supported platforms.
|
|
|
|
|
2019-02-10 20:23:21 +01:00
|
|
|
use crate::env;
|
|
|
|
use crate::io::prelude::*;
|
|
|
|
use crate::io;
|
|
|
|
use crate::path::{self, Path};
|
|
|
|
use crate::ptr;
|
|
|
|
use crate::str;
|
|
|
|
use crate::sync::atomic::{self, Ordering};
|
|
|
|
use crate::sys::mutex::Mutex;
|
|
|
|
|
2018-12-14 23:37:51 +01:00
|
|
|
use rustc_demangle::demangle;
|
2015-09-09 00:53:46 +02:00
|
|
|
|
2019-02-10 20:23:21 +01:00
|
|
|
pub use crate::sys::backtrace::{
|
2016-12-04 22:38:27 +01:00
|
|
|
unwind_backtrace,
|
|
|
|
resolve_symname,
|
|
|
|
foreach_symbol_fileline,
|
|
|
|
BacktraceContext
|
|
|
|
};
|
2014-11-24 04:21:17 +01:00
|
|
|
|
2015-01-16 16:01:02 +01:00
|
|
|
#[cfg(target_pointer_width = "64")]
|
2015-03-26 01:06:52 +01:00
|
|
|
pub const HEX_WIDTH: usize = 18;
|
2015-01-07 05:26:55 +01:00
|
|
|
|
2015-01-16 16:01:02 +01:00
|
|
|
#[cfg(target_pointer_width = "32")]
|
2015-03-26 01:06:52 +01:00
|
|
|
pub const HEX_WIDTH: usize = 10;
|
2014-11-24 04:21:17 +01:00
|
|
|
|
2016-12-04 22:38:27 +01:00
|
|
|
/// Represents an item in the backtrace list. See `unwind_backtrace` for how
|
|
|
|
/// it is created.
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
pub struct Frame {
|
|
|
|
/// Exact address of the call that failed.
|
2017-11-01 21:04:03 +01:00
|
|
|
pub exact_position: *const u8,
|
2016-12-04 22:38:27 +01:00
|
|
|
/// Address of the enclosing function.
|
2017-11-01 21:04:03 +01:00
|
|
|
pub symbol_addr: *const u8,
|
2017-11-15 18:01:09 +01:00
|
|
|
/// Which inlined function is this frame referring to
|
|
|
|
pub inline_context: u32,
|
2016-12-04 22:38:27 +01: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 22:38:27 +01:00
|
|
|
static LOCK: Mutex = Mutex::new();
|
|
|
|
|
2018-12-15 01:47:18 +01: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) {
|
|
|
|
return Ok(())
|
|
|
|
}
|
|
|
|
|
2016-12-04 22:38:27 +01: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 22:38:27 +01:00
|
|
|
let mut frames = [Frame {
|
|
|
|
exact_position: ptr::null(),
|
|
|
|
symbol_addr: ptr::null(),
|
2017-11-15 18:01:09 +01:00
|
|
|
inline_context: 0,
|
2016-12-04 22:38:27 +01:00
|
|
|
}; MAX_NB_FRAMES];
|
|
|
|
let (nb_frames, context) = unwind_backtrace(&mut frames)?;
|
|
|
|
let (skipped_before, skipped_after) =
|
|
|
|
filter_frames(&frames[..nb_frames], format, &context);
|
2017-02-23 19:25:23 +01:00
|
|
|
if skipped_before + skipped_after > 0 {
|
2016-12-04 22:38:27 +01:00
|
|
|
writeln!(w, "note: Some details are omitted, \
|
|
|
|
run with `RUST_BACKTRACE=full` for a verbose backtrace.")?;
|
|
|
|
}
|
|
|
|
writeln!(w, "stack backtrace:")?;
|
|
|
|
|
|
|
|
let filtered_frames = &frames[..nb_frames - skipped_after];
|
|
|
|
for (index, frame) in filtered_frames.iter().skip(skipped_before).enumerate() {
|
|
|
|
resolve_symname(*frame, |symname| {
|
|
|
|
output(w, index, *frame, symname, format)
|
|
|
|
}, &context)?;
|
|
|
|
let has_more_filenames = foreach_symbol_fileline(*frame, |file, line| {
|
|
|
|
output_fileline(w, file, line, format)
|
|
|
|
}, &context)?;
|
|
|
|
if has_more_filenames {
|
|
|
|
w.write_all(b" <... and possibly more>")?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2017-03-04 16:27:52 +01:00
|
|
|
/// Returns a number of frames to remove at the beginning and at the end of the
|
|
|
|
/// backtrace, according to the backtrace format.
|
|
|
|
fn filter_frames(frames: &[Frame],
|
|
|
|
format: PrintFormat,
|
|
|
|
context: &BacktraceContext) -> (usize, usize)
|
2016-12-04 22:38:27 +01:00
|
|
|
{
|
2017-03-04 16:27:52 +01:00
|
|
|
if format == PrintFormat::Full {
|
|
|
|
return (0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
let skipped_before = 0;
|
|
|
|
|
|
|
|
let skipped_after = frames.len() - frames.iter().position(|frame| {
|
|
|
|
let mut is_marker = false;
|
|
|
|
let _ = resolve_symname(*frame, |symname| {
|
|
|
|
if let Some(mangled_symbol_name) = symname {
|
|
|
|
// Use grep to find the concerned functions
|
|
|
|
if mangled_symbol_name.contains("__rust_begin_short_backtrace") {
|
|
|
|
is_marker = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}, context);
|
|
|
|
is_marker
|
|
|
|
}).unwrap_or(frames.len());
|
|
|
|
|
|
|
|
if skipped_before + skipped_after >= frames.len() {
|
|
|
|
// Avoid showing completely empty backtraces
|
|
|
|
return (0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
(skipped_before, skipped_after)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`.
|
|
|
|
#[inline(never)]
|
|
|
|
pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
|
2017-12-24 00:35:38 +01:00
|
|
|
where F: FnOnce() -> T, F: Send, T: Send
|
2017-03-04 16:27:52 +01:00
|
|
|
{
|
|
|
|
f()
|
2016-12-04 22:38:27 +01:00
|
|
|
}
|
|
|
|
|
2018-02-16 15:56:50 +01:00
|
|
|
/// Controls how the backtrace should be formatted.
|
2016-12-04 22:38:27 +01:00
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
|
|
pub enum PrintFormat {
|
|
|
|
/// Show only relevant data from the backtrace.
|
2018-03-29 23:59:13 +02:00
|
|
|
Short = 2,
|
|
|
|
/// Show all the frames with absolute path for files.
|
|
|
|
Full = 3,
|
2016-12-04 22:38:27 +01:00
|
|
|
}
|
|
|
|
|
2015-09-09 00:53:46 +02: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 22:38:27 +01:00
|
|
|
pub fn log_enabled() -> Option<PrintFormat> {
|
2015-09-09 00:53:46 +02:00
|
|
|
static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
|
|
|
|
match ENABLED.load(Ordering::SeqCst) {
|
2018-03-29 23:59:13 +02:00
|
|
|
0 => {}
|
2016-12-04 22:38:27 +01:00
|
|
|
1 => return None,
|
2018-03-29 23:59:13 +02:00
|
|
|
2 => return Some(PrintFormat::Short),
|
|
|
|
_ => return Some(PrintFormat::Full),
|
2015-09-09 00:53:46 +02:00
|
|
|
}
|
|
|
|
|
2018-07-24 07:00:51 +02:00
|
|
|
let val = env::var_os("RUST_BACKTRACE").and_then(|x|
|
|
|
|
if &x == "0" {
|
2016-12-04 22:38:27 +01:00
|
|
|
None
|
|
|
|
} else if &x == "full" {
|
|
|
|
Some(PrintFormat::Full)
|
|
|
|
} else {
|
|
|
|
Some(PrintFormat::Short)
|
2018-07-24 07:00:51 +02:00
|
|
|
}
|
|
|
|
);
|
2016-12-04 22:38:27 +01:00
|
|
|
ENABLED.store(match val {
|
|
|
|
Some(v) => v as isize,
|
|
|
|
None => 1,
|
|
|
|
}, Ordering::SeqCst);
|
|
|
|
val
|
2015-09-09 00:53:46 +02:00
|
|
|
}
|
2015-08-26 02:44:55 +02:00
|
|
|
|
2019-02-09 23:16:58 +01:00
|
|
|
/// Prints the symbol of the backtrace frame.
|
2016-12-04 22:38:27 +01:00
|
|
|
///
|
|
|
|
/// These output functions should now be used everywhere to ensure consistency.
|
|
|
|
/// You may want to also use `output_fileline`.
|
2018-07-10 20:35:36 +02:00
|
|
|
fn output(w: &mut dyn Write, idx: usize, frame: Frame,
|
2016-12-04 22:38:27 +01:00
|
|
|
s: Option<&str>, format: PrintFormat) -> io::Result<()> {
|
|
|
|
// Remove the `17: 0x0 - <unknown>` line.
|
|
|
|
if format == PrintFormat::Short && frame.exact_position == ptr::null() {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
match format {
|
|
|
|
PrintFormat::Full => write!(w,
|
|
|
|
" {:2}: {:2$?} - ",
|
|
|
|
idx,
|
|
|
|
frame.exact_position,
|
|
|
|
HEX_WIDTH)?,
|
|
|
|
PrintFormat::Short => write!(w, " {:2}: ", idx)?,
|
2015-08-26 02:44:55 +02:00
|
|
|
}
|
2016-12-04 22:38:27 +01:00
|
|
|
match s {
|
2018-12-14 23:37:51 +01:00
|
|
|
Some(string) => {
|
|
|
|
let symbol = demangle(string);
|
|
|
|
match format {
|
|
|
|
PrintFormat::Full => write!(w, "{}", symbol)?,
|
|
|
|
// strip the trailing hash if short mode
|
|
|
|
PrintFormat::Short => write!(w, "{:#}", symbol)?,
|
|
|
|
}
|
|
|
|
}
|
2016-12-04 22:38:27 +01:00
|
|
|
None => w.write_all(b"<unknown>")?,
|
|
|
|
}
|
|
|
|
w.write_all(b"\n")
|
2015-08-26 02:44:55 +02:00
|
|
|
}
|
|
|
|
|
2019-02-09 23:16:58 +01:00
|
|
|
/// Prints the filename and line number of the backtrace frame.
|
2016-12-04 22:38:27 +01:00
|
|
|
///
|
|
|
|
/// See also `output`.
|
2015-08-26 02:44:55 +02:00
|
|
|
#[allow(dead_code)]
|
2018-07-10 20:35:36 +02:00
|
|
|
fn output_fileline(w: &mut dyn Write,
|
2017-11-01 21:04:03 +01:00
|
|
|
file: &[u8],
|
|
|
|
line: u32,
|
|
|
|
format: PrintFormat) -> io::Result<()> {
|
2015-08-26 02:44:55 +02:00
|
|
|
// prior line: " ##: {:2$} - func"
|
2016-12-04 22:38:27 +01:00
|
|
|
w.write_all(b"")?;
|
|
|
|
match format {
|
|
|
|
PrintFormat::Full => write!(w,
|
|
|
|
" {:1$}",
|
|
|
|
"",
|
|
|
|
HEX_WIDTH)?,
|
|
|
|
PrintFormat::Short => write!(w, " ")?,
|
2015-08-26 02:44:55 +02:00
|
|
|
}
|
2016-12-04 22:38:27 +01:00
|
|
|
|
|
|
|
let file = str::from_utf8(file).unwrap_or("<unknown>");
|
|
|
|
let file_path = Path::new(file);
|
|
|
|
let mut already_printed = false;
|
|
|
|
if format == PrintFormat::Short && file_path.is_absolute() {
|
|
|
|
if let Ok(cwd) = env::current_dir() {
|
|
|
|
if let Ok(stripped) = file_path.strip_prefix(&cwd) {
|
|
|
|
if let Some(s) = stripped.to_str() {
|
2017-03-17 23:11:27 +01:00
|
|
|
write!(w, " at .{}{}:{}", path::MAIN_SEPARATOR, s, line)?;
|
2016-12-04 22:38:27 +01:00
|
|
|
already_printed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !already_printed {
|
|
|
|
write!(w, " at {}:{}", file, line)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
w.write_all(b"\n")
|
2015-08-26 02:44:55 +02:00
|
|
|
}
|
|
|
|
|