summaryrefslogtreecommitdiffstats
path: root/library/backtrace/src/backtrace/miri.rs
blob: f8c4964284e8f49649cc73bccb1921a6c9a52477 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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,
        },
    }
}

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);
        if !cb(&super::Frame { inner: frame }) {
            return;
        }
    }
}