#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
extern crate mach;
extern crate threadinfo;
mod unw {
include!(concat!(env!("OUT_DIR"), "/unwind_bindings.rs"));
}
use std::{cell::UnsafeCell, fs, io, mem, process};
use self::mach::{
kern_return::KERN_SUCCESS,
message::mach_msg_type_number_t,
structs::x86_thread_state64_t,
thread_act::thread_get_state,
thread_status::{thread_state_t, x86_THREAD_STATE64},
};
use self::threadinfo::{current_thread, Thread};
use types::{Frame, Sample, Unwinder};
pub struct Sampler {}
impl Sampler {
pub fn new() -> Self {
Sampler {}
}
pub fn suspend_and_resume_thread<F, T>(&self, thread: Thread, callback: F) -> T
where
F: FnOnce(&mut unw::unw_context_t) -> T,
{
debug_assert!(!thread.is_current_thread(), "Can't suspend sampler itself!");
thread.suspend().unwrap();
let mut count: mach_msg_type_number_t = x86_thread_state64_t::count();
let mut thread_state: x86_thread_state64_t = unsafe { std::mem::uninitialized() };
let mut thread_state_ptr: thread_state_t = &mut thread_state as *mut _ as thread_state_t;
let r =
unsafe { thread_get_state(thread.0, x86_THREAD_STATE64, thread_state_ptr, &mut count) };
assert!(r == KERN_SUCCESS, format!("{}", r));
assert!(
std::mem::size_of::<unw::unw_context_t>()
>= std::mem::size_of::<x86_thread_state64_t>()
);
let mut context: unw::unw_context_t = unsafe { std::mem::zeroed() };
unsafe {
std::ptr::copy_nonoverlapping::<x86_thread_state64_t>(
&mut thread_state,
std::mem::transmute(&mut context),
1,
);
}
let results = unsafe { callback(&mut context) };
thread.resume().unwrap();
results
}
}
impl Default for Sampler {
fn default() -> Self {
Self::new()
}
}
pub struct LibunwindUnwinder {
frames: Sample,
}
impl LibunwindUnwinder {
pub fn new(max_frames: usize) -> Self {
Self {
frames: Vec::with_capacity(max_frames),
}
}
}
impl Unwinder<&mut unw::unw_context_t> for LibunwindUnwinder {
fn unwind(mut self, context: &mut unw::unw_context_t) -> Result<Sample, i32> {
let mut cursor: unw::unw_cursor_t = unsafe { mem::uninitialized() };
let init = unsafe { unw::unw_init_local(&mut cursor, context) };
if init < 0 {
return Err(init);
}
loop {
if self.frames.len() == self.frames.capacity() {
break;
}
let step = unsafe { unw::unw_step(&mut cursor) };
if step == 0 {
break;
} else if step < 0 {
return Err(step);
}
let mut ip = 0;
let rr = unsafe { unw::unw_get_reg(&mut cursor, unw::UNW_REG_IP, &mut ip) };
if rr < 0 {
return Err(rr);
}
let frame = Frame { ip };
self.frames.push(frame);
}
Ok(self.frames)
}
}
#[cfg(test)]
mod tests {
extern crate rustc_demangle;
extern crate std;
use super::*;
use self::rustc_demangle::demangle;
use std::{
sync::{mpsc::channel, Arc},
thread::spawn,
};
#[test]
fn test_suspend_resume() {
let sampler = Sampler::new();
let (tx, rx) = channel();
let (tx2, rx2) = channel();
let handle = spawn(move || {
tx.send(current_thread().unwrap()).unwrap();
rx2.recv().unwrap();
});
let to = rx.recv().unwrap();
sampler.suspend_and_resume_thread(to, |context| {
let mut cursor: unw::unw_cursor_t = unsafe { mem::uninitialized() };
let init = unsafe { unw::unw_init_local(&mut cursor, context) };
assert!(init >= 0);
let mut ip = 0;
let rr = unsafe { unw::unw_get_reg(&mut cursor, unw::UNW_REG_IP, &mut ip) };
assert_eq!(rr, 0);
tx2.send(()).unwrap();
});
handle.join().unwrap();
}
#[test]
#[should_panic]
fn test_suspend_resume_itself() {
let sampler = Sampler::new();
let to = current_thread().unwrap();
sampler.suspend_and_resume_thread(to, |_| {});
}
}