Skip to main content

profile_bee_aya/programs/
trace_point.rs

1//! Tracepoint programs.
2use std::{
3    fs, io,
4    os::fd::AsFd as _,
5    path::{Path, PathBuf},
6};
7
8use aya_obj::generated::{bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT};
9use thiserror::Error;
10
11use crate::{
12    programs::{
13        FdLink, LinkError, ProgramData, ProgramError, ProgramType, define_link_wrapper,
14        impl_try_into_fdlink, load_program,
15        perf_attach::{PerfLinkIdInner, PerfLinkInner, perf_attach},
16        utils::find_tracefs_path,
17    },
18    sys::{SyscallError, bpf_link_get_info_by_fd, perf_event_open_trace_point},
19};
20
21/// The type returned when attaching a [`TracePoint`] fails.
22#[derive(Debug, Error)]
23pub enum TracePointError {
24    /// Error detaching from debugfs
25    #[error("`{filename}`")]
26    FileError {
27        /// The file name
28        filename: PathBuf,
29        /// The [`io::Error`] returned from the file operation
30        #[source]
31        io_error: io::Error,
32    },
33}
34
35/// A program that can be attached at a pre-defined kernel trace point.
36///
37/// The kernel provides a set of pre-defined trace points that eBPF programs can
38/// be attached to. See `/sys/kernel/debug/tracing/events` for a list of which
39/// events can be traced.
40///
41/// # Minimum kernel version
42///
43/// The minimum kernel version required to use this feature is 4.7.
44///
45/// # Examples
46///
47/// ```no_run
48/// # let mut bpf = aya::Ebpf::load(&[])?;
49/// use aya::programs::TracePoint;
50///
51/// let prog: &mut TracePoint = bpf.program_mut("trace_context_switch").unwrap().try_into()?;
52/// prog.load()?;
53/// prog.attach("sched", "sched_switch")?;
54/// # Ok::<(), aya::EbpfError>(())
55/// ```
56#[derive(Debug)]
57#[doc(alias = "BPF_PROG_TYPE_TRACEPOINT")]
58pub struct TracePoint {
59    pub(crate) data: ProgramData<TracePointLink>,
60}
61
62impl TracePoint {
63    /// The type of the program according to the kernel.
64    pub const PROGRAM_TYPE: ProgramType = ProgramType::TracePoint;
65
66    /// Loads the program inside the kernel.
67    pub fn load(&mut self) -> Result<(), ProgramError> {
68        load_program(BPF_PROG_TYPE_TRACEPOINT, &mut self.data)
69    }
70
71    /// Attaches to a given trace point.
72    ///
73    /// For a list of the available event categories and names, see
74    /// `/sys/kernel/debug/tracing/events`.
75    ///
76    /// The returned value can be used to detach, see [`TracePoint::detach`].
77    pub fn attach(&mut self, category: &str, name: &str) -> Result<TracePointLinkId, ProgramError> {
78        let prog_fd = self.fd()?;
79        let prog_fd = prog_fd.as_fd();
80        let tracefs = find_tracefs_path()?;
81        let id = read_sys_fs_trace_point_id(tracefs, category, name.as_ref())?;
82        let perf_fd = perf_event_open_trace_point(id, None).map_err(|io_error| SyscallError {
83            call: "perf_event_open_trace_point",
84            io_error,
85        })?;
86
87        let link = perf_attach(prog_fd, perf_fd, None /* cookie */)?;
88        self.data.links.insert(TracePointLink::new(link))
89    }
90}
91
92define_link_wrapper!(
93    TracePointLink,
94    TracePointLinkId,
95    PerfLinkInner,
96    PerfLinkIdInner,
97    TracePoint,
98);
99
100impl_try_into_fdlink!(TracePointLink, PerfLinkInner);
101
102impl TryFrom<FdLink> for TracePointLink {
103    type Error = LinkError;
104
105    fn try_from(fd_link: FdLink) -> Result<Self, Self::Error> {
106        let info = bpf_link_get_info_by_fd(fd_link.fd.as_fd())?;
107        if info.type_ == (bpf_link_type::BPF_LINK_TYPE_TRACING as u32) {
108            return Ok(Self::new(PerfLinkInner::Fd(fd_link)));
109        }
110        Err(LinkError::InvalidLink)
111    }
112}
113
114pub(crate) fn read_sys_fs_trace_point_id(
115    tracefs: &Path,
116    category: &str,
117    name: &Path,
118) -> Result<u64, TracePointError> {
119    let filename = tracefs.join("events").join(category).join(name).join("id");
120
121    let id = match fs::read_to_string(&filename) {
122        Ok(id) => id,
123        Err(io_error) => return Err(TracePointError::FileError { filename, io_error }),
124    };
125    let id = match id.trim().parse() {
126        Ok(id) => id,
127        Err(error) => {
128            return Err(TracePointError::FileError {
129                filename,
130                io_error: io::Error::other(error),
131            });
132        }
133    };
134
135    Ok(id)
136}