Skip to main content

perf_event/events/
tracepoint.rs

1use std::num::ParseIntError;
2use std::path::{Path, PathBuf};
3use std::{fmt, io};
4
5use perf_event_open_sys::bindings;
6
7use crate::events::Event;
8
9/// Kernel tracepoint event.
10///
11/// Tracepoints allow you to dynamically insert breakpoints into specific hook
12/// points defined by the kernel. These can be used to count function executions
13/// or to attach eBPF programs that run during those breakpoints.
14///
15/// Tracepoints are similar to kprobes. The difference, however, is that
16/// tracepoints are stable and documented. On the other hand, kprobes can be
17/// inserted (almost) anywhere within the kernel whereas tracepoints are
18/// restricted to only the locations at which they have been defined.
19///
20/// Note that it is possible to create tracepoints from kprobes by using
21/// [`perf probe`].
22///
23/// [`perf probe`]: https://man7.org/linux/man-pages/man1/perf-probe.1.html
24#[derive(Clone, Copy, Debug)]
25pub struct Tracepoint {
26    id: u64,
27}
28
29impl Tracepoint {
30    /// Create a tracepoint directly from its raw ID.
31    ///
32    /// Usually you will have to look within debugfs to get this ID.
33    /// [`with_name`](Tracepoint::with_name) is a helper to do this by looking
34    /// up the event ID in the debugfs instance mounted at `/sys/kernel/debug`.
35    pub fn with_id(id: u64) -> Self {
36        Self { id }
37    }
38
39    /// Create a tracepoint by looking up its ID within `/sys/kernel/debug`.
40    ///
41    /// Event names are listed under `/sys/kernel/debug/tracing/events`. All
42    /// this method does is read the file at
43    /// `/sys/kernel/debug/tracing/events/<name>/id` and use the contents of the
44    /// `id` file as the tracepoint id.
45    ///
46    /// Note that `/sys/kernel/debug` is only accessible if running as root or
47    /// if the process has `CAP_SYS_ADMIN`.
48    ///
49    /// # Example
50    /// Create a tracepoint event for the `sched_switch` tracepoint.
51    /// ```
52    /// # use perf_event::events::Tracepoint;
53    /// # fn run() -> std::io::Result<()> {
54    /// let tracepoint = Tracepoint::with_name("sched/sched_switch")?;
55    /// # Ok(())
56    /// # }
57    /// # let _ = run();
58    /// ```
59    pub fn with_name(name: impl AsRef<Path>) -> io::Result<Self> {
60        let mut path = PathBuf::from("/sys/kernel/debug/tracing/events");
61        path.push(name.as_ref());
62        path.push("id");
63
64        let id = std::fs::read_to_string(&path)?
65            .trim_end()
66            .parse()
67            .map_err(move |e| {
68                io::Error::new(io::ErrorKind::Other, UnparseableIdFile::new(path, e))
69            })?;
70
71        Ok(Self::with_id(id))
72    }
73
74    /// Get the id of this tracepoint.
75    pub fn id(&self) -> u64 {
76        self.id
77    }
78}
79
80impl Event for Tracepoint {
81    fn update_attrs(self, attr: &mut bindings::perf_event_attr) {
82        attr.type_ = bindings::PERF_TYPE_TRACEPOINT;
83        attr.config = self.id;
84    }
85}
86
87#[derive(Debug)]
88struct UnparseableIdFile {
89    path: PathBuf,
90    source: ParseIntError,
91}
92
93impl UnparseableIdFile {
94    fn new(path: PathBuf, source: ParseIntError) -> Self {
95        Self { path, source }
96    }
97}
98
99impl fmt::Display for UnparseableIdFile {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        f.write_fmt(format_args!(
102            "unparseable tracepoint id file `{}`",
103            self.path.display()
104        ))
105    }
106}
107
108impl std::error::Error for UnparseableIdFile {
109    fn cause(&self) -> Option<&dyn std::error::Error> {
110        Some(&self.source)
111    }
112}