summaryrefslogtreecommitdiffstats
path: root/library/backtrace/src/backtrace
diff options
context:
space:
mode:
Diffstat (limited to 'library/backtrace/src/backtrace')
-rw-r--r--library/backtrace/src/backtrace/dbghelp.rs257
-rw-r--r--library/backtrace/src/backtrace/libunwind.rs267
-rw-r--r--library/backtrace/src/backtrace/miri.rs107
-rw-r--r--library/backtrace/src/backtrace/mod.rs162
-rw-r--r--library/backtrace/src/backtrace/noop.rs28
5 files changed, 821 insertions, 0 deletions
diff --git a/library/backtrace/src/backtrace/dbghelp.rs b/library/backtrace/src/backtrace/dbghelp.rs
new file mode 100644
index 000000000..ba0f05f3b
--- /dev/null
+++ b/library/backtrace/src/backtrace/dbghelp.rs
@@ -0,0 +1,257 @@
+//! Backtrace strategy for MSVC platforms.
+//!
+//! This module contains the ability to generate a backtrace on MSVC using one
+//! of two possible methods. The `StackWalkEx` function is primarily used if
+//! possible, but not all systems have that. Failing that the `StackWalk64`
+//! function is used instead. Note that `StackWalkEx` is favored because it
+//! handles debuginfo internally and returns inline frame information.
+//!
+//! Note that all dbghelp support is loaded dynamically, see `src/dbghelp.rs`
+//! for more information about that.
+
+#![allow(bad_style)]
+
+use super::super::{dbghelp, windows::*};
+use core::ffi::c_void;
+use core::mem;
+
+#[derive(Clone, Copy)]
+pub enum StackFrame {
+ New(STACKFRAME_EX),
+ Old(STACKFRAME64),
+}
+
+#[derive(Clone, Copy)]
+pub struct Frame {
+ pub(crate) stack_frame: StackFrame,
+ base_address: *mut c_void,
+}
+
+// we're just sending around raw pointers and reading them, never interpreting
+// them so this should be safe to both send and share across threads.
+unsafe impl Send for Frame {}
+unsafe impl Sync for Frame {}
+
+impl Frame {
+ pub fn ip(&self) -> *mut c_void {
+ self.addr_pc().Offset as *mut _
+ }
+
+ pub fn sp(&self) -> *mut c_void {
+ self.addr_stack().Offset as *mut _
+ }
+
+ pub fn symbol_address(&self) -> *mut c_void {
+ self.ip()
+ }
+
+ pub fn module_base_address(&self) -> Option<*mut c_void> {
+ Some(self.base_address)
+ }
+
+ fn addr_pc(&self) -> &ADDRESS64 {
+ match self.stack_frame {
+ StackFrame::New(ref new) => &new.AddrPC,
+ StackFrame::Old(ref old) => &old.AddrPC,
+ }
+ }
+
+ fn addr_pc_mut(&mut self) -> &mut ADDRESS64 {
+ match self.stack_frame {
+ StackFrame::New(ref mut new) => &mut new.AddrPC,
+ StackFrame::Old(ref mut old) => &mut old.AddrPC,
+ }
+ }
+
+ fn addr_frame_mut(&mut self) -> &mut ADDRESS64 {
+ match self.stack_frame {
+ StackFrame::New(ref mut new) => &mut new.AddrFrame,
+ StackFrame::Old(ref mut old) => &mut old.AddrFrame,
+ }
+ }
+
+ fn addr_stack(&self) -> &ADDRESS64 {
+ match self.stack_frame {
+ StackFrame::New(ref new) => &new.AddrStack,
+ StackFrame::Old(ref old) => &old.AddrStack,
+ }
+ }
+
+ fn addr_stack_mut(&mut self) -> &mut ADDRESS64 {
+ match self.stack_frame {
+ StackFrame::New(ref mut new) => &mut new.AddrStack,
+ StackFrame::Old(ref mut old) => &mut old.AddrStack,
+ }
+ }
+}
+
+#[repr(C, align(16))] // required by `CONTEXT`, is a FIXME in winapi right now
+struct MyContext(CONTEXT);
+
+#[inline(always)]
+pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) {
+ // Allocate necessary structures for doing the stack walk
+ let process = GetCurrentProcess();
+ let thread = GetCurrentThread();
+
+ let mut context = mem::zeroed::<MyContext>();
+ RtlCaptureContext(&mut context.0);
+
+ // Ensure this process's symbols are initialized
+ let dbghelp = match dbghelp::init() {
+ Ok(dbghelp) => dbghelp,
+ Err(()) => return, // oh well...
+ };
+
+ // On x86_64 and ARM64 we opt to not use the default `Sym*` functions from
+ // dbghelp for getting the function table and module base. Instead we use
+ // the `RtlLookupFunctionEntry` function in kernel32 which will account for
+ // JIT compiler frames as well. These should be equivalent, but using
+ // `Rtl*` allows us to backtrace through JIT frames.
+ //
+ // Note that `RtlLookupFunctionEntry` only works for in-process backtraces,
+ // but that's all we support anyway, so it all lines up well.
+ cfg_if::cfg_if! {
+ if #[cfg(target_pointer_width = "64")] {
+ use core::ptr;
+
+ unsafe extern "system" fn function_table_access(_process: HANDLE, addr: DWORD64) -> PVOID {
+ let mut base = 0;
+ RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut()).cast()
+ }
+
+ unsafe extern "system" fn get_module_base(_process: HANDLE, addr: DWORD64) -> DWORD64 {
+ let mut base = 0;
+ RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut());
+ base
+ }
+ } else {
+ let function_table_access = dbghelp.SymFunctionTableAccess64();
+ let get_module_base = dbghelp.SymGetModuleBase64();
+ }
+ }
+
+ let process_handle = GetCurrentProcess();
+
+ // Attempt to use `StackWalkEx` if we can, but fall back to `StackWalk64`
+ // since it's in theory supported on more systems.
+ match (*dbghelp.dbghelp()).StackWalkEx() {
+ Some(StackWalkEx) => {
+ let mut inner: STACKFRAME_EX = mem::zeroed();
+ inner.StackFrameSize = mem::size_of::<STACKFRAME_EX>() as DWORD;
+ let mut frame = super::Frame {
+ inner: Frame {
+ stack_frame: StackFrame::New(inner),
+ base_address: 0 as _,
+ },
+ };
+ let image = init_frame(&mut frame.inner, &context.0);
+ let frame_ptr = match &mut frame.inner.stack_frame {
+ StackFrame::New(ptr) => ptr as *mut STACKFRAME_EX,
+ _ => unreachable!(),
+ };
+
+ while StackWalkEx(
+ image as DWORD,
+ process,
+ thread,
+ frame_ptr,
+ &mut context.0 as *mut CONTEXT as *mut _,
+ None,
+ Some(function_table_access),
+ Some(get_module_base),
+ None,
+ 0,
+ ) == TRUE
+ {
+ frame.inner.base_address = get_module_base(process_handle, frame.ip() as _) as _;
+
+ if !cb(&frame) {
+ break;
+ }
+ }
+ }
+ None => {
+ let mut frame = super::Frame {
+ inner: Frame {
+ stack_frame: StackFrame::Old(mem::zeroed()),
+ base_address: 0 as _,
+ },
+ };
+ let image = init_frame(&mut frame.inner, &context.0);
+ let frame_ptr = match &mut frame.inner.stack_frame {
+ StackFrame::Old(ptr) => ptr as *mut STACKFRAME64,
+ _ => unreachable!(),
+ };
+
+ while dbghelp.StackWalk64()(
+ image as DWORD,
+ process,
+ thread,
+ frame_ptr,
+ &mut context.0 as *mut CONTEXT as *mut _,
+ None,
+ Some(function_table_access),
+ Some(get_module_base),
+ None,
+ ) == TRUE
+ {
+ frame.inner.base_address = get_module_base(process_handle, frame.ip() as _) as _;
+
+ if !cb(&frame) {
+ break;
+ }
+ }
+ }
+ }
+}
+
+#[cfg(target_arch = "x86_64")]
+fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
+ frame.addr_pc_mut().Offset = ctx.Rip as u64;
+ frame.addr_pc_mut().Mode = AddrModeFlat;
+ frame.addr_stack_mut().Offset = ctx.Rsp as u64;
+ frame.addr_stack_mut().Mode = AddrModeFlat;
+ frame.addr_frame_mut().Offset = ctx.Rbp as u64;
+ frame.addr_frame_mut().Mode = AddrModeFlat;
+
+ IMAGE_FILE_MACHINE_AMD64
+}
+
+#[cfg(target_arch = "x86")]
+fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
+ frame.addr_pc_mut().Offset = ctx.Eip as u64;
+ frame.addr_pc_mut().Mode = AddrModeFlat;
+ frame.addr_stack_mut().Offset = ctx.Esp as u64;
+ frame.addr_stack_mut().Mode = AddrModeFlat;
+ frame.addr_frame_mut().Offset = ctx.Ebp as u64;
+ frame.addr_frame_mut().Mode = AddrModeFlat;
+
+ IMAGE_FILE_MACHINE_I386
+}
+
+#[cfg(target_arch = "aarch64")]
+fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
+ frame.addr_pc_mut().Offset = ctx.Pc as u64;
+ frame.addr_pc_mut().Mode = AddrModeFlat;
+ frame.addr_stack_mut().Offset = ctx.Sp as u64;
+ frame.addr_stack_mut().Mode = AddrModeFlat;
+ unsafe {
+ frame.addr_frame_mut().Offset = ctx.u.s().Fp as u64;
+ }
+ frame.addr_frame_mut().Mode = AddrModeFlat;
+ IMAGE_FILE_MACHINE_ARM64
+}
+
+#[cfg(target_arch = "arm")]
+fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
+ frame.addr_pc_mut().Offset = ctx.Pc as u64;
+ frame.addr_pc_mut().Mode = AddrModeFlat;
+ frame.addr_stack_mut().Offset = ctx.Sp as u64;
+ frame.addr_stack_mut().Mode = AddrModeFlat;
+ unsafe {
+ frame.addr_frame_mut().Offset = ctx.R11 as u64;
+ }
+ frame.addr_frame_mut().Mode = AddrModeFlat;
+ IMAGE_FILE_MACHINE_ARMNT
+}
diff --git a/library/backtrace/src/backtrace/libunwind.rs b/library/backtrace/src/backtrace/libunwind.rs
new file mode 100644
index 000000000..ef77edda5
--- /dev/null
+++ b/library/backtrace/src/backtrace/libunwind.rs
@@ -0,0 +1,267 @@
+//! Backtrace support using libunwind/gcc_s/etc APIs.
+//!
+//! This module contains the ability to unwind the stack using libunwind-style
+//! APIs. Note that there's a whole bunch of implementations of the
+//! libunwind-like API, and this is just trying to be compatible with most of
+//! them all at once instead of being picky.
+//!
+//! The libunwind API is powered by `_Unwind_Backtrace` and is in practice very
+//! reliable at generating a backtrace. It's not entirely clear how it does it
+//! (frame pointers? eh_frame info? both?) but it seems to work!
+//!
+//! Most of the complexity of this module is handling the various platform
+//! differences across libunwind implementations. Otherwise this is a pretty
+//! straightforward Rust binding to the libunwind APIs.
+//!
+//! This is the default unwinding API for all non-Windows platforms currently.
+
+use super::super::Bomb;
+use core::ffi::c_void;
+
+pub enum Frame {
+ Raw(*mut uw::_Unwind_Context),
+ Cloned {
+ ip: *mut c_void,
+ sp: *mut c_void,
+ symbol_address: *mut c_void,
+ },
+}
+
+// With a raw libunwind pointer it should only ever be access in a readonly
+// threadsafe fashion, so it's `Sync`. When sending to other threads via `Clone`
+// we always switch to a version which doesn't retain interior pointers, so we
+// should be `Send` as well.
+unsafe impl Send for Frame {}
+unsafe impl Sync for Frame {}
+
+impl Frame {
+ pub fn ip(&self) -> *mut c_void {
+ let ctx = match *self {
+ Frame::Raw(ctx) => ctx,
+ Frame::Cloned { ip, .. } => return ip,
+ };
+ unsafe { uw::_Unwind_GetIP(ctx) as *mut c_void }
+ }
+
+ pub fn sp(&self) -> *mut c_void {
+ match *self {
+ Frame::Raw(ctx) => unsafe { uw::get_sp(ctx) as *mut c_void },
+ Frame::Cloned { sp, .. } => sp,
+ }
+ }
+
+ pub fn symbol_address(&self) -> *mut c_void {
+ if let Frame::Cloned { symbol_address, .. } = *self {
+ return symbol_address;
+ }
+
+ // The macOS linker emits a "compact" unwind table that only includes an
+ // entry for a function if that function either has an LSDA or its
+ // encoding differs from that of the previous entry. Consequently, on
+ // macOS, `_Unwind_FindEnclosingFunction` is unreliable (it can return a
+ // pointer to some totally unrelated function). Instead, we just always
+ // return the ip.
+ //
+ // https://github.com/rust-lang/rust/issues/74771#issuecomment-664056788
+ //
+ // Note the `skip_inner_frames.rs` test is skipped on macOS due to this
+ // clause, and if this is fixed that test in theory can be run on macOS!
+ if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
+ self.ip()
+ } else {
+ unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) }
+ }
+ }
+
+ pub fn module_base_address(&self) -> Option<*mut c_void> {
+ None
+ }
+}
+
+impl Clone for Frame {
+ fn clone(&self) -> Frame {
+ Frame::Cloned {
+ ip: self.ip(),
+ sp: self.sp(),
+ symbol_address: self.symbol_address(),
+ }
+ }
+}
+
+#[inline(always)]
+pub unsafe fn trace(mut cb: &mut dyn FnMut(&super::Frame) -> bool) {
+ uw::_Unwind_Backtrace(trace_fn, &mut cb as *mut _ as *mut _);
+
+ extern "C" fn trace_fn(
+ ctx: *mut uw::_Unwind_Context,
+ arg: *mut c_void,
+ ) -> uw::_Unwind_Reason_Code {
+ let cb = unsafe { &mut *(arg as *mut &mut dyn FnMut(&super::Frame) -> bool) };
+ let cx = super::Frame {
+ inner: Frame::Raw(ctx),
+ };
+
+ let mut bomb = Bomb { enabled: true };
+ let keep_going = cb(&cx);
+ bomb.enabled = false;
+
+ if keep_going {
+ uw::_URC_NO_REASON
+ } else {
+ uw::_URC_FAILURE
+ }
+ }
+}
+
+/// Unwind library interface used for backtraces
+///
+/// Note that dead code is allowed as here are just bindings
+/// iOS doesn't use all of them it but adding more
+/// platform-specific configs pollutes the code too much
+#[allow(non_camel_case_types)]
+#[allow(non_snake_case)]
+#[allow(dead_code)]
+mod uw {
+ pub use self::_Unwind_Reason_Code::*;
+
+ use core::ffi::c_void;
+
+ #[repr(C)]
+ pub enum _Unwind_Reason_Code {
+ _URC_NO_REASON = 0,
+ _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
+ _URC_FATAL_PHASE2_ERROR = 2,
+ _URC_FATAL_PHASE1_ERROR = 3,
+ _URC_NORMAL_STOP = 4,
+ _URC_END_OF_STACK = 5,
+ _URC_HANDLER_FOUND = 6,
+ _URC_INSTALL_CONTEXT = 7,
+ _URC_CONTINUE_UNWIND = 8,
+ _URC_FAILURE = 9, // used only by ARM EABI
+ }
+
+ pub enum _Unwind_Context {}
+
+ pub type _Unwind_Trace_Fn =
+ extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code;
+
+ extern "C" {
+ pub fn _Unwind_Backtrace(
+ trace: _Unwind_Trace_Fn,
+ trace_argument: *mut c_void,
+ ) -> _Unwind_Reason_Code;
+ }
+
+ cfg_if::cfg_if! {
+ // available since GCC 4.2.0, should be fine for our purpose
+ if #[cfg(all(
+ not(all(target_os = "android", target_arch = "arm")),
+ not(all(target_os = "freebsd", target_arch = "arm")),
+ not(all(target_os = "linux", target_arch = "arm")),
+ not(all(target_os = "horizon", target_arch = "arm"))
+ ))] {
+ extern "C" {
+ pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
+ pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
+
+ #[cfg(not(all(target_os = "linux", target_arch = "s390x")))]
+ // This function is a misnomer: rather than getting this frame's
+ // Canonical Frame Address (aka the caller frame's SP) it
+ // returns this frame's SP.
+ //
+ // https://github.com/libunwind/libunwind/blob/d32956507cf29d9b1a98a8bce53c78623908f4fe/src/unwind/GetCFA.c#L28-L35
+ #[link_name = "_Unwind_GetCFA"]
+ pub fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
+
+ }
+
+ // s390x uses a biased CFA value, therefore we need to use
+ // _Unwind_GetGR to get the stack pointer register (%r15)
+ // instead of relying on _Unwind_GetCFA.
+ #[cfg(all(target_os = "linux", target_arch = "s390x"))]
+ pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
+ extern "C" {
+ pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, index: libc::c_int) -> libc::uintptr_t;
+ }
+ _Unwind_GetGR(ctx, 15)
+ }
+ } else {
+ // On android and arm, the function `_Unwind_GetIP` and a bunch of
+ // others are macros, so we define functions containing the
+ // expansion of the macros.
+ //
+ // TODO: link to the header file that defines these macros, if you
+ // can find it. (I, fitzgen, cannot find the header file that some
+ // of these macro expansions were originally borrowed from.)
+ #[repr(C)]
+ enum _Unwind_VRS_Result {
+ _UVRSR_OK = 0,
+ _UVRSR_NOT_IMPLEMENTED = 1,
+ _UVRSR_FAILED = 2,
+ }
+ #[repr(C)]
+ enum _Unwind_VRS_RegClass {
+ _UVRSC_CORE = 0,
+ _UVRSC_VFP = 1,
+ _UVRSC_FPA = 2,
+ _UVRSC_WMMXD = 3,
+ _UVRSC_WMMXC = 4,
+ }
+ #[repr(C)]
+ enum _Unwind_VRS_DataRepresentation {
+ _UVRSD_UINT32 = 0,
+ _UVRSD_VFPX = 1,
+ _UVRSD_FPAX = 2,
+ _UVRSD_UINT64 = 3,
+ _UVRSD_FLOAT = 4,
+ _UVRSD_DOUBLE = 5,
+ }
+
+ type _Unwind_Word = libc::c_uint;
+ extern "C" {
+ fn _Unwind_VRS_Get(
+ ctx: *mut _Unwind_Context,
+ klass: _Unwind_VRS_RegClass,
+ word: _Unwind_Word,
+ repr: _Unwind_VRS_DataRepresentation,
+ data: *mut c_void,
+ ) -> _Unwind_VRS_Result;
+ }
+
+ pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
+ let mut val: _Unwind_Word = 0;
+ let ptr = &mut val as *mut _Unwind_Word;
+ let _ = _Unwind_VRS_Get(
+ ctx,
+ _Unwind_VRS_RegClass::_UVRSC_CORE,
+ 15,
+ _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
+ ptr as *mut c_void,
+ );
+ (val & !1) as libc::uintptr_t
+ }
+
+ // R13 is the stack pointer on arm.
+ const SP: _Unwind_Word = 13;
+
+ pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
+ let mut val: _Unwind_Word = 0;
+ let ptr = &mut val as *mut _Unwind_Word;
+ let _ = _Unwind_VRS_Get(
+ ctx,
+ _Unwind_VRS_RegClass::_UVRSC_CORE,
+ SP,
+ _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
+ ptr as *mut c_void,
+ );
+ val as libc::uintptr_t
+ }
+
+ // This function also doesn't exist on Android or ARM/Linux, so make it
+ // a no-op.
+ pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
+ pc
+ }
+ }
+ }
+}
diff --git a/library/backtrace/src/backtrace/miri.rs b/library/backtrace/src/backtrace/miri.rs
new file mode 100644
index 000000000..9a5f65b80
--- /dev/null
+++ b/library/backtrace/src/backtrace/miri.rs
@@ -0,0 +1,107 @@
+use alloc::boxed::Box;
+use alloc::vec::Vec;
+use core::ffi::c_void;
+
+extern "Rust" {
+ fn miri_backtrace_size(flags: u64) -> usize;
+ fn miri_get_backtrace(flags: u64, buf: *mut *mut ());
+ fn miri_resolve_frame(ptr: *mut (), flags: u64) -> MiriFrame;
+ fn miri_resolve_frame_names(ptr: *mut (), flags: u64, name_buf: *mut u8, filename_buf: *mut u8);
+}
+
+#[repr(C)]
+pub struct MiriFrame {
+ pub name_len: usize,
+ pub filename_len: usize,
+ pub lineno: u32,
+ pub colno: u32,
+ pub fn_ptr: *mut c_void,
+}
+
+#[derive(Clone, Debug)]
+pub struct FullMiriFrame {
+ pub name: Box<[u8]>,
+ pub filename: Box<[u8]>,
+ pub lineno: u32,
+ pub colno: u32,
+ pub fn_ptr: *mut c_void,
+}
+
+#[derive(Debug, Clone)]
+pub struct Frame {
+ pub addr: *mut c_void,
+ pub inner: FullMiriFrame,
+}
+
+// SAFETY: Miri guarantees that the returned pointer
+// can be used from any thread.
+unsafe impl Send for Frame {}
+unsafe impl Sync for Frame {}
+
+impl Frame {
+ pub fn ip(&self) -> *mut c_void {
+ self.addr
+ }
+
+ pub fn sp(&self) -> *mut c_void {
+ core::ptr::null_mut()
+ }
+
+ pub fn symbol_address(&self) -> *mut c_void {
+ self.inner.fn_ptr
+ }
+
+ pub fn module_base_address(&self) -> Option<*mut c_void> {
+ None
+ }
+}
+
+pub fn trace<F: FnMut(&super::Frame) -> bool>(cb: F) {
+ // SAFETY: Miri guarantees that the backtrace API functions
+ // can be called from any thread.
+ unsafe { trace_unsynchronized(cb) };
+}
+
+pub fn resolve_addr(ptr: *mut c_void) -> Frame {
+ // SAFETY: Miri will stop execution with an error if this pointer
+ // is invalid.
+ let frame = unsafe { miri_resolve_frame(ptr as *mut (), 1) };
+
+ let mut name = Vec::with_capacity(frame.name_len);
+ let mut filename = Vec::with_capacity(frame.filename_len);
+
+ // SAFETY: name and filename have been allocated with the amount
+ // of memory miri has asked for, and miri guarantees it will initialize it
+ unsafe {
+ miri_resolve_frame_names(ptr as *mut (), 0, name.as_mut_ptr(), filename.as_mut_ptr());
+
+ name.set_len(frame.name_len);
+ filename.set_len(frame.filename_len);
+ }
+
+ Frame {
+ addr: ptr,
+ inner: FullMiriFrame {
+ name: name.into(),
+ filename: filename.into(),
+ lineno: frame.lineno,
+ colno: frame.colno,
+ fn_ptr: frame.fn_ptr,
+ },
+ }
+}
+
+pub unsafe fn trace_unsynchronized<F: FnMut(&super::Frame) -> bool>(mut cb: F) {
+ let len = miri_backtrace_size(0);
+
+ let mut frames = Vec::with_capacity(len);
+
+ miri_get_backtrace(1, frames.as_mut_ptr());
+
+ frames.set_len(len);
+
+ for ptr in frames.iter() {
+ let frame = resolve_addr(*ptr as *mut c_void);
+ cb(&super::Frame { inner: frame });
+ }
+}
diff --git a/library/backtrace/src/backtrace/mod.rs b/library/backtrace/src/backtrace/mod.rs
new file mode 100644
index 000000000..93355d744
--- /dev/null
+++ b/library/backtrace/src/backtrace/mod.rs
@@ -0,0 +1,162 @@
+use core::ffi::c_void;
+use core::fmt;
+
+/// Inspects the current call-stack, passing all active frames into the closure
+/// provided to calculate a stack trace.
+///
+/// This function is the workhorse of this library in calculating the stack
+/// traces for a program. The given closure `cb` is yielded instances of a
+/// `Frame` which represent information about that call frame on the stack. The
+/// closure is yielded frames in a top-down fashion (most recently called
+/// functions first).
+///
+/// The closure's return value is an indication of whether the backtrace should
+/// continue. A return value of `false` will terminate the backtrace and return
+/// immediately.
+///
+/// Once a `Frame` is acquired you will likely want to call `backtrace::resolve`
+/// to convert the `ip` (instruction pointer) or symbol address to a `Symbol`
+/// through which the name and/or filename/line number can be learned.
+///
+/// Note that this is a relatively low-level function and if you'd like to, for
+/// example, capture a backtrace to be inspected later, then the `Backtrace`
+/// type may be more appropriate.
+///
+/// # Required features
+///
+/// This function requires the `std` feature of the `backtrace` crate to be
+/// enabled, and the `std` feature is enabled by default.
+///
+/// # Panics
+///
+/// This function strives to never panic, but if the `cb` provided panics then
+/// some platforms will force a double panic to abort the process. Some
+/// platforms use a C library which internally uses callbacks which cannot be
+/// unwound through, so panicking from `cb` may trigger a process abort.
+///
+/// # Example
+///
+/// ```
+/// extern crate backtrace;
+///
+/// fn main() {
+/// backtrace::trace(|frame| {
+/// // ...
+///
+/// true // continue the backtrace
+/// });
+/// }
+/// ```
+#[cfg(feature = "std")]
+pub fn trace<F: FnMut(&Frame) -> bool>(cb: F) {
+ let _guard = crate::lock::lock();
+ unsafe { trace_unsynchronized(cb) }
+}
+
+/// Same as `trace`, only unsafe as it's unsynchronized.
+///
+/// This function does not have synchronization guarantees but is available
+/// when the `std` feature of this crate isn't compiled in. See the `trace`
+/// function for more documentation and examples.
+///
+/// # Panics
+///
+/// See information on `trace` for caveats on `cb` panicking.
+pub unsafe fn trace_unsynchronized<F: FnMut(&Frame) -> bool>(mut cb: F) {
+ trace_imp(&mut cb)
+}
+
+/// A trait representing one frame of a backtrace, yielded to the `trace`
+/// function of this crate.
+///
+/// The tracing function's closure will be yielded frames, and the frame is
+/// virtually dispatched as the underlying implementation is not always known
+/// until runtime.
+#[derive(Clone)]
+pub struct Frame {
+ pub(crate) inner: FrameImp,
+}
+
+impl Frame {
+ /// Returns the current instruction pointer of this frame.
+ ///
+ /// This is normally the next instruction to execute in the frame, but not
+ /// all implementations list this with 100% accuracy (but it's generally
+ /// pretty close).
+ ///
+ /// It is recommended to pass this value to `backtrace::resolve` to turn it
+ /// into a symbol name.
+ pub fn ip(&self) -> *mut c_void {
+ self.inner.ip()
+ }
+
+ /// Returns the current stack pointer of this frame.
+ ///
+ /// In the case that a backend cannot recover the stack pointer for this
+ /// frame, a null pointer is returned.
+ pub fn sp(&self) -> *mut c_void {
+ self.inner.sp()
+ }
+
+ /// Returns the starting symbol address of the frame of this function.
+ ///
+ /// This will attempt to rewind the instruction pointer returned by `ip` to
+ /// the start of the function, returning that value. In some cases, however,
+ /// backends will just return `ip` from this function.
+ ///
+ /// The returned value can sometimes be used if `backtrace::resolve` failed
+ /// on the `ip` given above.
+ pub fn symbol_address(&self) -> *mut c_void {
+ self.inner.symbol_address()
+ }
+
+ /// Returns the base address of the module to which the frame belongs.
+ pub fn module_base_address(&self) -> Option<*mut c_void> {
+ self.inner.module_base_address()
+ }
+}
+
+impl fmt::Debug for Frame {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Frame")
+ .field("ip", &self.ip())
+ .field("symbol_address", &self.symbol_address())
+ .finish()
+ }
+}
+
+cfg_if::cfg_if! {
+ // This needs to come first, to ensure that
+ // Miri takes priority over the host platform
+ if #[cfg(miri)] {
+ pub(crate) mod miri;
+ use self::miri::trace as trace_imp;
+ pub(crate) use self::miri::Frame as FrameImp;
+ } else if #[cfg(
+ any(
+ all(
+ unix,
+ not(target_os = "emscripten"),
+ not(all(target_os = "ios", target_arch = "arm")),
+ ),
+ all(
+ target_env = "sgx",
+ target_vendor = "fortanix",
+ ),
+ )
+ )] {
+ mod libunwind;
+ use self::libunwind::trace as trace_imp;
+ pub(crate) use self::libunwind::Frame as FrameImp;
+ } else if #[cfg(all(windows, not(target_vendor = "uwp")))] {
+ mod dbghelp;
+ use self::dbghelp::trace as trace_imp;
+ pub(crate) use self::dbghelp::Frame as FrameImp;
+ #[cfg(target_env = "msvc")] // only used in dbghelp symbolize
+ pub(crate) use self::dbghelp::StackFrame;
+ } else {
+ mod noop;
+ use self::noop::trace as trace_imp;
+ pub(crate) use self::noop::Frame as FrameImp;
+ }
+}
diff --git a/library/backtrace/src/backtrace/noop.rs b/library/backtrace/src/backtrace/noop.rs
new file mode 100644
index 000000000..7bcea67aa
--- /dev/null
+++ b/library/backtrace/src/backtrace/noop.rs
@@ -0,0 +1,28 @@
+//! Empty implementation of unwinding used when no other implementation is
+//! appropriate.
+
+use core::ffi::c_void;
+
+#[inline(always)]
+pub fn trace(_cb: &mut dyn FnMut(&super::Frame) -> bool) {}
+
+#[derive(Clone)]
+pub struct Frame;
+
+impl Frame {
+ pub fn ip(&self) -> *mut c_void {
+ 0 as *mut _
+ }
+
+ pub fn sp(&self) -> *mut c_void {
+ 0 as *mut _
+ }
+
+ pub fn symbol_address(&self) -> *mut c_void {
+ 0 as *mut _
+ }
+
+ pub fn module_base_address(&self) -> Option<*mut c_void> {
+ None
+ }
+}