1use alloc::boxed::Box;
2use alloc::vec::Vec;
3use core::ffi::c_void;
4
5unsafe extern "Rust" {
6 unsafefn miri_backtrace_size(flags: u64) -> usize;
7 unsafefn miri_get_backtrace(flags: u64, buf: *mut *mut ());
8 unsafefn miri_resolve_frame(ptr: *mut (), flags: u64) -> MiriFrame;
9 unsafefn miri_resolve_frame_names(ptr: *mut (), flags: u64, name_buf: *mut u8, filename_buf: *mut u8);
10}
11
12#[repr(C)]
13pub struct MiriFrame {
14 pub name_len: usize,
15 pub filename_len: usize,
16 pub lineno: u32,
17 pub colno: u32,
18 pub fn_ptr: *mut c_void,
19}
20
21#[derive(Clone, Debug)]
22pub struct FullMiriFrame {
23 pub name: Box<[u8]>,
24 pub filename: Box<[u8]>,
25 pub lineno: u32,
26 pub colno: u32,
27 pub fn_ptr: *mut c_void,
28}
29
30#[derive(Debug, Clone)]
31pub struct Frame {
32 pub addr: *mut c_void,
33 pub inner: FullMiriFrame,
34}
35
36// SAFETY: Miri guarantees that the returned pointer
37// can be used from any thread.
38unsafe impl Send for Frame {}
39unsafe impl Sync for Frame {}
40
41impl Frame {
42 pub fn ip(&self) -> *mut c_void {
43 self.addr
44 }
45
46 pub fn sp(&self) -> *mut c_void {
47 core::ptr::null_mut()
48 }
49
50 pub fn symbol_address(&self) -> *mut c_void {
51 self.inner.fn_ptr
52 }
53
54 pub fn module_base_address(&self) -> Option<*mut c_void> {
55 None
56 }
57}
58
59// SAFETY: This function is safe to call. It is only marked as `unsafe` to
60// avoid having to allow `unused_unsafe` since other implementations are
61// unsafe.
62pub unsafe fn trace<F: FnMut(&super::Frame) -> bool>(cb: F) {
63 // SAFETY: Miri guarantees that the backtrace API functions
64 // can be called from any thread.
65 unsafe { trace_unsynchronized(cb) };
66}
67
68pub fn resolve_addr(ptr: *mut c_void) -> Frame {
69 // SAFETY: Miri will stop execution with an error if this pointer
70 // is invalid.
71 let frame = unsafe { miri_resolve_frame(ptr.cast::<()>(), 1) };
72
73 let mut name = Vec::with_capacity(frame.name_len);
74 let mut filename = Vec::with_capacity(frame.filename_len);
75
76 // SAFETY: name and filename have been allocated with the amount
77 // of memory miri has asked for, and miri guarantees it will initialize it
78 unsafe {
79 miri_resolve_frame_names(
80 ptr.cast::<()>(),
81 0,
82 name.as_mut_ptr(),
83 filename.as_mut_ptr(),
84 );
85
86 name.set_len(frame.name_len);
87 filename.set_len(frame.filename_len);
88 }
89
90 Frame {
91 addr: ptr,
92 inner: FullMiriFrame {
93 name: name.into(),
94 filename: filename.into(),
95 lineno: frame.lineno,
96 colno: frame.colno,
97 fn_ptr: frame.fn_ptr,
98 },
99 }
100}
101
102unsafe fn trace_unsynchronized<F: FnMut(&super::Frame) -> bool>(mut cb: F) {
103 let len: usize = unsafe { miri_backtrace_size(flags:0) };
104
105 let mut frames: Vec<*mut ()> = Vec::with_capacity(len);
106
107 unsafe {
108 miri_get_backtrace(flags:1, buf:frames.as_mut_ptr());
109
110 frames.set_len(len);
111 }
112
113 for ptr: &*mut () in frames.iter() {
114 let frame: Frame = resolve_addr((*ptr).cast::<c_void>());
115 if !cb(&super::Frame { inner: frame }) {
116 return;
117 }
118 }
119}
120