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 unsafe { Ok(crate::MockableFd::from_raw_fd(fd)) }
142}
143
144