1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! Typed ownership target for `perf_event_open(2)`.
pub(crate) use super::cpu_id::PerfCpuId;
/// Raw CPU selector retained until the target task has been resolved.
///
/// Linux resolves a positive TID before validating the optional CPU filter,
/// so this request intentionally defers range validation.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct PerfCpuRequest(i32);
impl PerfCpuRequest {
/// Creates an unresolved CPU selector from the syscall argument.
const fn new(value: i32) -> Self {
Self(value)
}
/// Resolves an optional task CPU filter.
pub(crate) fn resolve_optional(
self,
cpu_count: usize,
) -> Result<Option<PerfCpuId>, PerfTargetError> {
match self.0 {
-1 => Ok(None),
value if value >= 0 && (value as usize) < cpu_count => {
Ok(Some(PerfCpuId::new(value as usize)))
}
_ => Err(PerfTargetError::InvalidTuple),
}
}
/// Resolves a required system-wide CPU owner.
pub(crate) fn resolve_required(self, cpu_count: usize) -> Result<PerfCpuId, PerfTargetError> {
self.resolve_optional(cpu_count)?
.ok_or(PerfTargetError::InvalidTuple)
}
}
/// Linux error class produced while parsing a `pid`/`cpu` target tuple.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum PerfTargetError {
/// The tuple cannot identify a task or CPU context.
InvalidTuple,
/// A negative PID other than the `-1` CPU-context sentinel has no task.
NoSuchProcess,
}
/// Validated `perf_event_open(2)` flag set.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct PerfOpenFlags(u32);
impl PerfOpenFlags {
/// `PERF_FLAG_FD_NO_GROUP`.
pub(crate) const FD_NO_GROUP: u32 = 1 << 0;
/// `PERF_FLAG_FD_OUTPUT`.
pub(crate) const FD_OUTPUT: u32 = 1 << 1;
/// `PERF_FLAG_PID_CGROUP`.
pub(crate) const PID_CGROUP: u32 = 1 << 2;
/// `PERF_FLAG_FD_CLOEXEC`.
pub(crate) const FD_CLOEXEC: u32 = 1 << 3;
const ALL: u64 =
(Self::FD_NO_GROUP | Self::FD_OUTPUT | Self::PID_CGROUP | Self::FD_CLOEXEC) as u64;
/// Parses the complete syscall-width flag word.
pub(crate) const fn parse(flags: u64) -> Result<Self, PerfTargetError> {
if flags & !Self::ALL != 0 {
return Err(PerfTargetError::InvalidTuple);
}
Ok(Self(flags as u32))
}
/// Returns the validated Linux flag bits.
pub(crate) const fn bits(self) -> u32 {
self.0
}
/// Reports whether one validated flag is set.
pub(crate) const fn contains(self, flag: u32) -> bool {
self.0 & flag != 0
}
}
/// Task identity accepted by `perf_event_open(2)`.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum PerfTaskTarget {
/// The calling task (`pid == 0`).
Current,
/// One Linux thread id (`pid > 0`).
Tid(u32),
}
/// Runtime owner class used for target-specific event validation.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum PerfTargetKind {
/// A task scheduler context.
Task,
/// A fixed logical CPU context.
Cpu,
}
/// Scheduler or CPU context that owns one perf event.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum PerfTarget {
/// A task context with a deferred optional CPU filter.
Task {
task: PerfTaskTarget,
cpu: PerfCpuRequest,
},
/// A CPU context (`pid == -1`) with deferred CPU validation.
Cpu(PerfCpuRequest),
}
impl PerfTarget {
/// Parses target identity while deferring CPU validation.
///
/// Deferral preserves Linux's error precedence: a missing positive TID is
/// reported as `ESRCH` even when its CPU filter is also invalid.
pub(crate) fn parse(pid: i32, cpu: i32) -> Result<Self, PerfTargetError> {
if pid < -1 {
return Err(PerfTargetError::NoSuchProcess);
}
let cpu = PerfCpuRequest::new(cpu);
match pid {
-1 => Ok(Self::Cpu(cpu)),
0 => Ok(Self::Task {
task: PerfTaskTarget::Current,
cpu,
}),
value if value > 0 => Ok(Self::Task {
task: PerfTaskTarget::Tid(value as u32),
cpu,
}),
_ => unreachable!("negative task PIDs were rejected before CPU parsing"),
}
}
}