use alloc::{boxed::Box, sync::Arc};
use core::any::Any;
use ax_errno::{AxError, AxResult};
use axpoll::Pollable;
use kbpf_basic::perf::{PerfProbeArgs, PerfProbeConfig};
use ktracepoint::{TraceCallbackType, TraceEventFunc};
use crate::{
file::FileLike,
perf::{PerfEventOps, bpf::OwnedEbpfVm},
tracepoint::{KernelExtTracePoint, lookup_ext_tracepoint},
};
type TpCallback = Box<dyn Fn(&[u8], &(dyn Any + Send + Sync)) + Send + Sync>;
pub struct TracepointPerfEvent {
_args: PerfProbeArgs,
ext_tp: KernelExtTracePoint,
registered: alloc::vec::Vec<Arc<TraceEventFunc>>,
}
impl core::fmt::Debug for TracepointPerfEvent {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TracepointPerfEvent").finish()
}
}
impl TracepointPerfEvent {
pub fn new(args: PerfProbeArgs, ext_tp: KernelExtTracePoint) -> Self {
Self {
_args: args,
ext_tp,
registered: alloc::vec::Vec::new(),
}
}
}
impl Pollable for TracepointPerfEvent {
fn poll(&self) -> axpoll::IoEvents {
axpoll::IoEvents::empty()
}
fn register(&self, _context: &mut core::task::Context<'_>, _events: axpoll::IoEvents) {
}
}
impl PerfEventOps for TracepointPerfEvent {
fn set_bpf_prog(&mut self, bpf_prog: Arc<dyn FileLike>) -> AxResult<()> {
struct Ctx {
vm: OwnedEbpfVm,
}
let ctx = Box::new(Ctx {
vm: OwnedEbpfVm::new(bpf_prog)?,
});
let func: TpCallback = Box::new(|entry: &[u8], data: &(dyn Any + Send + Sync)| {
let ctx = data.downcast_ref::<Ctx>().expect("tracepoint Ctx mismatch");
let entry =
unsafe { core::slice::from_raw_parts_mut(entry.as_ptr() as *mut u8, entry.len()) };
if let Err(e) = ctx.vm.execute_program(entry) {
error!("tracepoint BPF program failed: {e:?}");
}
});
let callback = Arc::new(TraceEventFunc::new(func, ctx));
self.ext_tp
.lock()
.register(TraceCallbackType::Event(callback.clone()));
self.registered.push(callback);
Ok(())
}
fn enable(&mut self) -> AxResult<()> {
for cb in &self.registered {
cb.set_perf_enable(true);
}
Ok(())
}
fn disable(&mut self) -> AxResult<()> {
for cb in &self.registered {
cb.set_perf_enable(false);
}
Ok(())
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl Drop for TracepointPerfEvent {
fn drop(&mut self) {
let mut ext_tp = self.ext_tp.lock();
for cb in self.registered.drain(..) {
ext_tp.unregister(TraceCallbackType::Event(cb));
}
}
}
pub fn perf_event_open_tracepoint(args: PerfProbeArgs) -> AxResult<TracepointPerfEvent> {
let tp_id = match args.config {
PerfProbeConfig::Raw(id) => id as u32,
_ => return Err(AxError::InvalidInput),
};
let ext_tp = lookup_ext_tracepoint(tp_id).ok_or(AxError::NotFound)?;
Ok(TracepointPerfEvent::new(args, ext_tp))
}