mirror of https://github.com/aya-rs/aya
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
141 lines
4.2 KiB
Rust
141 lines
4.2 KiB
Rust
//! Tracepoint programs.
|
|
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},
|
|
};
|
|
|
|
/// The type returned when attaching a [`TracePoint`] fails.
|
|
#[derive(Debug, Error)]
|
|
pub enum TracePointError {
|
|
/// Error detaching from debugfs
|
|
#[error("`{filename}`")]
|
|
FileError {
|
|
/// The file name
|
|
filename: String,
|
|
/// The [`io::Error`] returned from the file operation
|
|
#[source]
|
|
io_error: io::Error,
|
|
},
|
|
}
|
|
|
|
/// A program that can be attached at a pre-defined kernel trace point.
|
|
///
|
|
/// The kernel provides a set of pre-defined trace points that eBPF programs can
|
|
/// be attached to. See `/sys/kernel/debug/tracing/events` for a list of which
|
|
/// events can be traced.
|
|
///
|
|
/// # Minimum kernel version
|
|
///
|
|
/// The minimum kernel version required to use this feature is 4.7.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```no_run
|
|
/// # let mut bpf = aya::Ebpf::load(&[])?;
|
|
/// use aya::programs::TracePoint;
|
|
///
|
|
/// let prog: &mut TracePoint = bpf.program_mut("trace_context_switch").unwrap().try_into()?;
|
|
/// prog.load()?;
|
|
/// prog.attach("sched", "sched_switch")?;
|
|
/// # Ok::<(), aya::EbpfError>(())
|
|
/// ```
|
|
#[derive(Debug)]
|
|
#[doc(alias = "BPF_PROG_TYPE_TRACEPOINT")]
|
|
pub struct TracePoint {
|
|
pub(crate) data: ProgramData<TracePointLink>,
|
|
}
|
|
|
|
impl TracePoint {
|
|
/// Loads the program inside the kernel.
|
|
pub fn load(&mut self) -> Result<(), ProgramError> {
|
|
load_program(BPF_PROG_TYPE_TRACEPOINT, &mut self.data)
|
|
}
|
|
|
|
/// Attaches to a given trace point.
|
|
///
|
|
/// For a list of the available event categories and names, see
|
|
/// `/sys/kernel/debug/tracing/events`.
|
|
///
|
|
/// The returned value can be used to detach, see [TracePoint::detach].
|
|
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, None /* cookie */)?;
|
|
self.data.links.insert(TracePointLink::new(link))
|
|
}
|
|
}
|
|
|
|
define_link_wrapper!(
|
|
/// The link used by [TracePoint] programs.
|
|
TracePointLink,
|
|
/// The type returned by [TracePoint::attach]. Can be passed to [TracePoint::detach].
|
|
TracePointLinkId,
|
|
PerfLinkInner,
|
|
PerfLinkIdInner,
|
|
TracePoint,
|
|
);
|
|
|
|
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)
|
|
}
|