std: Depend on backtrace crate from crates.io

This commit removes all in-tree support for generating backtraces in
favor of depending on the `backtrace` crate on crates.io. This resolves
a very longstanding piece of duplication where the standard library has
long contained the ability to generate a backtrace on panics, but the
code was later extracted and duplicated on crates.io with the
`backtrace` crate. Since that fork each implementation has seen various
improvements one way or another, but typically `backtrace`-the-crate has
lagged behind libstd in one way or another.

The goal here is to remove this duplication of a fairly critical piece
of code and ensure that there's only one source of truth for generating
backtraces between the standard library and the crate on crates.io.
Recently I've been working to bring the `backtrace` crate on crates.io
up to speed with the support in the standard library which includes:

* Support for `StackWalkEx` on MSVC to recover inline frames with
  debuginfo.
* Using `libbacktrace` by default on MinGW targets.
* Supporting `libbacktrace` on OSX as an option.
* Ensuring all the requisite support in `backtrace`-the-crate compiles
  with `#![no_std]`.
* Updating the `libbacktrace` implementation in `backtrace`-the-crate to
  initialize the global state with the correct filename where necessary.

After reviewing the code in libstd the `backtrace` crate should be at
exact feature parity with libstd today. The backtraces generated should
have the same symbols and same number of frames in general, and there's
not known divergence from libstd currently.

Note that one major difference between libstd's backtrace support and
the `backtrace` crate is that on OSX the crates.io crate enables the
`coresymbolication` feature by default. This feature, however, uses
private internal APIs that aren't published for OSX. While they provide
more accurate backtraces this isn't appropriate for libstd distributed
as a binary, so libstd's dependency on the `backtrace` crate explicitly
disables this feature and forces OSX to use `libbacktrace` as a
symbolication strategy.

The long-term goal of this refactoring is to eventually move us towards
a world where we can drop `libbacktrace` entirely and simply use Gimli
and the surrounding crates for backtrace support. That's still aways off
but hopefully will much more easily enabled by having the source of
truth for backtraces live in crates.io!

Procedurally if we go forward with this I'd like to transfer the
`backtrace-rs` crate to the rust-lang GitHub organization as well, but I
figured I'd hold off on that until we get closer to merging.
This commit is contained in:
Alex Crichton 2019-05-15 07:30:15 -07:00
parent 02f5786a32
commit d1040fe329
35 changed files with 209 additions and 2164 deletions

View File

@ -105,16 +105,23 @@ dependencies = [
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "autocfg"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "backtrace"
version = "0.3.11"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace-sys 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"compiler_builtins 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-std-workspace-core 1.0.0",
]
[[package]]
@ -335,8 +342,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.6"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"compiler_builtins 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-std-workspace-core 1.0.0",
]
[[package]]
name = "chalk-engine"
@ -562,7 +573,7 @@ name = "crc32fast"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -598,7 +609,7 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -612,7 +623,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -624,7 +635,7 @@ name = "crossbeam-utils"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -632,7 +643,7 @@ name = "crossbeam-utils"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -814,7 +825,7 @@ name = "error-chain"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -822,7 +833,7 @@ name = "error-chain"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -837,7 +848,7 @@ name = "failure"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -862,7 +873,7 @@ name = "filetime"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1356,7 +1367,7 @@ name = "log"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1642,7 +1653,7 @@ name = "net2"
version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1711,7 +1722,7 @@ version = "0.10.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1767,7 +1778,7 @@ name = "packed_simd"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -2351,7 +2362,7 @@ name = "rustc"
version = "0.0.0"
dependencies = [
"arena 0.0.0",
"backtrace 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"chalk-engine 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2409,7 +2420,7 @@ name = "rustc-ap-rustc_data_structures"
version = "407.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ena 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"jobserver 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2481,7 +2492,7 @@ name = "rustc-ap-syntax_pos"
version = "407.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-arena 407.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-rustc_data_structures 407.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-ap-serialize 407.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2708,7 +2719,7 @@ dependencies = [
name = "rustc_data_structures"
version = "0.0.0"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ena 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"graphviz 0.0.0",
"jobserver 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3291,7 +3302,7 @@ name = "socket2"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3307,7 +3318,7 @@ name = "std"
version = "0.0.0"
dependencies = [
"alloc 0.0.0",
"backtrace-sys 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)",
"compiler_builtins 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
"core 0.0.0",
@ -3319,7 +3330,6 @@ dependencies = [
"panic_unwind 0.0.0",
"profiler_builtins 0.0.0",
"rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_asan 0.0.0",
"rustc_lsan 0.0.0",
"rustc_msan 0.0.0",
@ -3462,7 +3472,7 @@ name = "syntax_pos"
version = "0.0.0"
dependencies = [
"arena 0.0.0",
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_data_structures 0.0.0",
"rustc_macros 0.1.0",
"scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3486,7 +3496,7 @@ name = "tempfile"
version = "3.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
@ -4091,7 +4101,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
"checksum backtrace 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "18b65ea1161bfb2dd6da6fade5edd4dbd08fba85012123dd333d2fd1b90b2782"
"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf"
"checksum backtrace 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)" = "1c50b4cb6d852a8567d98bb11c03f91ccec4dfbd88778bc1b92789c624081283"
"checksum backtrace-sys 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "6ea90dd7b012b3d1a2cb6bec16670a0db2c95d4e931e84f4047e0460c1b34c8d"
"checksum bit-set 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f1efcc46c18245a69c38fcc5cc650f16d3a59d034f3106e9ed63748f695730a"
"checksum bit-vec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4440d5cb623bb7390ae27fec0bb6c61111969860f8e3ae198bfa0663645e67cf"
@ -4109,7 +4120,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum bytesize 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "716960a18f978640f25101b5cbf1c6f6b0d3192fab36a2d98ca96f0ecbe41010"
"checksum cargo_metadata 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "585784cac9b05c93a53b17a0b24a5cdd1dfdda5256f030e089b549d2390cc720"
"checksum cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5f3fee5eeb60324c2781f1e41286bdee933850fff9b3c672587fed5ec58c83"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
"checksum cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "89431bba4e6b7092fb5fcd00a6f6ca596c55cc26b2f1e6dcdd08a1f4933f66b2"
"checksum chalk-engine 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17ec698a6f053a23bfbe646d9f2fde4b02abc19125595270a99e6f44ae0bdd1a"
"checksum chalk-macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "295635afd6853aa9f20baeb7f0204862440c0fe994c5a253d5f479dac41d047e"
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"

View File

@ -23,8 +23,18 @@ compiler_builtins = { version = "0.1.15" }
profiler_builtins = { path = "../libprofiler_builtins", optional = true }
unwind = { path = "../libunwind" }
hashbrown = { version = "0.3.0", features = ['rustc-dep-of-std'] }
rustc-demangle = { version = "0.1.10", features = ['rustc-dep-of-std'] }
backtrace-sys = { version = "0.1.24", features = ["rustc-dep-of-std"], optional = true }
[dependencies.backtrace]
version = "0.3.25"
default-features = false # don't use coresymbolication on OSX
features = [
"rustc-dep-of-std", # enable build support for integrating into libstd
"dbghelp", # backtrace/symbolize on MSVC
"libbacktrace", # symbolize on most platforms
"libunwind", # backtrace on most platforms
"dladdr", # symbolize on platforms w/o libbacktrace
]
optional = true
[dev-dependencies]
rand = "0.6.1"
@ -51,7 +61,6 @@ cc = "1.0"
[features]
default = ["std_detect_file_io", "std_detect_dlsym_getauxval"]
backtrace = ["backtrace-sys"]
panic-unwind = ["panic_unwind"]
profiler = ["profiler_builtins"]
compiler-builtins-c = ["alloc/compiler-builtins-c"]

View File

@ -1,116 +0,0 @@
use crate::error::Error;
use crate::ffi::CStr;
use crate::fmt;
use crate::intrinsics;
use crate::io;
use crate::sys_common::backtrace::Frame;
use unwind as uw;
pub struct BacktraceContext;
struct Context<'a> {
idx: usize,
frames: &'a mut [Frame],
}
#[derive(Debug)]
struct UnwindError(uw::_Unwind_Reason_Code);
impl Error for UnwindError {
fn description(&self) -> &'static str {
"unexpected return value while unwinding"
}
}
impl fmt::Display for UnwindError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {:?}", self.description(), self.0)
}
}
#[inline(never)] // if we know this is a function call, we can skip it when
// tracing
pub fn unwind_backtrace(frames: &mut [Frame]) -> io::Result<(usize, BacktraceContext)> {
let mut cx = Context { idx: 0, frames };
let result_unwind = unsafe {
uw::_Unwind_Backtrace(trace_fn, &mut cx as *mut Context<'_> as *mut libc::c_void)
};
// See libunwind:src/unwind/Backtrace.c for the return values.
// No, there is no doc.
match result_unwind {
// These return codes seem to be benign and need to be ignored for backtraces
// to show up properly on all tested platforms.
uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => {
Ok((cx.idx, BacktraceContext))
}
_ => Err(io::Error::new(
io::ErrorKind::Other,
UnwindError(result_unwind),
)),
}
}
extern "C" fn trace_fn(
ctx: *mut uw::_Unwind_Context,
arg: *mut libc::c_void,
) -> uw::_Unwind_Reason_Code {
let cx = unsafe { &mut *(arg as *mut Context<'_>) };
if cx.idx >= cx.frames.len() {
return uw::_URC_NORMAL_STOP;
}
let mut ip_before_insn = 0;
let mut ip = unsafe { uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void };
if !ip.is_null() && ip_before_insn == 0 {
// this is a non-signaling frame, so `ip` refers to the address
// after the calling instruction. account for that.
ip = (ip as usize - 1) as *mut _;
}
let symaddr = unsafe { uw::_Unwind_FindEnclosingFunction(ip) };
cx.frames[cx.idx] = Frame {
symbol_addr: symaddr as *mut u8,
exact_position: ip as *mut u8,
inline_context: 0,
};
cx.idx += 1;
uw::_URC_NO_REASON
}
pub fn foreach_symbol_fileline<F>(_: Frame, _: F, _: &BacktraceContext) -> io::Result<bool>
where
F: FnMut(&[u8], u32) -> io::Result<()>,
{
// No way to obtain this information on CloudABI.
Ok(false)
}
pub fn resolve_symname<F>(frame: Frame, callback: F, _: &BacktraceContext) -> io::Result<()>
where
F: FnOnce(Option<&str>) -> io::Result<()>,
{
unsafe {
let mut info: Dl_info = intrinsics::init();
let symname =
if dladdr(frame.exact_position as *mut _, &mut info) == 0 || info.dli_sname.is_null() {
None
} else {
CStr::from_ptr(info.dli_sname).to_str().ok()
};
callback(symname)
}
}
#[repr(C)]
struct Dl_info {
dli_fname: *const libc::c_char,
dli_fbase: *mut libc::c_void,
dli_sname: *const libc::c_char,
dli_saddr: *mut libc::c_void,
}
extern "C" {
fn dladdr(addr: *const libc::c_void, info: *mut Dl_info) -> libc::c_int;
}

View File

@ -4,8 +4,6 @@ use crate::mem;
#[path = "../unix/alloc.rs"]
pub mod alloc;
pub mod args;
#[cfg(feature = "backtrace")]
pub mod backtrace;
#[path = "../unix/cmath.rs"]
pub mod cmath;
pub mod condvar;

View File

@ -1,32 +0,0 @@
/// See sys/unix/backtrace/mod.rs for an explanation of the method used here.
pub use self::tracing::unwind_backtrace;
pub use self::printing::{foreach_symbol_fileline, resolve_symname};
// tracing impls:
mod tracing;
// symbol resolvers:
mod printing;
pub mod gnu {
use crate::io;
use crate::fs;
use crate::vec::Vec;
use crate::ffi::OsStr;
use crate::os::unix::ffi::OsStrExt;
use crate::io::Read;
use libc::c_char;
pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> {
let mut exefile = fs::File::open("sys:exe")?;
let mut exename = Vec::new();
exefile.read_to_end(&mut exename)?;
if exename.last() == Some(&b'\n') {
exename.pop();
}
let file = fs::File::open(OsStr::from_bytes(&exename))?;
Ok((exename.into_iter().map(|c| c as c_char).collect(), file))
}
}
pub struct BacktraceContext;

View File

@ -1 +0,0 @@
pub use crate::sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname};

View File

@ -1,99 +0,0 @@
use crate::error::Error;
use crate::fmt;
use crate::io;
use crate::sys::backtrace::BacktraceContext;
use crate::sys_common::backtrace::Frame;
use unwind as uw;
struct Context<'a> {
idx: usize,
frames: &'a mut [Frame],
}
#[derive(Debug)]
struct UnwindError(uw::_Unwind_Reason_Code);
impl Error for UnwindError {
fn description(&self) -> &'static str {
"unexpected return value while unwinding"
}
}
impl fmt::Display for UnwindError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {:?}", self.description(), self.0)
}
}
#[inline(never)] // if we know this is a function call, we can skip it when
// tracing
pub fn unwind_backtrace(frames: &mut [Frame])
-> io::Result<(usize, BacktraceContext)>
{
let mut cx = Context {
idx: 0,
frames: frames,
};
let result_unwind = unsafe {
uw::_Unwind_Backtrace(trace_fn,
&mut cx as *mut Context<'_>
as *mut libc::c_void)
};
// See libunwind:src/unwind/Backtrace.c for the return values.
// No, there is no doc.
match result_unwind {
// These return codes seem to be benign and need to be ignored for backtraces
// to show up properly on all tested platforms.
uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => {
Ok((cx.idx, BacktraceContext))
}
_ => {
Err(io::Error::new(io::ErrorKind::Other,
UnwindError(result_unwind)))
}
}
}
extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
let cx = unsafe { &mut *(arg as *mut Context<'_>) };
if cx.idx >= cx.frames.len() {
return uw::_URC_NORMAL_STOP;
}
let mut ip_before_insn = 0;
let mut ip = unsafe {
uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void
};
if !ip.is_null() && ip_before_insn == 0 {
// this is a non-signaling frame, so `ip` refers to the address
// after the calling instruction. account for that.
ip = (ip as usize - 1) as *mut _;
}
// dladdr() on osx gets whiny when we use FindEnclosingFunction, and
// it appears to work fine without it, so we only use
// FindEnclosingFunction on non-osx platforms. In doing so, we get a
// slightly more accurate stack trace in the process.
//
// This is often because panic involves the last instruction of a
// function being "call std::rt::begin_unwind", with no ret
// instructions after it. This means that the return instruction
// pointer points *outside* of the calling function, and by
// unwinding it we go back to the original function.
let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
ip
} else {
unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
};
cx.frames[cx.idx] = Frame {
symbol_addr: symaddr as *mut u8,
exact_position: ip as *mut u8,
inline_context: 0,
};
cx.idx += 1;
uw::_URC_NO_REASON
}

