aya/sys/
perf_event.rs

1use std::{
2    ffi::{c_int, CString, OsStr},
3    io, mem,
4    os::fd::{BorrowedFd, FromRawFd as _},
5};
6
7use libc::pid_t;
8
9use super::{syscall, SysResult, Syscall};
10use crate::generated::{
11    perf_event_attr,
12    perf_event_sample_format::PERF_SAMPLE_RAW,
13    perf_sw_ids::PERF_COUNT_SW_BPF_OUTPUT,
14    perf_type_id::{PERF_TYPE_SOFTWARE, PERF_TYPE_TRACEPOINT},
15    PERF_FLAG_FD_CLOEXEC,
16};
17
18#[allow(clippy::too_many_arguments)]
19pub(crate) fn perf_event_open(
20    perf_type: u32,
21    config: u64,
22    pid: pid_t,
23    cpu: c_int,
24    sample_period: u64,
25    sample_frequency: Option<u64>,
26    wakeup: bool,
27    inherit: bool,
28    flags: u32,
29) -> SysResult<crate::MockableFd> {
30    let mut attr = unsafe { mem::zeroed::<perf_event_attr>() };
31
32    attr.config = config;
33    attr.size = mem::size_of::<perf_event_attr>() as u32;
34    attr.type_ = perf_type;
35    attr.sample_type = PERF_SAMPLE_RAW as u64;
36    attr.set_inherit(if inherit { 1 } else { 0 });
37    attr.__bindgen_anon_2.wakeup_events = u32::from(wakeup);
38
39    if let Some(frequency) = sample_frequency {
40        attr.set_freq(1);
41        attr.__bindgen_anon_1.sample_freq = frequency;
42    } else {
43        attr.__bindgen_anon_1.sample_period = sample_period;
44    }
45
46    perf_event_sys(attr, pid, cpu, flags)
47}
48
49pub(crate) fn perf_event_open_bpf(cpu: c_int) -> SysResult<crate::MockableFd> {
50    perf_event_open(
51        PERF_TYPE_SOFTWARE as u32,
52        PERF_COUNT_SW_BPF_OUTPUT as u64,
53        -1,
54        cpu,
55        1,
56        None,
57        true,
58        false,
59        PERF_FLAG_FD_CLOEXEC,
60    )
61}
62
63pub(crate) fn perf_event_open_probe(
64    ty: u32,
65    ret_bit: Option<u32>,
66    name: &OsStr,
67    offset: u64,
68    pid: Option<pid_t>,
69) -> SysResult<crate::MockableFd> {
70    use std::os::unix::ffi::OsStrExt as _;
71
72    let mut attr = unsafe { mem::zeroed::<perf_event_attr>() };
73
74    if let Some(ret_bit) = ret_bit {
75        attr.config = 1 << ret_bit;
76    }
77
78    let c_name = CString::new(name.as_bytes()).unwrap();
79
80    attr.size = mem::size_of::<perf_event_attr>() as u32;
81    attr.type_ = ty;
82    attr.__bindgen_anon_3.config1 = c_name.as_ptr() as u64;
83    attr.__bindgen_anon_4.config2 = offset;
84
85    let cpu = if pid.is_some() { -1 } else { 0 };
86    let pid = pid.unwrap_or(-1);
87
88    perf_event_sys(attr, pid, cpu, PERF_FLAG_FD_CLOEXEC)
89}
90
91pub(crate) fn perf_event_open_trace_point(
92    id: u32,
93    pid: Option<pid_t>,
94) -> SysResult<crate::MockableFd> {
95    let mut attr = unsafe { mem::zeroed::<perf_event_attr>() };
96
97    attr.size = mem::size_of::<perf_event_attr>() as u32;
98    attr.type_ = PERF_TYPE_TRACEPOINT as u32;
99    attr.config = id as u64;
100
101    let cpu = if pid.is_some() { -1 } else { 0 };
102    let pid = pid.unwrap_or(-1);
103
104    perf_event_sys(attr, pid, cpu, PERF_FLAG_FD_CLOEXEC)
105}
106
107pub(crate) fn perf_event_ioctl(fd: BorrowedFd<'_>, request: c_int, arg: c_int) -> SysResult<i64> {
108    let call = Syscall::PerfEventIoctl { fd, request, arg };
109    #[cfg(not(test))]
110    return syscall(call);
111
112    #[cfg(test)]
113    return crate::sys::TEST_SYSCALL.with(|test_impl| unsafe { test_impl.borrow()(call) });
114}
115
116fn perf_event_sys(
117    attr: perf_event_attr,
118    pid: pid_t,
119    cpu: i32,
120    flags: u32,
121) -> SysResult<crate::MockableFd> {
122    let fd = syscall(Syscall::PerfEventOpen {
123        attr,
124        pid,
125        cpu,
126        group: -1,
127        flags,
128    })?;
129
130    let fd = fd.try_into().map_err(|_| {
131        (
132            fd,
133            io::Error::new(
134                io::ErrorKind::InvalidData,
135                format!("perf_event_open: invalid fd returned: {fd}"),
136            ),
137        )
138    })?;
139
140    // SAFETY: perf_event_open returns a new file descriptor on success.
141    unsafe { Ok(crate::MockableFd::from_raw_fd(fd)) }
142}
143
144/*
145impl TryFrom<u32> for perf_event_type {
146    PERF_RECORD_MMAP = 1,
147    PERF_RECORD_LOST = 2,
148    PERF_RECORD_COMM = 3,
149    PERF_RECORD_EXIT = 4,
150    PERF_RECORD_THROTTLE = 5,
151    PERF_RECORD_UNTHROTTLE = 6,
152    PERF_RECORD_FORK = 7,
153    PERF_RECORD_READ = 8,
154    PERF_RECORD_SAMPLE = 9,
155    PERF_RECORD_MMAP2 = 10,
156    PERF_RECORD_AUX = 11,
157    PERF_RECORD_ITRACE_START = 12,
158    PERF_RECORD_LOST_SAMPLES = 13,
159    PERF_RECORD_SWITCH = 14,
160    PERF_RECORD_SWITCH_CPU_WIDE = 15,
161    PERF_RECORD_NAMESPACES = 16,
162    PERF_RECORD_KSYMBOL = 17,
163    PERF_RECORD_BPF_EVENT = 18,
164    PERF_RECORD_CGROUP = 19,
165    PERF_RECORD_MAX
166
167    type Error = ();
168
169    fn try_from(value: u32) -> Result<Self, Self::Error> {
170        todo!()
171    }
172}
173*/