use std::{
fs, io,
os::fd::AsFd as _,
path::{Path, PathBuf},
};
use aya_obj::generated::{bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT};
use thiserror::Error;
use crate::{
programs::{
FdLink, LinkError, ProgramData, ProgramError, ProgramType, define_link_wrapper,
impl_try_into_fdlink, load_program,
perf_attach::{PerfLinkIdInner, PerfLinkInner, perf_attach},
utils::find_tracefs_path,
},
sys::{SyscallError, bpf_link_get_info_by_fd, perf_event_open_trace_point},
};
#[derive(Debug, Error)]
pub enum TracePointError {
#[error("`{filename}`")]
FileError {
filename: PathBuf,
#[source]
io_error: io::Error,
},
}
#[derive(Debug)]
#[doc(alias = "BPF_PROG_TYPE_TRACEPOINT")]
pub struct TracePoint {
pub(crate) data: ProgramData<TracePointLink>,
}
impl TracePoint {
pub const PROGRAM_TYPE: ProgramType = ProgramType::TracePoint;
pub fn load(&mut self) -> Result<(), ProgramError> {
load_program(BPF_PROG_TYPE_TRACEPOINT, &mut self.data)
}
pub fn attach(&mut self, category: &str, name: &str) -> Result<TracePointLinkId, ProgramError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let tracefs = find_tracefs_path()?;
let id = read_sys_fs_trace_point_id(tracefs, category, name.as_ref())?;
let perf_fd = perf_event_open_trace_point(id, None).map_err(|io_error| SyscallError {
call: "perf_event_open_trace_point",
io_error,
})?;
let link = perf_attach(prog_fd, perf_fd, None )?;
self.data.links.insert(TracePointLink::new(link))
}
}
define_link_wrapper!(
TracePointLink,
TracePointLinkId,
PerfLinkInner,
PerfLinkIdInner,
TracePoint,
);
impl_try_into_fdlink!(TracePointLink, PerfLinkInner);
impl TryFrom<FdLink> for TracePointLink {
type Error = LinkError;
fn try_from(fd_link: FdLink) -> Result<Self, Self::Error> {
let info = bpf_link_get_info_by_fd(fd_link.fd.as_fd())?;
if info.type_ == (bpf_link_type::BPF_LINK_TYPE_TRACING as u32) {
return Ok(Self::new(PerfLinkInner::Fd(fd_link)));
}
Err(LinkError::InvalidLink)
}
}
pub(crate) fn read_sys_fs_trace_point_id(
tracefs: &Path,
category: &str,
name: &Path,
) -> Result<u64, TracePointError> {
let filename = tracefs.join("events").join(category).join(name).join("id");
let id = match fs::read_to_string(&filename) {
Ok(id) => id,
Err(io_error) => return Err(TracePointError::FileError { filename, io_error }),
};
let id = match id.trim().parse() {
Ok(id) => id,
Err(error) => {
return Err(TracePointError::FileError {
filename,
io_error: io::Error::other(error),
});
}
};
Ok(id)
}