View File

@ -8,8 +8,6 @@ pub use self::rand::hashmap_random_keys;
#[path = "../unix/alloc.rs"]
pub mod alloc;
pub mod args;
#[cfg(feature = "backtrace")]
pub mod backtrace;
pub mod cmath;
pub mod condvar;
pub mod env;

View File

@ -1,98 +0,0 @@
use crate::io;
use crate::error::Error;
use crate::fmt;
use crate::sys_common::backtrace::Frame;
use crate::sys::sgx::abi::mem::image_base;
use unwind as uw;
pub struct BacktraceContext;
struct Context<'a> {
idx: usize,
frames: &'a mut [Frame],
}
#[derive(Debug)]
struct UnwindError(uw::_Unwind_Reason_Code);
impl Error for UnwindError {
fn description(&self) -> &'static str {
"unexpected return value while unwinding"
}
}
impl fmt::Display for UnwindError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {:?}", self.description(), self.0)
}
}
#[inline(never)] // this function call can be skipped it when tracing.
pub fn unwind_backtrace(frames: &mut [Frame]) -> io::Result<(usize, BacktraceContext)> {
let mut cx = Context { idx: 0, frames };
let result_unwind = unsafe {
uw::_Unwind_Backtrace(trace_fn, &mut cx as *mut Context<'_> as *mut libc::c_void)
};
// See libunwind:src/unwind/Backtrace.c for the return values.
// No, there is no doc.
let res = match result_unwind {
// These return codes seem to be benign and need to be ignored for backtraces
// to show up properly on all tested platforms.
uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => {
Ok((cx.idx, BacktraceContext))
}
_ => Err(io::Error::new(
io::ErrorKind::Other,
UnwindError(result_unwind),
)),
};
res
}
extern "C" fn trace_fn(
ctx: *mut uw::_Unwind_Context,
arg: *mut libc::c_void,
) -> uw::_Unwind_Reason_Code {
let cx = unsafe { &mut *(arg as *mut Context<'_>) };
if cx.idx >= cx.frames.len() {
return uw::_URC_NORMAL_STOP;
}
let mut ip_before_insn = 0;
let mut ip = unsafe { uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void };
if !ip.is_null() && ip_before_insn == 0 {
// this is a non-signaling frame, so `ip` refers to the address
// after the calling instruction. account for that.
ip = (ip as usize - 1) as *mut _;
}
let symaddr = unsafe { uw::_Unwind_FindEnclosingFunction(ip) };
cx.frames[cx.idx] = Frame {
symbol_addr: symaddr as *mut u8,
exact_position: ip as *mut u8,
inline_context: 0,
};
cx.idx += 1;
uw::_URC_NO_REASON
}
// To reduce TCB size in Sgx enclave, we do not want to implement resolve_symname functionality.
// Rather, we print the offset of the address here, which could be later mapped to correct function.
pub fn resolve_symname<F>(frame: Frame,
callback: F,
_: &BacktraceContext) -> io::Result<()>
where F: FnOnce(Option<&str>) -> io::Result<()>
{
callback(Some(&format!("0x{:x}",
(frame.symbol_addr.wrapping_offset_from(image_base() as _)))))
}
pub fn foreach_symbol_fileline<F>(_: Frame,
_: F,
_: &BacktraceContext) -> io::Result<bool>
where F: FnMut(&[u8], u32) -> io::Result<()>
{
Ok(false)
}

View File

@ -12,8 +12,6 @@ mod waitqueue;
pub mod alloc;
pub mod args;
#[cfg(feature = "backtrace")]
pub mod backtrace;
pub mod cmath;
pub mod condvar;
pub mod env;

View File

@ -1,110 +0,0 @@
/// Backtrace support built on libgcc with some extra OS-specific support
///
/// Some methods of getting a backtrace:
///
/// * The backtrace() functions on unix. It turns out this doesn't work very
/// well for green threads on macOS, and the address to symbol portion of it
/// suffers problems that are described below.
///
/// * Using libunwind. This is more difficult than it sounds because libunwind
/// isn't installed everywhere by default. It's also a bit of a hefty library,
/// so possibly not the best option. When testing, libunwind was excellent at
/// getting both accurate backtraces and accurate symbols across platforms.
/// This route was not chosen in favor of the next option, however.
///
/// * We're already using libgcc_s for exceptions in rust (triggering thread
/// unwinding and running destructors on the stack), and it turns out that it
/// conveniently comes with a function that also gives us a backtrace. All of
/// these functions look like _Unwind_*, but it's not quite the full
/// repertoire of the libunwind API. Due to it already being in use, this was
/// the chosen route of getting a backtrace.
///
/// After choosing libgcc_s for backtraces, the sad part is that it will only
/// give us a stack trace of instruction pointers. Thankfully these instruction
/// pointers are accurate (they work for green and native threads), but it's
/// then up to us again to figure out how to translate these addresses to
/// symbols. As with before, we have a few options. Before, that, a little bit
/// of an interlude about symbols. This is my very limited knowledge about
/// symbol tables, and this information is likely slightly wrong, but the
/// general idea should be correct.
///
/// When talking about symbols, it's helpful to know a few things about where
/// symbols are located. Some symbols are located in the dynamic symbol table
/// of the executable which in theory means that they're available for dynamic
/// linking and lookup. Other symbols end up only in the local symbol table of
/// the file. This loosely corresponds to pub and priv functions in Rust.
///
/// Armed with this knowledge, we know that our solution for address to symbol
/// translation will need to consult both the local and dynamic symbol tables.
/// With that in mind, here's our options of translating an address to
/// a symbol.
///
/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr()
/// behind the scenes to translate, and this is why backtrace() was not used.
/// Conveniently, this method works fantastically on macOS. It appears dladdr()
/// uses magic to consult the local symbol table, or we're putting everything
/// in the dynamic symbol table anyway. Regardless, for macOS, this is the
/// method used for translation. It's provided by the system and easy to do.o
///
/// Sadly, all other systems have a dladdr() implementation that does not
/// consult the local symbol table. This means that most functions are blank
/// because they don't have symbols. This means that we need another solution.
///
/// * Use unw_get_proc_name(). This is part of the libunwind api (not the
/// libgcc_s version of the libunwind api), but involves taking a dependency
/// to libunwind. We may pursue this route in the future if we bundle
/// libunwind, but libunwind was unwieldy enough that it was not chosen at
/// this time to provide this functionality.
///
/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a
/// semi-reasonable solution. The stdlib already knows how to spawn processes,
/// so in theory it could invoke readelf, parse the output, and consult the
/// local/dynamic symbol tables from there. This ended up not getting chosen
/// due to the craziness of the idea plus the advent of the next option.
///
/// * Use `libbacktrace`. It turns out that this is a small library bundled in
/// the gcc repository which provides backtrace and symbol translation
/// functionality. All we really need from it is the backtrace functionality,
/// and we only really need this on everything that's not macOS, so this is the
/// chosen route for now.
///
/// In summary, the current situation uses libgcc_s to get a trace of stack
/// pointers, and we use dladdr() or libbacktrace to translate these addresses
/// to symbols. This is a bit of a hokey implementation as-is, but it works for
/// all unix platforms we support right now, so it at least gets the job done.
pub use self::tracing::unwind_backtrace;
pub use self::printing::{foreach_symbol_fileline, resolve_symname};
// tracing impls:
mod tracing;
// symbol resolvers:
mod printing;
#[cfg(not(target_os = "emscripten"))]
pub mod gnu {
use crate::io;
use crate::fs;
use libc::c_char;
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> {
Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> {
use crate::env;
use crate::os::unix::ffi::OsStrExt;
let filename = env::current_exe()?;
let file = fs::File::open(&filename)?;
let mut filename_cstr: Vec<_> = filename.as_os_str().as_bytes().iter()
.map(|&x| x as c_char).collect();
filename_cstr.push(0); // Null terminate
Ok((filename_cstr, file))
}
}
pub struct BacktraceContext;

View File

@ -1,34 +0,0 @@
use crate::io;
use crate::intrinsics;
use crate::ffi::CStr;
use crate::sys::backtrace::BacktraceContext;
use crate::sys_common::backtrace::Frame;
pub fn resolve_symname<F>(frame: Frame,
callback: F,
_: &BacktraceContext) -> io::Result<()>
where F: FnOnce(Option<&str>) -> io::Result<()>
{
unsafe {
let mut info: Dl_info = intrinsics::init();
let symname = if dladdr(frame.exact_position as *mut _, &mut info) == 0 ||
info.dli_sname.is_null() {
None
} else {
CStr::from_ptr(info.dli_sname).to_str().ok()
};
callback(symname)
}
}
#[repr(C)]
struct Dl_info {
dli_fname: *const libc::c_char,
dli_fbase: *mut libc::c_void,
dli_sname: *const libc::c_char,
dli_saddr: *mut libc::c_void,
}
extern {
fn dladdr(addr: *const libc::c_void, info: *mut Dl_info) -> libc::c_int;
}

View File

@ -1,33 +0,0 @@
mod dladdr;
use crate::sys::backtrace::BacktraceContext;
use crate::sys_common::backtrace::Frame;
use crate::io;
#[cfg(target_os = "emscripten")]
pub use self::dladdr::resolve_symname;
#[cfg(target_os = "emscripten")]
pub fn foreach_symbol_fileline<F>(_: Frame, _: F, _: &BacktraceContext) -> io::Result<bool>
where
F: FnMut(&[u8], u32) -> io::Result<()>
{
Ok(false)
}
#[cfg(not(target_os = "emscripten"))]
pub use crate::sys_common::gnu::libbacktrace::foreach_symbol_fileline;
#[cfg(not(target_os = "emscripten"))]
pub fn resolve_symname<F>(frame: Frame, callback: F, bc: &BacktraceContext) -> io::Result<()>
where
F: FnOnce(Option<&str>) -> io::Result<()>
{
crate::sys_common::gnu::libbacktrace::resolve_symname(frame, |symname| {
if symname.is_some() {
callback(symname)
} else {
dladdr::resolve_symname(frame, callback, bc)
}
}, bc)
}

View File

@ -1,39 +0,0 @@
/// As always - iOS on arm uses SjLj exceptions and
/// _Unwind_Backtrace is even not available there. Still,
/// backtraces could be extracted using a backtrace function,
/// which thanks god is public
///
/// As mentioned in a huge comment block in `super::super`, backtrace
/// doesn't play well with green threads, so while it is extremely nice and
/// simple to use it should be used only on iOS devices as the only viable
/// option.
use crate::io;
use crate::ptr;
use crate::sys::backtrace::BacktraceContext;
use crate::sys_common::backtrace::Frame;
#[inline(never)] // if we know this is a function call, we can skip it when
// tracing
pub fn unwind_backtrace(frames: &mut [Frame])
-> io::Result<(usize, BacktraceContext)>
{
const FRAME_LEN: usize = 100;
assert!(FRAME_LEN >= frames.len());
let mut raw_frames = [ptr::null_mut(); FRAME_LEN];
let nb_frames = unsafe {
backtrace(raw_frames.as_mut_ptr(), raw_frames.len() as libc::c_int)
} as usize;
for (from, to) in raw_frames.iter().zip(frames.iter_mut()).take(nb_frames) {
*to = Frame {
exact_position: *from as *mut u8,
symbol_addr: *from as *mut u8,
inline_context: 0,
};
}
Ok((nb_frames as usize, BacktraceContext))
}
extern {
fn backtrace(buf: *mut *mut libc::c_void, sz: libc::c_int) -> libc::c_int;
}

View File

@ -1,99 +0,0 @@
use crate::error::Error;
use crate::fmt;
use crate::io;
use crate::sys::backtrace::BacktraceContext;
use crate::sys_common::backtrace::Frame;
use unwind as uw;
struct Context<'a> {
idx: usize,
frames: &'a mut [Frame],
}
#[derive(Debug)]
struct UnwindError(uw::_Unwind_Reason_Code);
impl Error for UnwindError {
fn description(&self) -> &'static str {
"unexpected return value while unwinding"
}
}
impl fmt::Display for UnwindError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {:?}", self.description(), self.0)
}
}
#[inline(never)] // if we know this is a function call, we can skip it when
// tracing
pub fn unwind_backtrace(frames: &mut [Frame])
-> io::Result<(usize, BacktraceContext)>
{
let mut cx = Context {
idx: 0,
frames,
};
let result_unwind = unsafe {
uw::_Unwind_Backtrace(trace_fn,
&mut cx as *mut Context<'_>
as *mut libc::c_void)
};
// See libunwind:src/unwind/Backtrace.c for the return values.
// No, there is no doc.
match result_unwind {
// These return codes seem to be benign and need to be ignored for backtraces
// to show up properly on all tested platforms.
uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => {
Ok((cx.idx, BacktraceContext))
}
_ => {
Err(io::Error::new(io::ErrorKind::Other,
UnwindError(result_unwind)))
}
}
}
extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
let cx = unsafe { &mut *(arg as *mut Context<'_>) };
if cx.idx >= cx.frames.len() {
return uw::_URC_NORMAL_STOP;
}
let mut ip_before_insn = 0;
let mut ip = unsafe {
uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void
};
if !ip.is_null() && ip_before_insn == 0 {
// this is a non-signaling frame, so `ip` refers to the address
// after the calling instruction. account for that.
ip = (ip as usize - 1) as *mut _;
}
// dladdr() on osx gets whiny when we use FindEnclosingFunction, and
// it appears to work fine without it, so we only use
// FindEnclosingFunction on non-osx platforms. In doing so, we get a
// slightly more accurate stack trace in the process.
//
// This is often because panic involves the last instruction of a
// function being "call std::rt::begin_unwind", with no ret
// instructions after it. This means that the return instruction
// pointer points *outside* of the calling function, and by
// unwinding it we go back to the original function.
let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
ip
} else {
unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
};
cx.frames[cx.idx] = Frame {
symbol_addr: symaddr as *mut u8,
exact_position: ip as *mut u8,
inline_context: 0,
};
cx.idx += 1;
uw::_URC_NO_REASON
}

