Skip to content

Commit 391d058

Browse files
committed
aya: plumb PerfEventScope through probe attach
Remove the intermediate Option<u32> pid plumbing from probe attachment paths and pass PerfEventScope through to the perf_event helpers directly. This keeps the UProbeScope to PerfEventScope conversion in UProbe::attach, where both the process scope and /proc/<pid>/maps lookup semantics are visible. These kprobe, uprobe, and tracepoint attach paths are backed by perf_event. For all-processes attachment, perf_event_open requires an explicit CPU, so use CPU 0 only to open the backing perf event. Process-scoped uprobes continue to use cpu=-1 through PerfEventScope.
1 parent 9c32f80 commit 391d058

5 files changed

Lines changed: 79 additions & 45 deletions

File tree

aya/src/programs/kprobe.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::{
1515
ProgramData, ProgramError, ProgramType, define_link_wrapper, impl_try_from_fdlink,
1616
impl_try_into_fdlink, load_program_without_attach_type,
1717
perf_attach::{PerfLinkIdInner, PerfLinkInner},
18+
perf_event::PerfEventScope,
1819
probe::{Probe, ProbeKind, attach},
1920
},
2021
};
@@ -87,7 +88,9 @@ impl KProbe {
8788
*kind,
8889
fn_name.as_ref(),
8990
offset,
90-
None, // pid
91+
// For all-processes attachment, perf_event_open requires an explicit
92+
// CPU. Use CPU 0 only to open the backing perf event.
93+
PerfEventScope::AllProcessesOneCpu { cpu: 0 },
9194
None, // cookie
9295
)
9396
}

