The existing comments were misleading, confusing, and wrong.
Take this comment for example:
```
// Any frames between `__rust_begin_short_backtrace` and `__rust_end_short_backtrace`
// are omitted from the backtrace in short mode, `__rust_end_short_backtrace` will be
// called before the panic hook, so we won't ignore any frames if there is no
// invoke of `__rust_begin_short_backtrace`.
```
this is just wrong. here is an example (full) backtrace:
```
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
Running `/home/jyn/.local/lib/cargo/target/debug/example`
called `Option::unwrap()` on a `None` value
stack backtrace:
0: 0x56499698c595 - std::backtrace_rs::backtrace::libunwind::trace::h5ef2cc16e9a7415a
1: 0x56499698c595 - std::backtrace_rs::backtrace::trace_unsynchronized::h9b5e016e9075f714
2: 0x56499698c595 - std::sys_common::backtrace::_print_fmt::h2f62c7f9ff224e93
3: 0x56499698c595 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hbe51682735731910
4: 0x5649969aa26b - core::fmt::rt::Argument::fmt::h1994ab2b310d665e
5: 0x5649969aa26b - core::fmt::write::hade58a36d63468d7
6: 0x56499698a43f - std::io::Write::write_fmt::h16145587d801a9ab
7: 0x56499698c36e - std::sys_common::backtrace::_print::ha8082e56201dadb4
8: 0x56499698c36e - std::sys_common::backtrace::print::he30f96b4e7f6cbfd
9: 0x56499698d709 - std::panicking::default_hook::{{closure}}::hf0801f6b18a968d3
10: 0x56499698d4ac - std::panicking::default_hook::hd2defec7eda5aeb0
11: 0x56499698dc31 - std::panicking::rust_panic_with_hook::hde93283600065c53
12: 0x56499698daf3 - std::panicking::begin_panic_handler::{{closure}}::h5e151adbdb7ec0c1
13: 0x56499698ca59 - std::sys_common::backtrace::__rust_end_short_backtrace::he36a1407e0f77700
14: 0x56499698d7d4 - rust_begin_unwind
15: 0x5649969a9503 - core::panicking::panic_fmt::h2380d41365f95412
16: 0x5649969a958c - core::panicking::panic::h38cf8db80e8c6e67
17: 0x5649969a93e9 - core::option::unwrap_failed::he72696e53ff29a05
18: 0x5649969722b6 - core::option::Option<T>::unwrap::hb574dc0dc1703062
19: 0x5649969722b6 - example::main::h7a867aafacd93d75
20: 0x5649969721db - core::ops::function::FnOnce::call_once::h734f99a5e57291b7
21: 0x56499697226e - std::sys_common::backtrace::__rust_begin_short_backtrace::h02f5d58c351c4756
22: 0x564996972241 - std::rt::lang_start::{{closure}}::h8b134fe2c31a4355
23: 0x564996988662 - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::h88d7bb571ee2aaf4
24: 0x564996988662 - std::panicking::try::do_call::hfb78dfb6599c871d
25: 0x564996988662 - std::panicking::try::habd041c8c4c8e50c
27: 0x564996988662 - std::rt::lang_start_internal::{{closure}}::h227591a6f9c0879e
28: 0x564996988662 - std::panicking::try::do_call::h3c5878333c38916a
29: 0x564996988662 - std::panicking::try::h5af7b3a127cdae70
31: 0x564996988662 - std::rt::lang_start_internal::hbc85e809eeace0dd
32: 0x56499697221a - std::rt::lang_start::ha1eb16922c9cb224
33: 0x5649969722ee - main
34: 0x7f031962a1ca - __libc_start_call_main
35: 0x7f031962a28b - __libc_start_main_impl
36: 0x5649969720a5 - _start
37: 0x0 - <unknown>
```
note particularly frames 13-21, from start_backtrace to end_backtrace. with PrintFmt::Short, these are the *only* frames that are printed; i.e. we are doing the exact opposite of the comment.
225 lines
7.8 KiB
Rust
225 lines
7.8 KiB
Rust
//! Common code for printing backtraces.
|
|
#![forbid(unsafe_op_in_unsafe_fn)]
|
|
|
|
use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt};
|
|
use crate::borrow::Cow;
|
|
use crate::io::prelude::*;
|
|
use crate::path::{self, Path, PathBuf};
|
|
use crate::sync::{Mutex, MutexGuard, PoisonError};
|
|
use crate::{env, fmt, io};
|
|
|
|
/// Max number of frames to print.
|
|
const MAX_NB_FRAMES: usize = 100;
|
|
|
|
pub(crate) struct BacktraceLock<'a>(#[allow(dead_code)] MutexGuard<'a, ()>);
|
|
|
|
pub(crate) fn lock<'a>() -> BacktraceLock<'a> {
|
|
static LOCK: Mutex<()> = Mutex::new(());
|
|
BacktraceLock(LOCK.lock().unwrap_or_else(PoisonError::into_inner))
|
|
}
|
|
|
|
impl BacktraceLock<'_> {
|
|
/// Prints the current backtrace.
|
|
///
|
|
/// NOTE: this function is not Sync. The caller must hold a mutex lock, or there must be only one thread in the program.
|
|
pub(crate) fn print(&mut self, w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
|
|
// There are issues currently linking libbacktrace into tests, and in
|
|
// general during std'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(());
|
|
}
|
|
|
|
struct DisplayBacktrace {
|
|
format: PrintFmt,
|
|
}
|
|
impl fmt::Display for DisplayBacktrace {
|
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
unsafe { _print_fmt(fmt, self.format) }
|
|
}
|
|
}
|
|
write!(w, "{}", DisplayBacktrace { format })
|
|
}
|
|
}
|
|
|
|
unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result {
|
|
// Always 'fail' to get the cwd when running under Miri -
|
|
// this allows Miri to display backtraces in isolation mode
|
|
let cwd = if !cfg!(miri) { env::current_dir().ok() } else { None };
|
|
|
|
let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| {
|
|
output_filename(fmt, bows, print_fmt, cwd.as_ref())
|
|
};
|
|
writeln!(fmt, "stack backtrace:")?;
|
|
let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path);
|
|
bt_fmt.add_context()?;
|
|
let mut idx = 0;
|
|
let mut res = Ok(());
|
|
let mut omitted_count: usize = 0;
|
|
let mut first_omit = true;
|
|
// If we're using a short backtrace, ignore all frames until we're told to start printing.
|
|
let mut print = print_fmt != PrintFmt::Short;
|
|
set_image_base();
|
|
// SAFETY: we roll our own locking in this town
|
|
unsafe {
|
|
backtrace_rs::trace_unsynchronized(|frame| {
|
|
if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
|
|
return false;
|
|
}
|
|
|
|
let mut hit = false;
|
|
backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
|
|
hit = true;
|
|
|
|
// `__rust_end_short_backtrace` means we are done hiding symbols
|
|
// for now. Print until we see `__rust_begin_short_backtrace`.
|
|
if print_fmt == PrintFmt::Short {
|
|
if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
|
|
if sym.contains("__rust_end_short_backtrace") {
|
|
print = true;
|
|
return;
|
|
}
|
|
if print && sym.contains("__rust_begin_short_backtrace") {
|
|
print = false;
|
|
return;
|
|
}
|
|
if !print {
|
|
omitted_count += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if print {
|
|
if omitted_count > 0 {
|
|
debug_assert!(print_fmt == PrintFmt::Short);
|
|
// only print the message between the middle of frames
|
|
if !first_omit {
|
|
let _ = writeln!(
|
|
bt_fmt.formatter(),
|
|
" [... omitted {} frame{} ...]",
|
|
omitted_count,
|
|
if omitted_count > 1 { "s" } else { "" }
|
|
);
|
|
}
|
|
first_omit = false;
|
|
omitted_count = 0;
|
|
}
|
|
res = bt_fmt.frame().symbol(frame, symbol);
|
|
}
|
|
});
|
|
#[cfg(target_os = "nto")]
|
|
if libc::__my_thread_exit as *mut libc::c_void == frame.ip() {
|
|
if !hit && print {
|
|
use crate::backtrace_rs::SymbolName;
|
|
res = bt_fmt.frame().print_raw(
|
|
frame.ip(),
|
|
Some(SymbolName::new("__my_thread_exit".as_bytes())),
|
|
None,
|
|
None,
|
|
);
|
|
}
|
|
return false;
|
|
}
|
|
if !hit && print {
|
|
res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
|
|
}
|
|
|
|
idx += 1;
|
|
res.is_ok()
|
|
})
|
|
};
|
|
res?;
|
|
bt_fmt.finish()?;
|
|
if print_fmt == PrintFmt::Short {
|
|
writeln!(
|
|
fmt,
|
|
"note: Some details are omitted, \
|
|
run with `RUST_BACKTRACE=full` for a verbose backtrace."
|
|
)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
|
|
/// this is only inline(never) when backtraces in std are enabled, otherwise
|
|
/// it's fine to optimize away.
|
|
#[cfg_attr(feature = "backtrace", inline(never))]
|
|
pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
|
|
where
|
|
F: FnOnce() -> T,
|
|
{
|
|
let result = f();
|
|
|
|
// prevent this frame from being tail-call optimised away
|
|
crate::hint::black_box(());
|
|
|
|
result
|
|
}
|
|
|
|
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
|
|
/// this is only inline(never) when backtraces in std are enabled, otherwise
|
|
/// it's fine to optimize away.
|
|
#[cfg_attr(feature = "backtrace", inline(never))]
|
|
pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
|
|
where
|
|
F: FnOnce() -> T,
|
|
{
|
|
let result = f();
|
|
|
|
// prevent this frame from being tail-call optimised away
|
|
crate::hint::black_box(());
|
|
|
|
result
|
|
}
|
|
|
|
/// Prints the filename of the backtrace frame.
|
|
///
|
|
/// See also `output`.
|
|
pub fn output_filename(
|
|
fmt: &mut fmt::Formatter<'_>,
|
|
bows: BytesOrWideString<'_>,
|
|
print_fmt: PrintFmt,
|
|
cwd: Option<&PathBuf>,
|
|
) -> fmt::Result {
|
|
let file: Cow<'_, Path> = match bows {
|
|
#[cfg(unix)]
|
|
BytesOrWideString::Bytes(bytes) => {
|
|
use crate::os::unix::prelude::*;
|
|
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>")).into()
|
|
}
|
|
#[cfg(windows)]
|
|
BytesOrWideString::Wide(wide) => {
|
|
use crate::os::windows::prelude::*;
|
|
Cow::Owned(crate::ffi::OsString::from_wide(wide).into())
|
|
}
|
|
#[cfg(not(windows))]
|
|
BytesOrWideString::Wide(_wide) => Path::new("<unknown>").into(),
|
|
};
|
|
if print_fmt == PrintFmt::Short && file.is_absolute() {
|
|
if let Some(cwd) = cwd {
|
|
if let Ok(stripped) = file.strip_prefix(&cwd) {
|
|
if let Some(s) = stripped.to_str() {
|
|
return write!(fmt, ".{}{s}", path::MAIN_SEPARATOR);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fmt::Display::fmt(&file.display(), fmt)
|
|
}
|
|
|
|
#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
|
|
pub fn set_image_base() {
|
|
let image_base = crate::os::fortanix_sgx::mem::image_base();
|
|
backtrace_rs::set_image_base(crate::ptr::without_provenance_mut(image_base as _));
|
|
}
|
|
|
|
#[cfg(not(all(target_vendor = "fortanix", target_env = "sgx")))]
|
|
pub fn set_image_base() {
|
|
// nothing to do for platforms other than SGX
|
|
}
|