View File

@ -1,8 +0,0 @@
pub use self::imp::*;
#[cfg(not(all(target_os = "ios", target_arch = "arm")))]
#[path = "gcc_s.rs"]
mod imp;
#[cfg(all(target_os = "ios", target_arch = "arm"))]
#[path = "backtrace_fn.rs"]
mod imp;

View File

@ -27,8 +27,6 @@ pub mod weak;
pub mod alloc;
pub mod args;
pub mod android;
#[cfg(feature = "backtrace")]
pub mod backtrace;
pub mod cmath;
pub mod condvar;
pub mod env;

View File

@ -1,27 +0,0 @@
use crate::io;
use crate::sys::unsupported;
use crate::sys_common::backtrace::Frame;
pub struct BacktraceContext;
pub fn unwind_backtrace(_frames: &mut [Frame])
-> io::Result<(usize, BacktraceContext)>
{
unsupported()
}
pub fn resolve_symname<F>(_frame: Frame,
_callback: F,
_: &BacktraceContext) -> io::Result<()>
where F: FnOnce(Option<&str>) -> io::Result<()>
{
unsupported()
}
pub fn foreach_symbol_fileline<F>(_: Frame,
_: F,
_: &BacktraceContext) -> io::Result<bool>
where F: FnMut(&[u8], u32) -> io::Result<()>
{
unsupported()
}