aya/src/programs/probe.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use std::{
1212
use crate::{
1313
programs::{
1414
Link, ProgramData, ProgramError, perf_attach, perf_attach::PerfLinkInner,
15-
perf_attach_debugfs, trace_point::read_sys_fs_trace_point_id, utils::find_tracefs_path,
15+
perf_attach_debugfs, perf_event::PerfEventScope, trace_point::read_sys_fs_trace_point_id,
16+
utils::find_tracefs_path,
1617
},
1718
sys::{SyscallError, perf_event_open_probe, perf_event_open_trace_point},
1819
util::KernelVersion,
@@ -145,21 +146,21 @@ pub(crate) fn attach<P: Probe, T: Link + From<PerfLinkInner>>(
145146
// separate argument.
146147
fn_name: &OsStr,
147148
offset: u64,
148-
pid: Option<u32>,
149+
scope: PerfEventScope,
149150
cookie: Option<u64>,
150151
) -> Result<T::Id, ProgramError> {
151152
// https://github.com/torvalds/linux/commit/e12f03d7031a977356e3d7b75a68c2185ff8d155
152153
// Use debugfs to create probe
153154
let prog_fd = program_data.fd()?;
154155
let prog_fd = prog_fd.as_fd();
155156
let link = if KernelVersion::at_least(4, 17, 0) {
156-
let perf_fd = create_as_probe::<P>(kind, fn_name, offset, pid)?;
157+
let perf_fd = create_as_probe::<P>(kind, fn_name, offset, scope)?;
157158
perf_attach(prog_fd, perf_fd, cookie)
158159
} else {
159160
if cookie.is_some() {
160161
return Err(ProgramError::AttachCookieNotSupported);
161162
}
162-
let (perf_fd, event) = create_as_trace_point::<P>(kind, fn_name, offset, pid)?;
163+
let (perf_fd, event) = create_as_trace_point::<P>(kind, fn_name, offset, scope)?;
163164
perf_attach_debugfs(prog_fd, perf_fd, event)
164165
}?;
165166
program_data.links.insert(T::from(link))
@@ -176,7 +177,7 @@ fn create_as_probe<P: Probe>(
176177
kind: ProbeKind,
177178
fn_name: &OsStr,
178179
offset: u64,
179-
pid: Option<u32>,
180+
scope: PerfEventScope,
180181
) -> Result<crate::MockableFd, ProgramError> {
181182
let perf_ty = read_sys_fs_perf_type(P::PMU)
182183
.map_err(|(filename, io_error)| P::file_error(filename, io_error).into())?;
@@ -189,7 +190,7 @@ fn create_as_probe<P: Probe>(
189190
ProbeKind::Entry => None,
190191
};
191192

192-
perf_event_open_probe(perf_ty, ret_bit, fn_name, offset, pid)
193+
perf_event_open_probe(perf_ty, ret_bit, fn_name, offset, scope)
193194
.map_err(|io_error| SyscallError {
194195
call: "perf_event_open",
195196
io_error,
@@ -201,7 +202,7 @@ fn create_as_trace_point<P: Probe>(
201202
kind: ProbeKind,
202203
name: &OsStr,
203204
offset: u64,
204-
pid: Option<u32>,
205+
scope: PerfEventScope,
205206
) -> Result<(crate::MockableFd, ProbeEvent), ProgramError> {
206207
let tracefs = find_tracefs_path()?;
207208

@@ -214,7 +215,7 @@ fn create_as_trace_point<P: Probe>(
214215
} = &event;
215216
let category = format!("{}s", P::PMU);
216217
let tpid = read_sys_fs_trace_point_id(tracefs, &category, event_alias.as_ref())?;
217-
let perf_fd = perf_event_open_trace_point(tpid, pid).map_err(|io_error| SyscallError {
218+
let perf_fd = perf_event_open_trace_point(tpid, scope).map_err(|io_error| SyscallError {
218219
call: "perf_event_open",
219220
io_error,
220221
})?;

aya/src/programs/trace_point.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::{
1313
ProgramData, ProgramError, ProgramType, define_link_wrapper, impl_try_from_fdlink,
1414
impl_try_into_fdlink, load_program_without_attach_type,
1515
perf_attach::{PerfLinkIdInner, PerfLinkInner, perf_attach},
16+
perf_event::PerfEventScope,
1617
utils::find_tracefs_path,
1718
},
1819
sys::{SyscallError, perf_event_open_trace_point},
@@ -80,7 +81,13 @@ impl TracePoint {
8081
let prog_fd = prog_fd.as_fd();
8182
let tracefs = find_tracefs_path()?;
8283
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+
let perf_fd = perf_event_open_trace_point(
85+
id,
86+
// For all-processes attachment, perf_event_open requires an explicit
87+
// CPU. Use CPU 0 only to open the backing perf event.
88+
PerfEventScope::AllProcessesOneCpu { cpu: 0 },
89+
)
90+
.map_err(|io_error| SyscallError {
8491
call: "perf_event_open_trace_point",
8592
io_error,
8693
})?;

aya/src/programs/uprobe.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use crate::{
2222
ProgramData, ProgramError, ProgramType, define_link_wrapper, impl_try_from_fdlink,
2323
impl_try_into_fdlink, load_program_without_attach_type,
2424
perf_attach::{PerfLinkIdInner, PerfLinkInner},
25+
perf_event::PerfEventScope,
2526
probe::{OsStringExt as _, Probe, ProbeKind, attach},
2627
},
2728
util::MMap,
@@ -144,15 +145,21 @@ impl UProbe {
144145
) -> Result<UProbeLinkId, ProgramError> {
145146
let UProbeAttachPoint { location, cookie } = point.into();
146147
let target = target.as_ref();
147-
let (proc_map_pid, perf_event_pid) = match scope {
148-
UProbeScope::AllProcesses => (None, None),
148+
let (proc_map_pid, perf_event_scope) = match scope {
149+
// For an all-processes uprobe, perf_event_open uses pid=-1 and
150+
// requires an explicit CPU. Use CPU 0 only to open the backing
151+
// perf event.
152+
UProbeScope::AllProcesses => (None, PerfEventScope::AllProcessesOneCpu { cpu: 0 }),
149153
// /proc/0/maps does not exist, so use the real pid for ProcMap
150154
// resolution while keeping the kernel's pid=0 sentinel for attach.
151-
UProbeScope::CallingProcess => (Some(std::process::id()), Some(0)),
152-
UProbeScope::OneProcess(pid) => {
153-
let pid = pid.get();
154-
(Some(pid), Some(pid))
155-
}
155+
UProbeScope::CallingProcess => (
156+
Some(std::process::id()),
157+
PerfEventScope::CallingProcess { cpu: None },
158+
),
159+
UProbeScope::OneProcess(pid) => (
160+
Some(pid.get()),
161+
PerfEventScope::OneProcess { pid, cpu: None },
162+
),
156163
};
157164

158165
// Keep ProcMap in this scope so resolve_attach_target_basename can return
@@ -187,7 +194,7 @@ impl UProbe {
187194

188195
let Self { data, kind } = self;
189196
let path = path.as_os_str();
190-
attach::<Self, _>(data, *kind, path, offset, perf_event_pid, cookie)
197+
attach::<Self, _>(data, *kind, path, offset, perf_event_scope, cookie)
191198
}
192199

193200
/// Creates a program from a pinned entry on a bpffs.

aya/src/sys/perf_event.rs

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use std::{
22
ffi::{CString, OsStr, c_long, c_uint},
33
io, mem,
4-
num::NonZeroU32,
54
os::fd::{BorrowedFd, FromRawFd as _},
65
};
76

@@ -124,13 +123,7 @@ pub(crate) fn perf_event_open(
124123
}
125124
}
126125

127-
let (pid, cpu) = match scope {
128-
PerfEventScope::CallingProcess { cpu } => (0, cpu.map_or(-1, |cpu| cpu as i32)),
129-
PerfEventScope::OneProcess { pid, cpu } => {
130-
(pid.get() as i32, cpu.map_or(-1, |cpu| cpu as i32))
131-
}
132-
PerfEventScope::AllProcessesOneCpu { cpu } => (-1, cpu as i32),
133-
};
126+
let (pid, cpu) = perf_event_scope_pid_cpu(scope);
134127

135128
perf_event_sys(attr, pid, cpu, flags)
136129
}
@@ -140,7 +133,7 @@ pub(crate) fn perf_event_open_probe(
140133
ret_bit: Option<u32>,
141134
name: &OsStr,
142135
offset: u64,
143-
pid: Option<u32>,
136+
scope: PerfEventScope,
144137
) -> io::Result<crate::MockableFd> {
145138
use std::os::unix::ffi::OsStrExt as _;
146139

@@ -157,23 +150,15 @@ pub(crate) fn perf_event_open_probe(
157150
attr.__bindgen_anon_3.config1 = c_name.as_ptr() as u64;
158151
attr.__bindgen_anon_4.config2 = offset;
159152

160-
let (pid, cpu) = match pid {
161-
Some(pid) => (pid as i32, -1),
162-
None => (-1, 0),
163-
};
153+
let (pid, cpu) = perf_event_scope_pid_cpu(scope);
164154

165155
perf_event_sys(attr, pid, cpu, PERF_FLAG_FD_CLOEXEC)
166156
}
167157

168158
pub(crate) fn perf_event_open_trace_point(
169159
event_id: u64,
170-
pid: Option<u32>,
160+
scope: PerfEventScope,
171161
) -> io::Result<crate::MockableFd> {
172-
let scope = match pid.map(NonZeroU32::new) {
173-
None => PerfEventScope::AllProcessesOneCpu { cpu: 0 },
174-
Some(None) => PerfEventScope::CallingProcess { cpu: None },
175-
Some(Some(pid)) => PerfEventScope::OneProcess { pid, cpu: None },
176-
};
177162
perf_event_open(
178163
PerfEventConfig::TracePoint { event_id },
179164
scope,
@@ -184,6 +169,16 @@ pub(crate) fn perf_event_open_trace_point(
184169
)
185170
}
186171

172+
fn perf_event_scope_pid_cpu(scope: PerfEventScope) -> (pid_t, i32) {
173+
match scope {
174+
PerfEventScope::CallingProcess { cpu } => (0, cpu.map_or(-1, |cpu| cpu as i32)),
175+
PerfEventScope::OneProcess { pid, cpu } => {
176+
(pid.get() as i32, cpu.map_or(-1, |cpu| cpu as i32))
177+
}
178+
PerfEventScope::AllProcessesOneCpu { cpu } => (-1, cpu as i32),
179+
}
180+
}
181+
187182
pub(crate) fn perf_event_ioctl(
188183
fd: BorrowedFd<'_>,
189184
request: PerfEventIoctlRequest<'_>,
@@ -260,21 +255,42 @@ impl TryFrom<u32> for perf_event_type {
260255

261256
#[cfg(test)]
262257
mod tests {
263-
use std::os::fd::AsRawFd as _;
258+
use std::{num::NonZeroU32, os::fd::AsRawFd as _};
264259

265260
use libc::pid_t;
266261
use test_case::test_case;
267262

268263
use super::{PERF_FLAG_FD_CLOEXEC, perf_event_open_trace_point};
269-
use crate::sys::{Syscall, override_syscall};
264+
use crate::{
265+
programs::perf_event::PerfEventScope,
266+
sys::{Syscall, override_syscall},
267+
};
270268

271269
const EVENT_ID: u64 = 123;
272270

273-
#[test_case(None, -1, 0; "all_processes")]
274-
#[test_case(Some(0), 0, -1; "calling_process")]
275-
#[test_case(Some(42), 42, -1; "one_process")]
276-
fn perf_event_open_trace_point_maps_pid_scope(
277-
pid: Option<u32>,
271+
#[test_case(
272+
PerfEventScope::AllProcessesOneCpu { cpu: 0 },
273+
-1,
274+
0;
275+
"all_processes"
276+
)]
277+
#[test_case(
278+
PerfEventScope::CallingProcess { cpu: None },
279+
0,
280+
-1;
281+
"calling_process"
282+
)]
283+
#[test_case(
284+
PerfEventScope::OneProcess {
285+
pid: NonZeroU32::new(42).unwrap(),
286+
cpu: None,
287+
},
288+
42,
289+
-1;
290+
"one_process"
291+
)]
292+
fn perf_event_open_trace_point_maps_scope(
293+
scope: PerfEventScope,
278294
expected_pid: pid_t,
279295
expected_cpu: i32,
280296
) {
@@ -297,7 +313,7 @@ mod tests {
297313
call => panic!("unexpected syscall: {call:?}"),
298314
});
299315

300-
let fd = perf_event_open_trace_point(EVENT_ID, pid).unwrap();
316+
let fd = perf_event_open_trace_point(EVENT_ID, scope).unwrap();
301317
assert_eq!(fd.as_raw_fd(), crate::MockableFd::mock_signed_fd());
302318
}
303319
}

0 commit comments

Comments
 (0)