mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 08:44:35 +00:00
Auto merge of #64025 - Wind-River:master_003, r=alexcrichton
remove directory libstd/sys/vxworks/backtrace which is not used any more r? @alexcrichton cc @n-salim
This commit is contained in:
commit
fba38ac27e
@ -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;
|
@ -1,35 +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 {
|
||||
#[ link_name = "_rtld_dladdr" ]
|
||||
fn dladdr(addr: *const libc::c_void, info: *mut Dl_info) -> libc::c_int;
|
||||
}
|
@ -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)
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
|
||||
}
|
@ -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;
|
Loading…
Reference in New Issue
Block a user