View File

@ -21,8 +21,6 @@ use crate::os::raw::c_char;
pub mod alloc;
pub mod args;
#[cfg(feature = "backtrace")]
pub mod backtrace;
#[path = "../wasm/cmath.rs"]
pub mod cmath;
#[path = "../wasm/condvar.rs"]

View File

@ -1,27 +0,0 @@
use crate::io;
use crate::sys::unsupported;
use crate::sys_common::backtrace::Frame;
pub struct BacktraceContext;
pub fn unwind_backtrace(_frames: &mut [Frame])
-> io::Result<(usize, BacktraceContext)>
{
unsupported()
}
pub fn resolve_symname<F>(_frame: Frame,
_callback: F,
_: &BacktraceContext) -> io::Result<()>
where F: FnOnce(Option<&str>) -> io::Result<()>
{
unsupported()
}
pub fn foreach_symbol_fileline<F>(_: Frame,
_: F,
_: &BacktraceContext) -> io::Result<bool>
where F: FnMut(&[u8], u32) -> io::Result<()>
{
unsupported()
}

View File

@ -23,8 +23,6 @@ use crate::time::Duration;
pub mod alloc;
pub mod args;
#[cfg(feature = "backtrace")]
pub mod backtrace;
pub mod cmath;
pub mod env;
pub mod fs;

View File

@ -1,53 +0,0 @@
use crate::io;
use crate::sys::c;
use crate::path::PathBuf;
use crate::fs::{OpenOptions, File};
use crate::sys::ext::fs::OpenOptionsExt;
use crate::sys::handle::Handle;
use libc::c_char;
use super::super::{fill_utf16_buf, os2path, to_u16s, wide_char_to_multi_byte};
fn query_full_process_image_name() -> io::Result<PathBuf> {
unsafe {
let process_handle = Handle::new(c::OpenProcess(c::PROCESS_QUERY_INFORMATION,
c::FALSE,
c::GetCurrentProcessId()));
fill_utf16_buf(|buf, mut sz| {
if c::QueryFullProcessImageNameW(process_handle.raw(), 0, buf, &mut sz) == 0 {
0
} else {
sz
}
}, os2path)
}
}
fn lock_and_get_executable_filename() -> io::Result<(PathBuf, File)> {
// We query the current image name, open the file without FILE_SHARE_DELETE so it
// can't be moved and then get the current image name again. If the names are the
// same than we have successfully locked the file
let image_name1 = query_full_process_image_name()?;
let file = OpenOptions::new()
.read(true)
.share_mode(c::FILE_SHARE_READ | c::FILE_SHARE_WRITE)
.open(&image_name1)?;
let image_name2 = query_full_process_image_name()?;
if image_name1 != image_name2 {
return Err(io::Error::new(io::ErrorKind::Other,
"executable moved while trying to lock it"));
}
Ok((image_name1, file))
}
// Get the executable filename for libbacktrace
// This returns the path in the ANSI code page and a File which should remain open
// for as long as the path should remain valid
pub fn get_executable_filename() -> io::Result<(Vec<c_char>, File)> {
let (executable, file) = lock_and_get_executable_filename()?;
let u16_executable = to_u16s(executable.into_os_string())?;
Ok((wide_char_to_multi_byte(c::CP_ACP, c::WC_NO_BEST_FIT_CHARS,
&u16_executable, true)?, file))
}

View File

@ -1,355 +0,0 @@
//! As always, windows has something very different than unix, we mainly want
//! to avoid having to depend too much on libunwind for windows.
//!
//! If you google around, you'll find a fair bit of references to built-in
//! functions to get backtraces on windows. It turns out that most of these are
//! in an external library called dbghelp. I was unable to find this library
//! via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent
//! of it.
//!
//! You'll also find that there's a function called CaptureStackBackTrace
//! mentioned frequently (which is also easy to use), but sadly I didn't have a
//! copy of that function in my mingw install (maybe it was broken?). Instead,
//! this takes the route of using StackWalk64 in order to walk the stack.
#![allow(deprecated)] // dynamic_lib
use crate::io;
use crate::mem;
use crate::ptr;
use crate::sys::c;
use crate::sys::dynamic_lib::DynamicLibrary;
use crate::sys_common::backtrace::Frame;
use libc::c_void;
macro_rules! sym {
($lib:expr, $e:expr, $t:ident) => (
$lib.symbol($e).map(|f| unsafe {
$crate::mem::transmute::<usize, $t>(f)
})
)
}
mod printing;
#[cfg(target_env = "gnu")]
#[path = "backtrace_gnu.rs"]
pub mod gnu;
pub use self::printing::{foreach_symbol_fileline, resolve_symname};
use self::printing::{load_printing_fns_64, load_printing_fns_ex};
pub fn unwind_backtrace(frames: &mut [Frame]) -> io::Result<(usize, BacktraceContext)> {
let dbghelp = DynamicLibrary::open("dbghelp.dll")?;
// Fetch the symbols necessary from dbghelp.dll
let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn)?;
let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn)?;
// StackWalkEx might not be present and we'll fall back to StackWalk64
let sw_var = match sym!(dbghelp, "StackWalkEx", StackWalkExFn) {
Ok(StackWalkEx) => {
StackWalkVariant::StackWalkEx(StackWalkEx, load_printing_fns_ex(&dbghelp)?)
}
Err(e) => match sym!(dbghelp, "StackWalk64", StackWalk64Fn) {
Ok(StackWalk64) => {
StackWalkVariant::StackWalk64(StackWalk64, load_printing_fns_64(&dbghelp)?)
}
Err(..) => return Err(e),
},
};
// Allocate necessary structures for doing the stack walk
let process = unsafe { c::GetCurrentProcess() };
let backtrace_context = BacktraceContext {
handle: process,
SymCleanup,
StackWalkVariant: sw_var,
dbghelp,
};
// Initialize this process's symbols
let ret = unsafe { SymInitialize(process, ptr::null_mut(), c::TRUE) };
if ret != c::TRUE {
return Ok((0, backtrace_context));
}
// And now that we're done with all the setup, do the stack walking!
match backtrace_context.StackWalkVariant {
StackWalkVariant::StackWalkEx(StackWalkEx, _) => {
set_frames(StackWalkEx, frames).map(|i| (i, backtrace_context))
}
StackWalkVariant::StackWalk64(StackWalk64, _) => {
set_frames(StackWalk64, frames).map(|i| (i, backtrace_context))
}
}
}
fn set_frames<W: StackWalker>(StackWalk: W, frames: &mut [Frame]) -> io::Result<usize> {
let process = unsafe { c::GetCurrentProcess() };
let thread = unsafe { c::GetCurrentProcess() };
let mut context: c::CONTEXT = unsafe { mem::zeroed() };
unsafe { c::RtlCaptureContext(&mut context) };
let mut frame = W::Item::new();
let image = frame.init(&context);
let mut i = 0;
while i < frames.len()
&& StackWalk.walk(image, process, thread, &mut frame, &mut context) == c::TRUE
{
let addr = frame.get_addr();
frames[i] = Frame {
symbol_addr: addr,
exact_position: addr,
inline_context: frame.get_inline_context(),
};
i += 1
}
Ok(i)
}
type SymInitializeFn = unsafe extern "system" fn(c::HANDLE, *mut c_void, c::BOOL) -> c::BOOL;
type SymCleanupFn = unsafe extern "system" fn(c::HANDLE) -> c::BOOL;
type StackWalkExFn = unsafe extern "system" fn(
c::DWORD,
c::HANDLE,
c::HANDLE,
*mut c::STACKFRAME_EX,
*mut c::CONTEXT,
*mut c_void,
*mut c_void,
*mut c_void,
*mut c_void,
c::DWORD,
) -> c::BOOL;
type StackWalk64Fn = unsafe extern "system" fn(
c::DWORD,
c::HANDLE,
c::HANDLE,
*mut c::STACKFRAME64,
*mut c::CONTEXT,
*mut c_void,
*mut c_void,
*mut c_void,
*mut c_void,
) -> c::BOOL;
trait StackWalker {
type Item: StackFrame;
fn walk(
&self,
_: c::DWORD,
_: c::HANDLE,
_: c::HANDLE,
_: &mut Self::Item,
_: &mut c::CONTEXT
) -> c::BOOL;
}
impl StackWalker for StackWalkExFn {
type Item = c::STACKFRAME_EX;
fn walk(
&self,
image: c::DWORD,
process: c::HANDLE,
thread: c::HANDLE,
frame: &mut Self::Item,
context: &mut c::CONTEXT,
) -> c::BOOL {
unsafe {
self(
image,
process,
thread,
frame,
context,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0,
)
}
}
}
impl StackWalker for StackWalk64Fn {
type Item = c::STACKFRAME64;
fn walk(
&self,
image: c::DWORD,
process: c::HANDLE,
thread: c::HANDLE,
frame: &mut Self::Item,
context: &mut c::CONTEXT,
) -> c::BOOL {
unsafe {
self(
image,
process,
thread,
frame,
context,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
)
}
}
}
trait StackFrame {
fn new() -> Self;
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD;
fn get_addr(&self) -> *const u8;
fn get_inline_context(&self) -> u32;
}
impl StackFrame for c::STACKFRAME_EX {
fn new() -> c::STACKFRAME_EX {
unsafe { mem::zeroed() }
}
#[cfg(target_arch = "x86")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Eip as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Esp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.Ebp as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_I386
}
#[cfg(target_arch = "x86_64")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Rip as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Rsp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.Rbp as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_AMD64
}
#[cfg(target_arch = "arm")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Pc as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Sp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.R11 as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_ARMNT
}
#[cfg(target_arch = "aarch64")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Pc as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Sp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.Fp as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_ARM64
}
fn get_addr(&self) -> *const u8 {
(self.AddrPC.Offset - 1) as *const u8
}
fn get_inline_context(&self) -> u32 {
self.InlineFrameContext
}
}
impl StackFrame for c::STACKFRAME64 {
fn new() -> c::STACKFRAME64 {
unsafe { mem::zeroed() }
}
#[cfg(target_arch = "x86")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Eip as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Esp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.Ebp as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_I386
}
#[cfg(target_arch = "x86_64")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Rip as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Rsp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.Rbp as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_AMD64
}
#[cfg(target_arch = "arm")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Pc as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Sp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.R11 as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_ARMNT
}
#[cfg(target_arch = "aarch64")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Pc as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Sp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.Fp as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_ARM64
}
fn get_addr(&self) -> *const u8 {
(self.AddrPC.Offset - 1) as *const u8
}
fn get_inline_context(&self) -> u32 {
0
}
}
enum StackWalkVariant {
StackWalkEx(StackWalkExFn, printing::PrintingFnsEx),
StackWalk64(StackWalk64Fn, printing::PrintingFns64),
}
pub struct BacktraceContext {
handle: c::HANDLE,
SymCleanup: SymCleanupFn,
// Only used in printing for msvc and not gnu
// The gnu version is effectively a ZST dummy.
#[allow(dead_code)]
StackWalkVariant: StackWalkVariant,
// keeping DynamycLibrary loaded until its functions no longer needed
#[allow(dead_code)]
dbghelp: DynamicLibrary,
}
impl Drop for BacktraceContext {
fn drop(&mut self) {
unsafe {
(self.SymCleanup)(self.handle);
}
}
}

