use libc::pid_t;
use perf_event_open_sys as real;
use perf_event_open_sys::bindings;
use std::cell::RefCell;
use std::os::raw::{c_char, c_int, c_uint, c_ulong};
std::thread_local! {
static HOOKS: RefCell<Box<dyn Hooks + 'static>> = RefCell::new(Box::new(RealHooks));
}
pub unsafe fn set_thread_hooks(hooks: Box<dyn Hooks + 'static>) {
HOOKS.with(|per_thread| {
*per_thread.borrow_mut() = hooks;
})
}
pub unsafe fn clear_thread_hooks() {
HOOKS.with(|per_thread| {
*per_thread.borrow_mut() = Box::new(RealHooks);
})
}
macro_rules! define_ioctls {
( $expand:ident ) => {
$expand ! { ENABLE, perf_event_ioctls_ENABLE, c_uint }
$expand ! { DISABLE, perf_event_ioctls_DISABLE, c_uint }
$expand ! { REFRESH, perf_event_ioctls_REFRESH, c_int }
$expand ! { RESET, perf_event_ioctls_RESET, c_uint }
$expand ! { PERIOD, perf_event_ioctls_PERIOD, u64 }
$expand ! { SET_OUTPUT, perf_event_ioctls_SET_OUTPUT, c_int }
$expand ! { SET_FILTER, perf_event_ioctls_SET_FILTER, *mut c_char }
$expand ! { ID, perf_event_ioctls_ID, *mut u64 }
$expand ! { SET_BPF, perf_event_ioctls_SET_BPF, u32 }
$expand ! { PAUSE_OUTPUT, perf_event_ioctls_PAUSE_OUTPUT, u32 }
$expand ! { QUERY_BPF, perf_event_ioctls_QUERY_BPF, *mut bindings::perf_event_query_bpf }
$expand ! { MODIFY_ATTRIBUTES, perf_event_ioctls_MODIFY_ATTRIBUTES, *mut bindings::perf_event_attr }
}
}
macro_rules! expand_trait_method {
( $name:ident, $ioctl:ident, $arg_type:ty ) => {
#[doc = stringify!($ioctl)]
#[allow(non_snake_case)]
unsafe fn $name(&mut self, _fd: c_int, _arg: $arg_type) -> c_int {
panic!(
"unimplemented `perf_event::hooks::Hooks` method: {}",
stringify!($name)
);
}
};
}
#[allow(dead_code)]
pub trait Hooks {
#[allow(clippy::missing_safety_doc)]
unsafe fn perf_event_open(
&mut self,
attrs: *mut bindings::perf_event_attr,
pid: pid_t,
cpu: c_int,
group_fd: c_int,
flags: c_ulong,
) -> c_int;
define_ioctls!(expand_trait_method);
}
macro_rules! expand_realhooks_impl {
( $name:ident, $ioctl_:ident, $arg_type:ty ) => {
#[allow(clippy::missing_safety_doc)]
unsafe fn $name(&mut self, fd: c_int, arg: $arg_type) -> c_int {
real::ioctls::$name(fd, arg)
}
};
}
pub struct RealHooks;
impl Hooks for RealHooks {
unsafe fn perf_event_open(
&mut self,
attrs: *mut bindings::perf_event_attr,
pid: pid_t,
cpu: c_int,
group_fd: c_int,
flags: c_ulong,
) -> c_int {
real::perf_event_open(attrs, pid, cpu, group_fd, flags)
}
define_ioctls!(expand_realhooks_impl);
}
pub mod sys {
use super::HOOKS;
use libc::pid_t;
use std::os::raw::{c_int, c_ulong};
pub use perf_event_open_sys::bindings;
#[allow(clippy::missing_safety_doc)]
pub unsafe fn perf_event_open(
attrs: *mut bindings::perf_event_attr,
pid: pid_t,
cpu: c_int,
group_fd: c_int,
flags: c_ulong,
) -> c_int {
HOOKS.with(|hooks| {
hooks
.borrow_mut()
.perf_event_open(attrs, pid, cpu, group_fd, flags)
})
}
#[allow(dead_code, non_snake_case)]
pub mod ioctls {
use super::HOOKS;
use perf_event_open_sys::bindings;
use std::os::raw::{c_char, c_int, c_uint};
macro_rules! expand_hooked_ioctl {
( $name:ident, $ioctl_:ident, $arg_type:ty ) => {
#[allow(clippy::missing_safety_doc)]
pub unsafe fn $name(fd: c_int, arg: $arg_type) -> c_int {
HOOKS.with(|hooks| hooks.borrow_mut().$name(fd, arg))
}
};
}
define_ioctls!(expand_hooked_ioctl);
}
}