Skip to content

Commit

Permalink
Add support for Miri backtraces
Browse files Browse the repository at this point in the history
Companion to rust-lang/miri#1559
  • Loading branch information
Aaron1011 committed Sep 28, 2020
1 parent 37ec940 commit 203d6d7
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 7 deletions.
69 changes: 69 additions & 0 deletions src/backtrace/miri.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use std::boxed::Box;
use std::ffi::c_void;

extern "Rust" {
fn miri_get_backtrace(flags: u64) -> Box<[*mut ()]>;
fn miri_resolve_frame(ptr: *mut (), flags: u64) -> MiriFrame;
}

#[derive(Clone, Debug)]
#[repr(C)]
pub struct MiriFrame {
pub name: Box<[u8]>,
pub filename: Box<[u8]>,
pub lineno: u32,
pub colno: u32,
}

#[derive(Debug, Clone)]
pub struct Frame {
pub addr: *mut c_void,
pub inner: MiriFrame,
}

// 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 {
std::ptr::null_mut()
}

pub fn symbol_address(&self) -> *mut c_void {
self.addr
}

pub fn module_base_address(&self) -> Option<*mut c_void> {
None
}
}

pub fn trace<F: FnMut(&super::Frame) -> bool>(cb: F) {
// SAFETY: Miri guarnatees 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: MiriFrame = unsafe { miri_resolve_frame(ptr as *mut (), 0) };
Frame {
addr: ptr,
inner: frame,
}
}

pub unsafe fn trace_unsynchronized<F: FnMut(&super::Frame) -> bool>(mut cb: F) {
let frames = miri_get_backtrace(0);
for ptr in frames.iter() {
let frame = resolve_addr(*ptr as *mut c_void);
cb(&super::Frame { inner: frame });
}
}
8 changes: 5 additions & 3 deletions src/backtrace/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,12 @@ impl fmt::Debug for Frame {
}

cfg_if::cfg_if! {
// This needs to come first, to ensure that
// Miri takes priority over the host platform
if #[cfg(miri)] {
mod noop;
use self::noop::trace as trace_imp;
pub(crate) use self::noop::Frame as FrameImp;
pub(crate) mod miri;
use self::miri::trace as trace_imp;
pub(crate) use self::miri::Frame as FrameImp;
} else if #[cfg(
any(
all(
Expand Down
54 changes: 54 additions & 0 deletions src/symbolize/miri.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use core::ffi::c_void;
use core::marker::PhantomData;

use crate::backtrace::miri::{resolve_addr, Frame};
use crate::symbolize::{ResolveWhat, SymbolName};
use crate::types::BytesOrWideString;

pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) {
let sym = match what {
ResolveWhat::Address(addr) => Symbol {
inner: resolve_addr(addr),
_unused: PhantomData,
},
ResolveWhat::Frame(frame) => Symbol {
inner: frame.inner.clone(),
_unused: PhantomData,
},
};
cb(&super::Symbol { inner: sym })
}

pub struct Symbol<'a> {
inner: Frame,
_unused: PhantomData<&'a ()>,
}

impl<'a> Symbol<'a> {
pub fn name(&self) -> Option<SymbolName<'_>> {
Some(SymbolName::new(&self.inner.inner.name))
}

pub fn addr(&self) -> Option<*mut c_void> {
Some(self.inner.addr)
}

pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
Some(BytesOrWideString::Bytes(&self.inner.inner.filename))
}

pub fn lineno(&self) -> Option<u32> {
Some(self.inner.inner.lineno)
}

pub fn colno(&self) -> Option<u32> {
Some(self.inner.inner.colno)
}

pub fn filename(&self) -> Option<&::std::path::Path> {
use std::path::Path;
Some(Path::new(
std::str::from_utf8(&self.inner.inner.filename).unwrap(),
))
}
}
7 changes: 3 additions & 4 deletions src/symbolize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,10 +465,9 @@ pub fn clear_symbol_cache() {

cfg_if::cfg_if! {
if #[cfg(miri)] {
mod noop;
use self::noop::resolve as resolve_imp;
use self::noop::Symbol as SymbolImp;
#[allow(unused)]
mod miri;
use self::miri::resolve as resolve_imp;
use self::miri::Symbol as SymbolImp;
unsafe fn clear_symbol_cache_imp() {}
} else if #[cfg(all(windows, target_env = "msvc", not(target_vendor = "uwp")))] {
mod dbghelp;
Expand Down

0 comments on commit 203d6d7

Please sign in to comment.