View File

@ -1,24 +0,0 @@
#[cfg(target_env = "msvc")]
#[path = "msvc.rs"]
mod printing;
#[cfg(target_env = "gnu")]
mod printing {
pub use crate::sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname};
// dummy functions to mirror those present in msvc version.
use crate::sys::dynamic_lib::DynamicLibrary;
use crate::io;
pub struct PrintingFnsEx {}
pub struct PrintingFns64 {}
pub fn load_printing_fns_ex(_: &DynamicLibrary) -> io::Result<PrintingFnsEx> {
Ok(PrintingFnsEx{})
}
pub fn load_printing_fns_64(_: &DynamicLibrary) -> io::Result<PrintingFns64> {
Ok(PrintingFns64{})
}
}
pub use self::printing::{foreach_symbol_fileline, resolve_symname};
pub use self::printing::{load_printing_fns_ex, load_printing_fns_64,
PrintingFnsEx, PrintingFns64};

View File

@ -1,208 +0,0 @@
use crate::ffi::CStr;
use crate::io;
use crate::mem;
use crate::sys::backtrace::BacktraceContext;
use crate::sys::backtrace::StackWalkVariant;
use crate::sys::c;
use crate::sys::dynamic_lib::DynamicLibrary;
use crate::sys_common::backtrace::Frame;
use libc::{c_char, c_ulong};
// Structs holding printing functions and loaders for them
// Two versions depending on whether dbghelp.dll has StackWalkEx or not
// (the former being in newer Windows versions, the older being in Win7 and before)
pub struct PrintingFnsEx {
resolve_symname: SymFromInlineContextFn,
sym_get_line: SymGetLineFromInlineContextFn,
}
pub struct PrintingFns64 {
resolve_symname: SymFromAddrFn,
sym_get_line: SymGetLineFromAddr64Fn,
}
pub fn load_printing_fns_ex(dbghelp: &DynamicLibrary) -> io::Result<PrintingFnsEx> {
Ok(PrintingFnsEx {
resolve_symname: sym!(dbghelp, "SymFromInlineContext", SymFromInlineContextFn)?,
sym_get_line: sym!(
dbghelp,
"SymGetLineFromInlineContext",
SymGetLineFromInlineContextFn
)?,
})
}
pub fn load_printing_fns_64(dbghelp: &DynamicLibrary) -> io::Result<PrintingFns64> {
Ok(PrintingFns64 {
resolve_symname: sym!(dbghelp, "SymFromAddr", SymFromAddrFn)?,
sym_get_line: sym!(dbghelp, "SymGetLineFromAddr64", SymGetLineFromAddr64Fn)?,
})
}
type SymFromAddrFn =
unsafe extern "system" fn(c::HANDLE, u64, *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL;
type SymFromInlineContextFn =
unsafe extern "system" fn(c::HANDLE, u64, c::ULONG, *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL;
type SymGetLineFromAddr64Fn =
unsafe extern "system" fn(c::HANDLE, u64, *mut u32, *mut c::IMAGEHLP_LINE64) -> c::BOOL;
type SymGetLineFromInlineContextFn = unsafe extern "system" fn(
c::HANDLE,
u64,
c::ULONG,
u64,
*mut c::DWORD,
*mut c::IMAGEHLP_LINE64,
) -> c::BOOL;
/// Converts a pointer to symbol to its string value.
pub fn resolve_symname<F>(frame: Frame, callback: F, context: &BacktraceContext) -> io::Result<()>
where
F: FnOnce(Option<&str>) -> io::Result<()>,
{
match context.StackWalkVariant {
StackWalkVariant::StackWalkEx(_, ref fns) => resolve_symname_internal(
|process: c::HANDLE,
symbol_address: u64,
inline_context: c::ULONG,
info: *mut c::SYMBOL_INFO| unsafe {
let mut displacement = 0u64;
(fns.resolve_symname)(
process,
symbol_address,
inline_context,
&mut displacement,
info,
)
},
frame,
callback,
context,
),
StackWalkVariant::StackWalk64(_, ref fns) => resolve_symname_internal(
|process: c::HANDLE,
symbol_address: u64,
_inline_context: c::ULONG,
info: *mut c::SYMBOL_INFO| unsafe {
let mut displacement = 0u64;
(fns.resolve_symname)(process, symbol_address, &mut displacement, info)
},
frame,
callback,
context,
),
}
}
fn resolve_symname_internal<F, R>(
mut symbol_resolver: R,
frame: Frame,
callback: F,
context: &BacktraceContext,
) -> io::Result<()>
where
F: FnOnce(Option<&str>) -> io::Result<()>,
R: FnMut(c::HANDLE, u64, c::ULONG, *mut c::SYMBOL_INFO) -> c::BOOL,
{
unsafe {
let mut info: c::SYMBOL_INFO = mem::zeroed();
info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
// the struct size in C. the value is different to
// `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
// due to struct alignment.
info.SizeOfStruct = 88;
let ret = symbol_resolver(
context.handle,
frame.symbol_addr as u64,
frame.inline_context,
&mut info,
);
let valid_range = if ret == c::TRUE && frame.symbol_addr as usize >= info.Address as usize {
if info.Size != 0 {
(frame.symbol_addr as usize) < info.Address as usize + info.Size as usize
} else {
true
}
} else {
false
};
let symname = if valid_range {
let ptr = info.Name.as_ptr() as *const c_char;
CStr::from_ptr(ptr).to_str().ok()
} else {
None
};
callback(symname)
}
}
pub fn foreach_symbol_fileline<F>(
frame: Frame,
callback: F,
context: &BacktraceContext,
) -> io::Result<bool>
where
F: FnMut(&[u8], u32) -> io::Result<()>,
{
match context.StackWalkVariant {
StackWalkVariant::StackWalkEx(_, ref fns) => foreach_symbol_fileline_iternal(
|process: c::HANDLE,
frame_address: u64,
inline_context: c::ULONG,
line: *mut c::IMAGEHLP_LINE64| unsafe {
let mut displacement = 0u32;
(fns.sym_get_line)(
process,
frame_address,
inline_context,
0,
&mut displacement,
line,
)
},
frame,
callback,
context,
),
StackWalkVariant::StackWalk64(_, ref fns) => foreach_symbol_fileline_iternal(
|process: c::HANDLE,
frame_address: u64,
_inline_context: c::ULONG,
line: *mut c::IMAGEHLP_LINE64| unsafe {
let mut displacement = 0u32;
(fns.sym_get_line)(process, frame_address, &mut displacement, line)
},
frame,
callback,
context,
),
}
}
fn foreach_symbol_fileline_iternal<F, G>(
mut line_getter: G,
frame: Frame,
mut callback: F,
context: &BacktraceContext,
) -> io::Result<bool>
where
F: FnMut(&[u8], u32) -> io::Result<()>,
G: FnMut(c::HANDLE, u64, c::ULONG, *mut c::IMAGEHLP_LINE64) -> c::BOOL,
{
unsafe {
let mut line: c::IMAGEHLP_LINE64 = mem::zeroed();
line.SizeOfStruct = mem::size_of::<c::IMAGEHLP_LINE64>() as u32;
let ret = line_getter(
context.handle,
frame.exact_position as u64,
frame.inline_context,
&mut line,
);
if ret == c::TRUE {
let name = CStr::from_ptr(line.Filename).to_bytes();
callback(name, line.LineNumber as u32)?;
}
Ok(false)
}
}

View File

@ -5,8 +5,6 @@
#![unstable(issue = "0", feature = "windows_c")]
use crate::os::raw::{c_int, c_uint, c_ulong, c_long, c_longlong, c_ushort, c_char};
#[cfg(target_arch = "x86_64")]
use crate::os::raw::c_ulonglong;
use crate::ptr;
use libc::{wchar_t, size_t, c_void};
@ -33,10 +31,6 @@ pub type WORD = u16;
pub type CHAR = c_char;
pub type ULONG_PTR = usize;
pub type ULONG = c_ulong;
#[cfg(target_arch = "x86_64")]
pub type ULONGLONG = u64;
#[cfg(target_arch = "x86_64")]
pub type DWORDLONG = ULONGLONG;
pub type LPBOOL = *mut BOOL;
pub type LPBYTE = *mut BYTE;
@ -108,11 +102,6 @@ pub const SECURITY_SQOS_PRESENT: DWORD = 0x00100000;
pub const FIONBIO: c_ulong = 0x8004667e;
#[cfg(target_arch = "arm")]
const ARM_MAX_BREAKPOINTS: usize = 8;
#[cfg(target_arch = "arm")]
const ARM_MAX_WATCHPOINTS: usize = 1;
#[repr(C)]
#[derive(Copy)]
pub struct WIN32_FIND_DATAW {
@ -270,22 +259,6 @@ pub const WAIT_OBJECT_0: DWORD = 0x00000000;
pub const WAIT_TIMEOUT: DWORD = 258;
pub const WAIT_FAILED: DWORD = 0xFFFFFFFF;
#[cfg(target_env = "msvc")]
#[cfg(feature = "backtrace")]
pub const MAX_SYM_NAME: usize = 2000;
#[cfg(target_arch = "x86")]
#[cfg(feature = "backtrace")]
pub const IMAGE_FILE_MACHINE_I386: DWORD = 0x014c;
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "backtrace")]
pub const IMAGE_FILE_MACHINE_AMD64: DWORD = 0x8664;
#[cfg(target_arch = "aarch64")]
#[cfg(feature = "backtrace")]
pub const IMAGE_FILE_MACHINE_ARM64: DWORD = 0xAA64;
#[cfg(target_arch = "arm")]
#[cfg(feature = "backtrace")]
pub const IMAGE_FILE_MACHINE_ARMNT: DWORD = 0x01c4;
pub const EXCEPTION_CONTINUE_SEARCH: LONG = 0;
pub const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd;
pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15;
@ -580,41 +553,6 @@ pub struct OVERLAPPED {
pub hEvent: HANDLE,
}
#[repr(C)]
#[cfg(target_env = "msvc")]
#[cfg(feature = "backtrace")]
pub struct SYMBOL_INFO {
pub SizeOfStruct: c_ulong,
pub TypeIndex: c_ulong,
pub Reserved: [u64; 2],
pub Index: c_ulong,
pub Size: c_ulong,
pub ModBase: u64,
pub Flags: c_ulong,
pub Value: u64,
pub Address: u64,
pub Register: c_ulong,
pub Scope: c_ulong,
pub Tag: c_ulong,
pub NameLen: c_ulong,
pub MaxNameLen: c_ulong,
// note that windows has this as 1, but it basically just means that
// the name is inline at the end of the struct. For us, we just bump
// the struct size up to MAX_SYM_NAME.
pub Name: [c_char; MAX_SYM_NAME],
}
#[repr(C)]
#[cfg(target_env = "msvc")]
#[cfg(feature = "backtrace")]
pub struct IMAGEHLP_LINE64 {
pub SizeOfStruct: u32,
pub Key: *const c_void,
pub LineNumber: u32,
pub Filename: *const c_char,
pub Address: u64,
}
#[repr(C)]
#[allow(dead_code)] // we only use some variants
pub enum ADDRESS_MODE {
@ -624,280 +562,8 @@ pub enum ADDRESS_MODE {
AddrModeFlat,
}
#[repr(C)]
#[cfg(feature = "backtrace")]
pub struct ADDRESS64 {
pub Offset: u64,
pub Segment: u16,
pub Mode: ADDRESS_MODE,
}
#[repr(C)]
#[cfg(feature = "backtrace")]
pub struct STACKFRAME_EX {
pub AddrPC: ADDRESS64,
pub AddrReturn: ADDRESS64,
pub AddrFrame: ADDRESS64,
pub AddrStack: ADDRESS64,
pub AddrBStore: ADDRESS64,
pub FuncTableEntry: *mut c_void,
pub Params: [u64; 4],
pub Far: BOOL,
pub Virtual: BOOL,
pub Reserved: [u64; 3],
pub KdHelp: KDHELP64,
pub StackFrameSize: DWORD,
pub InlineFrameContext: DWORD,
}
#[repr(C)]
#[cfg(feature = "backtrace")]
pub struct STACKFRAME64 {
pub AddrPC: ADDRESS64,
pub AddrReturn: ADDRESS64,
pub AddrFrame: ADDRESS64,
pub AddrStack: ADDRESS64,
pub AddrBStore: ADDRESS64,
pub FuncTableEntry: *mut c_void,
pub Params: [u64; 4],
pub Far: BOOL,
pub Virtual: BOOL,
pub Reserved: [u64; 3],
pub KdHelp: KDHELP64,
}
#[repr(C)]
#[cfg(feature = "backtrace")]
pub struct KDHELP64 {
pub Thread: u64,
pub ThCallbackStack: DWORD,
pub ThCallbackBStore: DWORD,
pub NextCallback: DWORD,
pub FramePointer: DWORD,
pub KiCallUserMode: u64,
pub KeUserCallbackDispatcher: u64,
pub SystemRangeStart: u64,
pub KiUserExceptionDispatcher: u64,
pub StackBase: u64,
pub StackLimit: u64,
pub Reserved: [u64; 5],
}
#[cfg(target_arch = "x86")]
#[repr(C)]
pub struct CONTEXT {
pub ContextFlags: DWORD,
pub Dr0: DWORD,
pub Dr1: DWORD,
pub Dr2: DWORD,
pub Dr3: DWORD,
pub Dr6: DWORD,
pub Dr7: DWORD,
pub FloatSave: FLOATING_SAVE_AREA,
pub SegGs: DWORD,
pub SegFs: DWORD,
pub SegEs: DWORD,
pub SegDs: DWORD,
pub Edi: DWORD,
pub Esi: DWORD,
pub Ebx: DWORD,
pub Edx: DWORD,
pub Ecx: DWORD,
pub Eax: DWORD,
pub Ebp: DWORD,
pub Eip: DWORD,
pub SegCs: DWORD,
pub EFlags: DWORD,
pub Esp: DWORD,
pub SegSs: DWORD,
pub ExtendedRegisters: [u8; 512],
}
#[cfg(target_arch = "x86")]
#[repr(C)]
pub struct FLOATING_SAVE_AREA {
pub ControlWord: DWORD,
pub StatusWord: DWORD,
pub TagWord: DWORD,
pub ErrorOffset: DWORD,
pub ErrorSelector: DWORD,
pub DataOffset: DWORD,
pub DataSelector: DWORD,
pub RegisterArea: [u8; 80],
pub Cr0NpxState: DWORD,
}
#[cfg(target_arch = "x86_64")]
#[repr(C, align(16))]
pub struct CONTEXT {
pub P1Home: DWORDLONG,
pub P2Home: DWORDLONG,
pub P3Home: DWORDLONG,
pub P4Home: DWORDLONG,
pub P5Home: DWORDLONG,
pub P6Home: DWORDLONG,
pub ContextFlags: DWORD,
pub MxCsr: DWORD,
pub SegCs: WORD,
pub SegDs: WORD,
pub SegEs: WORD,
pub SegFs: WORD,
pub SegGs: WORD,
pub SegSs: WORD,
pub EFlags: DWORD,
pub Dr0: DWORDLONG,
pub Dr1: DWORDLONG,
pub Dr2: DWORDLONG,
pub Dr3: DWORDLONG,
pub Dr6: DWORDLONG,
pub Dr7: DWORDLONG,
pub Rax: DWORDLONG,
pub Rcx: DWORDLONG,
pub Rdx: DWORDLONG,
pub Rbx: DWORDLONG,
pub Rsp: DWORDLONG,
pub Rbp: DWORDLONG,
pub Rsi: DWORDLONG,
pub Rdi: DWORDLONG,
pub R8: DWORDLONG,
pub R9: DWORDLONG,
pub R10: DWORDLONG,
pub R11: DWORDLONG,
pub R12: DWORDLONG,
pub R13: DWORDLONG,
pub R14: DWORDLONG,
pub R15: DWORDLONG,
pub Rip: DWORDLONG,
pub FltSave: FLOATING_SAVE_AREA,
pub VectorRegister: [M128A; 26],
pub VectorControl: DWORDLONG,
pub DebugControl: DWORDLONG,
pub LastBranchToRip: DWORDLONG,
pub LastBranchFromRip: DWORDLONG,
pub LastExceptionToRip: DWORDLONG,
pub LastExceptionFromRip: DWORDLONG,
}
#[cfg(target_arch = "x86_64")]
#[repr(C, align(16))]
pub struct M128A {
pub Low: c_ulonglong,
pub High: c_longlong
}
#[cfg(target_arch = "x86_64")]
#[repr(C, align(16))]
pub struct FLOATING_SAVE_AREA {
_Dummy: [u8; 512] // FIXME: Fill this out
}
#[cfg(target_arch = "arm")]
#[repr(C)]
pub struct CONTEXT {
pub ContextFlags: ULONG,
pub R0: ULONG,
pub R1: ULONG,
pub R2: ULONG,
pub R3: ULONG,
pub R4: ULONG,
pub R5: ULONG,
pub R6: ULONG,
pub R7: ULONG,
pub R8: ULONG,
pub R9: ULONG,
pub R10: ULONG,
pub R11: ULONG,
pub R12: ULONG,
pub Sp: ULONG,
pub Lr: ULONG,
pub Pc: ULONG,
pub Cpsr: ULONG,
pub Fpscr: ULONG,
pub Padding: ULONG,
pub D: [u64; 32],
pub Bvr: [ULONG; ARM_MAX_BREAKPOINTS],
pub Bcr: [ULONG; ARM_MAX_BREAKPOINTS],
pub Wvr: [ULONG; ARM_MAX_WATCHPOINTS],
pub Wcr: [ULONG; ARM_MAX_WATCHPOINTS],
pub Padding2: [ULONG; 2]
}
// FIXME(#43348): This structure is used for backtrace only, and a fake
// definition is provided here only to allow rustdoc to pass type-check. This
// will not appear in the final documentation. This should be also defined for
// other architectures supported by Windows such as ARM, and for historical
// interest, maybe MIPS and PowerPC as well.
#[cfg(all(rustdoc, not(any(target_arch = "x86_64", target_arch = "x86",
target_arch = "aarch64", target_arch = "arm"))))]
pub enum CONTEXT {}
#[cfg(target_arch = "aarch64")]
pub const ARM64_MAX_BREAKPOINTS: usize = 8;
#[cfg(target_arch = "aarch64")]
pub const ARM64_MAX_WATCHPOINTS: usize = 2;
#[cfg(target_arch = "aarch64")]
#[repr(C)]
pub struct ARM64_NT_NEON128 {
pub D: [f64; 2],
}
#[cfg(target_arch = "aarch64")]
#[repr(C, align(16))]
pub struct CONTEXT {
pub ContextFlags: DWORD,
pub Cpsr: DWORD,
pub X0: u64,
pub X1: u64,
pub X2: u64,
pub X3: u64,
pub X4: u64,
pub X5: u64,
pub X6: u64,
pub X7: u64,
pub X8: u64,
pub X9: u64,
pub X10: u64,
pub X11: u64,
pub X12: u64,
pub X13: u64,
pub X14: u64,
pub X15: u64,
pub X16: u64,
pub X17: u64,
pub X18: u64,
pub X19: u64,
pub X20: u64,
pub X21: u64,
pub X22: u64,
pub X23: u64,
pub X24: u64,
pub X25: u64,
pub X26: u64,
pub X27: u64,
pub X28: u64,
pub Fp: u64,
pub Lr: u64,
pub Sp: u64,
pub Pc: u64,
pub V: [ARM64_NT_NEON128; 32],
pub Fpcr: DWORD,
pub Fpsr: DWORD,
pub Bcr: [DWORD; ARM64_MAX_BREAKPOINTS],
pub Bvr: [DWORD; ARM64_MAX_BREAKPOINTS],
pub Wcr: [DWORD; ARM64_MAX_WATCHPOINTS],
pub Wvr: [DWORD; ARM64_MAX_WATCHPOINTS],
}
#[repr(C)]
pub struct SOCKADDR_STORAGE_LH {
pub ss_family: ADDRESS_FAMILY,
@ -1220,8 +886,6 @@ extern "system" {
pub fn FindNextFileW(findFile: HANDLE, findFileData: LPWIN32_FIND_DATAW)
-> BOOL;
pub fn FindClose(findFile: HANDLE) -> BOOL;
#[cfg(feature = "backtrace")]
pub fn RtlCaptureContext(ctx: *mut CONTEXT);
pub fn getsockopt(s: SOCKET,
level: c_int,
optname: c_int,
@ -1252,10 +916,6 @@ extern "system" {
res: *mut *mut ADDRINFOA) -> c_int;
pub fn freeaddrinfo(res: *mut ADDRINFOA);
#[cfg(feature = "backtrace")]
pub fn LoadLibraryW(name: LPCWSTR) -> HMODULE;
#[cfg(feature = "backtrace")]
pub fn FreeLibrary(handle: HMODULE) -> BOOL;
pub fn GetProcAddress(handle: HMODULE,
name: LPCSTR) -> *mut c_void;
pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
@ -1361,34 +1021,3 @@ compat_fn! {
panic!("rwlocks not available")
}
}
#[cfg(all(target_env = "gnu", feature = "backtrace"))]
mod gnu {
use super::*;
pub const PROCESS_QUERY_INFORMATION: DWORD = 0x0400;
pub const CP_ACP: UINT = 0;
pub const WC_NO_BEST_FIT_CHARS: DWORD = 0x00000400;
extern "system" {
pub fn OpenProcess(dwDesiredAccess: DWORD,
bInheritHandle: BOOL,
dwProcessId: DWORD) -> HANDLE;
}
compat_fn! {
kernel32:
pub fn QueryFullProcessImageNameW(_hProcess: HANDLE,
_dwFlags: DWORD,
_lpExeName: LPWSTR,
_lpdwSize: LPDWORD) -> BOOL {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
}
}
}
#[cfg(all(target_env = "gnu", feature = "backtrace"))]
pub use self::gnu::*;

View File

@ -1,44 +0,0 @@
use crate::os::windows::prelude::*;
use crate::ffi::{CString, OsStr};
use crate::io;
use crate::sys::c;
pub struct DynamicLibrary {
handle: c::HMODULE,
}
impl DynamicLibrary {
pub fn open(filename: &str) -> io::Result<DynamicLibrary> {
let filename = OsStr::new(filename)
.encode_wide()
.chain(Some(0))
.collect::<Vec<_>>();
let result = unsafe {
c::LoadLibraryW(filename.as_ptr())
};
if result.is_null() {
Err(io::Error::last_os_error())
} else {
Ok(DynamicLibrary { handle: result })
}
}
pub fn symbol(&self, symbol: &str) -> io::Result<usize> {
let symbol = CString::new(symbol)?;
unsafe {
match c::GetProcAddress(self.handle, symbol.as_ptr()) as usize {
0 => Err(io::Error::last_os_error()),
n => Ok(n),
}
}
}
}
impl Drop for DynamicLibrary {
fn drop(&mut self) {
unsafe {
c::FreeLibrary(self.handle);
}
}
}

View File

@ -14,13 +14,9 @@ pub use self::rand::hashmap_random_keys;
pub mod alloc;
pub mod args;
#[cfg(feature = "backtrace")]
pub mod backtrace;
pub mod c;
pub mod cmath;
pub mod condvar;
#[cfg(feature = "backtrace")]
pub mod dynamic_lib;
pub mod env;
pub mod ext;
pub mod fast_thread_local;

View File

@ -2,40 +2,17 @@
/// supported platforms.
use crate::env;
use crate::io::prelude::*;
use crate::io;
use crate::io::prelude::*;
use crate::mem;
use crate::path::{self, Path};
use crate::ptr;
use crate::str;
use crate::sync::atomic::{self, Ordering};
use crate::sys::mutex::Mutex;
use rustc_demangle::demangle;
use backtrace::{BytesOrWideString, Frame, Symbol};
pub use crate::sys::backtrace::{
unwind_backtrace,
resolve_symname,
foreach_symbol_fileline,
BacktraceContext
};
#[cfg(target_pointer_width = "64")]
pub const HEX_WIDTH: usize = 18;
#[cfg(target_pointer_width = "32")]
pub const HEX_WIDTH: usize = 10;
/// 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.
pub exact_position: *const u8,
/// Address of the enclosing function.
pub symbol_addr: *const u8,
/// Which inlined function is this frame referring to
pub inline_context: u32,
}
pub const HEX_WIDTH: usize = 2 + 2 * mem::size_of::<usize>();
/// Max number of frames to print.
const MAX_NB_FRAMES: usize = 100;
@ -49,7 +26,7 @@ pub fn print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
// test mode immediately return here to optimize away any references to the
// libbacktrace symbols
if cfg!(test) {
return Ok(())
return Ok(());
}
// Use a lock to prevent mixed output in multithreading context.
@ -63,75 +40,39 @@ pub fn print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
}
fn _print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
let mut frames = [Frame {
exact_position: ptr::null(),
symbol_addr: ptr::null(),
inline_context: 0,
}; MAX_NB_FRAMES];
let (nb_frames, context) = unwind_backtrace(&mut frames)?;
let (skipped_before, skipped_after) =
filter_frames(&frames[..nb_frames], format, &context);
if skipped_before + skipped_after > 0 {
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>")?;
}
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);
}
!printer.done
});
}
if printer.skipped {
writeln!(
w,
"note: Some details are omitted, \
run with `RUST_BACKTRACE=full` for a verbose backtrace."
)?;
}
Ok(())
}
/// 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)
{
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
where F: FnOnce() -> T, F: Send, T: Send
where
F: FnOnce() -> T,
F: Send,
T: Send,
{
f()
}
@ -156,7 +97,7 @@ pub fn log_enabled() -> Option<PrintFormat> {
_ => return Some(PrintFormat::Full),
}
let val = env::var_os("RUST_BACKTRACE").and_then(|x|
let val = env::var_os("RUST_BACKTRACE").and_then(|x| {
if &x == "0" {
None
} else if &x == "full" {
@ -164,80 +105,141 @@ pub fn log_enabled() -> Option<PrintFormat> {
} else {
Some(PrintFormat::Short)
}
});
ENABLED.store(
match val {
Some(v) => v as isize,
None => 1,
},
Ordering::SeqCst,
);
ENABLED.store(match val {
Some(v) => v as isize,
None => 1,
}, Ordering::SeqCst);
val
}
/// 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(w: &mut dyn Write, idx: usize, frame: Frame,
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)?,
}
match s {
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)?,
}
}
None => w.write_all(b"<unknown>")?,
}
w.write_all(b"\n")
struct Printer<'a, 'b> {
format: PrintFormat,
done: bool,
skipped: bool,
idx: usize,
out: &'a mut (dyn Write + 'b),
}
/// Prints the filename and line number of the backtrace frame.
///
/// See also `output`.
#[allow(dead_code)]
fn output_fileline(w: &mut dyn Write,
file: &[u8],
line: u32,
format: PrintFormat) -> io::Result<()> {
// prior line: " ##: {:2$} - func"
w.write_all(b"")?;
match format {
PrintFormat::Full => write!(w,
" {:1$}",
"",
HEX_WIDTH)?,
PrintFormat::Short => write!(w, " ")?,
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 }
}
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() {
write!(w, " at .{}{}:{}", path::MAIN_SEPARATOR, s, line)?;
already_printed = true;
/// 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>")?,
}
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)]
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!(w, " at {}:{}", file, line)?;
}
if !already_printed {
write!(self.out, " at {}:{}", file.display(), line)?;
}
w.write_all(b"\n")
self.out.write_all(b"\n")
}
}

