use std::{fs, io, os::fd::AsFd as _, path::Path};
use thiserror::Error;
use crate::{
generated::{bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT},
programs::{
define_link_wrapper, load_program,
perf_attach::{perf_attach, PerfLinkIdInner, PerfLinkInner},
utils::find_tracefs_path,
FdLink, LinkError, ProgramData, ProgramError,
},
sys::{bpf_link_get_info_by_fd, perf_event_open_trace_point, SyscallError},
};
#[derive(Debug, Error)]
pub enum TracePointError {
#[error("`{filename}`")]
FileError {
filename: String,
#[source]
io_error: io::Error,
},
}
#[derive(Debug)]
#[doc(alias = "BPF_PROG_TYPE_TRACEPOINT")]
pub struct TracePoint {
pub(crate) data: ProgramData<TracePointLink>,
}
impl 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 fd =
perf_event_open_trace_point(id, None).map_err(|(_code, io_error)| SyscallError {
call: "perf_event_open_trace_point",
io_error,
})?;
let link = perf_attach(prog_fd, fd)?;
self.data.links.insert(TracePointLink::new(link))
}
pub fn detach(&mut self, link_id: TracePointLinkId) -> Result<(), ProgramError> {
self.data.links.remove(link_id)
}
pub fn take_link(&mut self, link_id: TracePointLinkId) -> Result<TracePointLink, ProgramError> {
self.data.take_link(link_id)
}
}
define_link_wrapper!(
TracePointLink,
TracePointLinkId,
PerfLinkInner,
PerfLinkIdInner
);
impl TryFrom<TracePointLink> for FdLink {
type Error = LinkError;
fn try_from(value: TracePointLink) -> Result<Self, Self::Error> {
if let PerfLinkInner::FdLink(fd) = value.into_inner() {
Ok(fd)
} else {
Err(LinkError::InvalidLink)
}
}
}
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::FdLink(fd_link)));
}
Err(LinkError::InvalidLink)
}
}
pub(crate) fn read_sys_fs_trace_point_id(
tracefs: &Path,
category: &str,
name: &Path,
) -> Result<u32, TracePointError> {
let file = tracefs.join("events").join(category).join(name).join("id");
let id = fs::read_to_string(&file).map_err(|io_error| TracePointError::FileError {
filename: file.display().to_string(),
io_error,
})?;
let id = id
.trim()
.parse::<u32>()
.map_err(|error| TracePointError::FileError {
filename: file.display().to_string(),
io_error: io::Error::new(io::ErrorKind::Other, error),
})?;
Ok(id)
}