Skip to main content

aya_friday/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        ProgramData, ProgramError, ProgramType, define_link_wrapper, impl_try_from_fdlink,
14        impl_try_into_fdlink, load_program_without_attach_type,
15        perf_attach::{PerfLinkIdInner, PerfLinkInner, perf_attach},
16        utils::find_tracefs_path,
17    },
18    sys::{SyscallError, 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        let Self { data } = self;
69        load_program_without_attach_type(BPF_PROG_TYPE_TRACEPOINT, data)
70    }
71
72    /// Attaches to a given trace point.
73    ///
74    /// For a list of the available event categories and names, see
75    /// `/sys/kernel/debug/tracing/events`.
76    ///
77    /// The returned value can be used to detach, see [`TracePoint::detach`].
78    pub fn attach(&mut self, category: &str, name: &str) -> Result<TracePointLinkId, ProgramError> {
79        let prog_fd = self.fd()?;
80        let prog_fd = prog_fd.as_fd();
81        let tracefs = find_tracefs_path()?;
82        let id = read_sys_fs_trace_point_id(tracefs, category, name.as_ref())?;
83        let perf_fd = perf_event_open_trace_point(id, None).map_err(|io_error| SyscallError {
84            call: "perf_event_open_trace_point",
85            io_error,
86        })?;
87
88        let link = perf_attach(prog_fd, perf_fd, None /* cookie */)?;
89        self.data.links.insert(TracePointLink::new(link))
90    }
91}
92
93define_link_wrapper!(
94    TracePointLink,
95    TracePointLinkId,
96    PerfLinkInner,
97    PerfLinkIdInner,
98    TracePoint,
99);
100
101impl_try_into_fdlink!(TracePointLink, PerfLinkInner);
102impl_try_from_fdlink!(
103    TracePointLink,
104    PerfLinkInner,
105    bpf_link_type::BPF_LINK_TYPE_PERF_EVENT
106);
107
108pub(crate) fn read_sys_fs_trace_point_id(
109    tracefs: &Path,
110    category: &str,
111    name: &Path,
112) -> Result<u64, TracePointError> {
113    let filename = tracefs.join("events").join(category).join(name).join("id");
114
115    let id = match fs::read_to_string(&filename) {
116        Ok(id) => id,
117        Err(io_error) => return Err(TracePointError::FileError { filename, io_error }),
118    };
119    let id = match id.trim().parse() {
120        Ok(id) => id,
121        Err(error) => {
122            return Err(TracePointError::FileError {
123                filename,
124                io_error: io::Error::other(error),
125            });
126        }
127    };
128
129    Ok(id)
130}