View File

@ -1,175 +0,0 @@
use backtrace_sys::backtrace_state;
use crate::ffi::CStr;
use crate::io;
use crate::mem;
use crate::ptr;
use crate::sys::backtrace::BacktraceContext;
use crate::sys_common::backtrace::Frame;
pub fn foreach_symbol_fileline<F>(frame: Frame,
mut f: F,
_: &BacktraceContext) -> io::Result<bool>
where F: FnMut(&[u8], u32) -> io::Result<()>
{
// pcinfo may return an arbitrary number of file:line pairs,
// in the order of stack trace (i.e., inlined calls first).
// in order to avoid allocation, we stack-allocate a fixed size of entries.
const FILELINE_SIZE: usize = 32;
let mut fileline_buf = [(ptr::null(), !0); FILELINE_SIZE];
let ret;
let fileline_count = {
let state = unsafe { init_state() };
if state.is_null() {
return Err(io::Error::new(
io::ErrorKind::Other,
"failed to allocate libbacktrace state")
)
}
let mut fileline_win: &mut [FileLine] = &mut fileline_buf;
let fileline_addr = &mut fileline_win as *mut &mut [FileLine];
ret = unsafe {
backtrace_sys::backtrace_pcinfo(
state,
frame.exact_position as libc::uintptr_t,
pcinfo_cb,
error_cb,
fileline_addr as *mut libc::c_void,
)
};
FILELINE_SIZE - fileline_win.len()
};
if ret == 0 {
for &(file, line) in &fileline_buf[..fileline_count] {
if file.is_null() { continue; } // just to be sure
let file = unsafe { CStr::from_ptr(file).to_bytes() };
f(file, line)?;
}
Ok(fileline_count == FILELINE_SIZE)
} else {
Ok(false)
}
}
/// Converts a pointer to symbol to its string value.
pub fn resolve_symname<F>(frame: Frame,
callback: F,
_: &BacktraceContext) -> io::Result<()>
where F: FnOnce(Option<&str>) -> io::Result<()>
{
let symname = {
let state = unsafe { init_state() };
if state.is_null() {
return Err(io::Error::new(
io::ErrorKind::Other,
"failed to allocate libbacktrace state")
)
}
let mut data: *const libc::c_char = ptr::null();
let data_addr = &mut data as *mut *const libc::c_char;
let ret = unsafe {
backtrace_sys::backtrace_syminfo(
state,
frame.symbol_addr as libc::uintptr_t,
syminfo_cb,
error_cb,
data_addr as *mut libc::c_void,
)
};
if ret == 0 || data.is_null() {
None
} else {
unsafe {
CStr::from_ptr(data).to_str().ok()
}
}
};
callback(symname)
}
////////////////////////////////////////////////////////////////////////
// helper callbacks
////////////////////////////////////////////////////////////////////////
type FileLine = (*const libc::c_char, u32);
extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char,
_errnum: libc::c_int) {
// do nothing for now
}
extern fn syminfo_cb(data: *mut libc::c_void,
_pc: libc::uintptr_t,
symname: *const libc::c_char,
_symval: libc::uintptr_t,
_symsize: libc::uintptr_t) {
let slot = data as *mut *const libc::c_char;
unsafe { *slot = symname; }
}
extern fn pcinfo_cb(data: *mut libc::c_void,
_pc: libc::uintptr_t,
filename: *const libc::c_char,
lineno: libc::c_int,
_function: *const libc::c_char) -> libc::c_int {
if !filename.is_null() {
let slot = data as *mut &mut [FileLine];
let buffer = unsafe {ptr::read(slot)};
// if the buffer is not full, add file:line to the buffer
// and adjust the buffer for next possible calls to pcinfo_cb.
if !buffer.is_empty() {
buffer[0] = (filename, lineno as u32);
unsafe { ptr::write(slot, &mut buffer[1..]); }
}
}
0
}
// The libbacktrace API supports creating a state, but it does not
// support destroying a state. I personally take this to mean that a
// state is meant to be created and then live forever.
//
// I would love to register an at_exit() handler which cleans up this
// state, but libbacktrace provides no way to do so.
//
// With these constraints, this function has a statically cached state
// that is calculated the first time this is requested. Remember that
// backtracing all happens serially (one global lock).
//
// Things don't work so well on not-Linux since libbacktrace can't track
// down that executable this is. We at one point used env::current_exe but
// it turns out that there are some serious security issues with that
// approach.
//
// Specifically, on certain platforms like BSDs, a malicious actor can cause
// an arbitrary file to be placed at the path returned by current_exe.
// libbacktrace does not behave defensively in the presence of ill-formed
// DWARF information, and has been demonstrated to segfault in at least one
// case. There is no evidence at the moment to suggest that a more carefully
// constructed file can't cause arbitrary code execution. As a result of all
// of this, we don't hint libbacktrace with the path to the current process.
unsafe fn init_state() -> *mut backtrace_state {
static mut STATE: *mut backtrace_state = ptr::null_mut();
if !STATE.is_null() { return STATE }
let filename = match crate::sys::backtrace::gnu::get_executable_filename() {
Ok((filename, file)) => {
// filename is purposely leaked here since libbacktrace requires
// it to stay allocated permanently, file is also leaked so that
// the file stays locked
let filename_ptr = filename.as_ptr();
mem::forget(filename);
mem::forget(file);
filename_ptr
},
Err(_) => ptr::null(),
};
STATE = backtrace_sys::backtrace_create_state(
filename,
0,
error_cb,
ptr::null_mut(),
);
STATE
}

View File

@ -1,5 +0,0 @@
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
pub mod libbacktrace;

View File

@ -77,12 +77,6 @@ cfg_if! {
}
}
#[cfg(feature = "backtrace")]
#[cfg(any(all(unix, not(target_os = "emscripten")),
all(windows, target_env = "gnu"),
target_os = "redox"))]
pub mod gnu;
// common error constructors
/// A trait for viewing representations from std types

View File

@ -38,6 +38,7 @@ features = [
"objbase",
"profileapi",
"processenv",
"processthreadsapi",
"psapi",
"schannel",
"securitybaseapi",

View File

@ -64,6 +64,7 @@ const WHITELIST: &[Crate<'_>] = &[
Crate("aho-corasick"),
Crate("arrayvec"),
Crate("atty"),
Crate("autocfg"),
Crate("backtrace"),
Crate("backtrace-sys"),
Crate("bitflags"),

View File

@ -61,6 +61,7 @@ const EXCEPTION_PATHS: &[&str] = &[
"src/libstd/net/test.rs",
"src/libstd/sys_common/mod.rs",
"src/libstd/sys_common/net.rs",
"src/libstd/sys_common/backtrace.rs",
"src/libterm", // Not sure how to make this crate portable, but test crate needs it.
"src/libtest", // Probably should defer to unstable `std::sys` APIs.
"src/libstd/sync/mpsc", // some tests are only run on non-emscripten