profile-bee-aya 0.13.2

An eBPF library with a focus on developer experience and operability. Fork of aya for profile-bee.
Documentation
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
//! Perf event programs.

use std::os::fd::AsFd as _;

use aya_obj::generated::{
    HW_BREAKPOINT_LEN_1, HW_BREAKPOINT_LEN_2, HW_BREAKPOINT_LEN_4, HW_BREAKPOINT_LEN_8,
    HW_BREAKPOINT_R, HW_BREAKPOINT_RW, HW_BREAKPOINT_W, bpf_link_type,
    bpf_prog_type::BPF_PROG_TYPE_PERF_EVENT, perf_hw_cache_id, perf_hw_cache_op_id,
    perf_hw_cache_op_result_id, perf_hw_id, perf_sw_ids, perf_type_id,
};

use crate::{
    programs::{
        FdLink, LinkError, ProgramData, ProgramError, ProgramType, impl_try_into_fdlink,
        links::define_link_wrapper,
        load_program,
        perf_attach::{PerfLinkIdInner, PerfLinkInner, perf_attach},
    },
    sys::{SyscallError, bpf_link_get_info_by_fd, perf_event_open},
};

/// The type of perf event and their respective configuration.
#[doc(alias = "perf_type_id")]
#[derive(Debug, Clone, Copy)]
pub enum PerfEventConfig {
    /// The hardware event to report.
    #[doc(alias = "PERF_TYPE_HARDWARE")]
    Hardware(HardwareEvent),
    /// The software event to report.
    #[doc(alias = "PERF_TYPE_SOFTWARE")]
    Software(SoftwareEvent),
    /// The kernel trace point event to report.
    #[doc(alias = "PERF_TYPE_TRACEPOINT")]
    TracePoint {
        /// The ID of the tracing event. This can be obtained from
        /// `/sys/kernel/debug/tracing/events/*/*/id` if `ftrace` is enabled in the kernel.
        event_id: u64,
    },
    /// The hardware cache event to report.
    #[doc(alias = "PERF_TYPE_HW_CACHE")]
    HwCache {
        /// The hardware cache event.
        event: HwCacheEvent,
        /// The hardware cache operation.
        operation: HwCacheOp,
        /// The hardware cache result of interest.
        result: HwCacheResult,
    },
    /// The "raw" implementation-specific event to report.
    #[doc(alias = "PERF_TYPE_RAW")]
    Raw {
        /// The "raw" event value, which is not covered by the "generalized" events. This is CPU
        /// implementation defined events.
        event_id: u64,
    },
    /// A hardware breakpoint.
    #[doc(alias = "PERF_TYPE_BREAKPOINT")]
    Breakpoint(BreakpointConfig),
    /// The dynamic PMU (Performance Monitor Unit) event to report.
    ///
    /// Available PMU's may be found under `/sys/bus/event_source/devices`.
    Pmu {
        /// The PMU type.
        ///
        /// This value can extracted from `/sys/bus/event_source/devices/*/type`.
        pmu_type: u32,
        /// The PMU config option.
        ///
        /// This value can extracted from `/sys/bus/event_source/devices/*/format/`, where the
        /// `config:<value>` indicates the bit position to set.
        ///
        /// For example, `config:3` => `config = 1 << 3`.
        config: u64,
    },
}

macro_rules! impl_to_u32 {
    ($($t:ty, $fn:ident),*) => {
        $(pub(crate) const fn $fn(id: $t) -> u32 {
            const _: [(); 4] = [(); std::mem::size_of::<$t>()];
            id as u32
        })*
    };
}

impl_to_u32!(
    perf_hw_id,
    perf_hw_id_to_u32,
    perf_sw_ids,
    perf_sw_ids_to_u32,
    perf_hw_cache_id,
    perf_hw_cache_id_to_u32,
    perf_hw_cache_op_id,
    perf_hw_cache_op_id_to_u32,
    perf_hw_cache_op_result_id,
    perf_hw_cache_op_result_id_to_u32,
    perf_type_id,
    perf_type_id_to_u32
);

/// The "generalized" hardware CPU events provided by the kernel.
#[doc(alias = "perf_hw_id")]
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum HardwareEvent {
    /// The total CPU cycles.
    #[doc(alias = "PERF_COUNT_HW_CPU_CYCLES")]
    CpuCycles = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_CPU_CYCLES),
    /// Number of retired instructions.
    #[doc(alias = "PERF_COUNT_HW_INSTRUCTIONS")]
    Instructions = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_INSTRUCTIONS),
    /// Number of cache accesses.
    #[doc(alias = "PERF_COUNT_HW_CACHE_REFERENCES")]
    CacheReferences = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_CACHE_REFERENCES),
    /// Number of cache misses.
    #[doc(alias = "PERF_COUNT_HW_CACHE_MISSES")]
    CacheMisses = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_CACHE_MISSES),
    /// Number of retired branch instructions.
    #[doc(alias = "PERF_COUNT_HW_BRANCH_INSTRUCTIONS")]
    BranchInstructions = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_BRANCH_INSTRUCTIONS),
    /// Number of mispredicted branch instructions.
    #[doc(alias = "PERF_COUNT_HW_BRANCH_MISSES")]
    BranchMisses = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_BRANCH_MISSES),
    /// Number of bus cycles.
    #[doc(alias = "PERF_COUNT_HW_BUS_CYCLES")]
    BusCycles = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_BUS_CYCLES),
    /// Number of stalled cycles during issue.
    #[doc(alias = "PERF_COUNT_HW_STALLED_CYCLES_FRONTEND")]
    StalledCyclesFrontend = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_STALLED_CYCLES_FRONTEND),
    /// Number of stalled cycles during retirement.
    #[doc(alias = "PERF_COUNT_HW_STALLED_CYCLES_BACKEND")]
    StalledCyclesBackend = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_STALLED_CYCLES_BACKEND),
    /// The total CPU cycles, which is not affected by CPU frequency scaling.
    #[doc(alias = "PERF_COUNT_HW_REF_CPU_CYCLES")]
    RefCpuCycles = perf_hw_id_to_u32(perf_hw_id::PERF_COUNT_HW_REF_CPU_CYCLES),
}

impl HardwareEvent {
    pub(crate) const fn into_primitive(self) -> u32 {
        const _: [(); 4] = [(); size_of::<HardwareEvent>()];
        self as u32
    }
}

/// The software-defined events provided by the kernel.
#[doc(alias = "perf_sw_ids")]
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum SoftwareEvent {
    /// The CPU clock timer.
    #[doc(alias = "PERF_COUNT_SW_CPU_CLOCK")]
    CpuClock = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_CPU_CLOCK),
    /// The clock count specific to the task that is running.
    #[doc(alias = "PERF_COUNT_SW_TASK_CLOCK")]
    TaskClock = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_TASK_CLOCK),
    /// Number of page faults.
    #[doc(alias = "PERF_COUNT_SW_PAGE_FAULTS")]
    PageFaults = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_PAGE_FAULTS),
    /// Numer of context switches.
    #[doc(alias = "PERF_COUNT_SW_CONTEXT_SWITCHES")]
    ContextSwitches = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_CONTEXT_SWITCHES),
    /// Number of times the process has migrated to a new CPU.
    #[doc(alias = "PERF_COUNT_SW_CPU_MIGRATIONS")]
    CpuMigrations = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_CPU_MIGRATIONS),
    /// Number of minor page faults.
    #[doc(alias = "PERF_COUNT_SW_PAGE_FAULTS_MIN")]
    PageFaultsMin = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_PAGE_FAULTS_MIN),
    /// Number of major page faults.
    #[doc(alias = "PERF_COUNT_SW_PAGE_FAULTS_MAJ")]
    PageFaultsMaj = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_PAGE_FAULTS_MAJ),
    /// Number of alignment faults.
    #[doc(alias = "PERF_COUNT_SW_ALIGNMENT_FAULTS")]
    AlignmentFaults = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_ALIGNMENT_FAULTS),
    /// Number of emulation faults.
    #[doc(alias = "PERF_COUNT_SW_EMULATION_FAULTS")]
    EmulationFaults = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_EMULATION_FAULTS),
    /// Placeholder event that counts nothing.
    #[doc(alias = "PERF_COUNT_SW_DUMMY")]
    Dummy = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_DUMMY),
    /// Generates raw sample data from BPF.
    #[doc(alias = "PERF_COUNT_SW_BPF_OUTPUT")]
    BpfOutput = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_BPF_OUTPUT),
    /// Number of context switches to a task when switching to a different cgroup.
    #[doc(alias = "PERF_COUNT_SW_CGROUP_SWITCHES")]
    CgroupSwitches = perf_sw_ids_to_u32(perf_sw_ids::PERF_COUNT_SW_CGROUP_SWITCHES),
}

impl SoftwareEvent {
    pub(crate) const fn into_primitive(self) -> u32 {
        const _: [(); 4] = [(); size_of::<SoftwareEvent>()];
        self as u32
    }
}

/// The hardware CPU cache events.
#[doc(alias = "perf_hw_cache_id")]
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum HwCacheEvent {
    /// Measures Level 1 data cache.
    #[doc(alias = "PERF_COUNT_HW_CACHE_L1D")]
    L1d = perf_hw_cache_id_to_u32(perf_hw_cache_id::PERF_COUNT_HW_CACHE_L1D),
    /// Measures Level 1 data cache.
    #[doc(alias = "PERF_COUNT_HW_CACHE_L1I")]
    L1i = perf_hw_cache_id_to_u32(perf_hw_cache_id::PERF_COUNT_HW_CACHE_L1I),
    /// Measures Last-level cache.
    #[doc(alias = "PERF_COUNT_HW_CACHE_LL")]
    Ll = perf_hw_cache_id_to_u32(perf_hw_cache_id::PERF_COUNT_HW_CACHE_LL),
    /// Measures Data TLB (Translation Lookaside Buffer).
    #[doc(alias = "PERF_COUNT_HW_CACHE_DTLB")]
    Dtlb = perf_hw_cache_id_to_u32(perf_hw_cache_id::PERF_COUNT_HW_CACHE_DTLB),
    /// Measures Instruction TLB (Translation Lookaside Buffer).
    #[doc(alias = "PERF_COUNT_HW_CACHE_ITLB")]
    Itlb = perf_hw_cache_id_to_u32(perf_hw_cache_id::PERF_COUNT_HW_CACHE_ITLB),
    /// Measures branch prediction.
    #[doc(alias = "PERF_COUNT_HW_CACHE_BPU")]
    Bpu = perf_hw_cache_id_to_u32(perf_hw_cache_id::PERF_COUNT_HW_CACHE_BPU),
    /// Measures local memory accesses.
    #[doc(alias = "PERF_COUNT_HW_CACHE_NODE")]
    Node = perf_hw_cache_id_to_u32(perf_hw_cache_id::PERF_COUNT_HW_CACHE_NODE),
}

impl HwCacheEvent {
    pub(crate) const fn into_primitive(self) -> u32 {
        const _: [(); 4] = [(); size_of::<HwCacheEvent>()];
        self as u32
    }
}

/// The hardware CPU cache operations.
#[doc(alias = "perf_hw_cache_op_id")]
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum HwCacheOp {
    /// Read access.
    #[doc(alias = "PERF_COUNT_HW_CACHE_OP_READ")]
    Read = perf_hw_cache_op_id_to_u32(perf_hw_cache_op_id::PERF_COUNT_HW_CACHE_OP_READ),
    /// Write access.
    #[doc(alias = "PERF_COUNT_HW_CACHE_OP_WRITE")]
    Write = perf_hw_cache_op_id_to_u32(perf_hw_cache_op_id::PERF_COUNT_HW_CACHE_OP_WRITE),
    /// Prefetch access.
    #[doc(alias = "PERF_COUNT_HW_CACHE_OP_PREFETCH")]
    Prefetch = perf_hw_cache_op_id_to_u32(perf_hw_cache_op_id::PERF_COUNT_HW_CACHE_OP_PREFETCH),
}

impl HwCacheOp {
    pub(crate) const fn into_primitive(self) -> u32 {
        const _: [(); 4] = [(); size_of::<HwCacheOp>()];
        self as u32
    }
}

/// The hardware CPU cache result.
#[doc(alias = "perf_hw_cache_op_result_id")]
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum HwCacheResult {
    /// Cache accesses.
    #[doc(alias = "PERF_COUNT_HW_CACHE_RESULT_ACCESS")]
    Access = perf_hw_cache_op_result_id_to_u32(
        perf_hw_cache_op_result_id::PERF_COUNT_HW_CACHE_RESULT_ACCESS,
    ),
    /// Cache missed accesses.
    #[doc(alias = "PERF_COUNT_HW_CACHE_RESULT_MISS")]
    Miss = perf_hw_cache_op_result_id_to_u32(
        perf_hw_cache_op_result_id::PERF_COUNT_HW_CACHE_RESULT_MISS,
    ),
}

impl HwCacheResult {
    pub(crate) const fn into_primitive(self) -> u32 {
        const _: [(); 4] = [(); size_of::<HwCacheResult>()];
        self as u32
    }
}

/// The breakpoint type.
#[repr(u32)]
#[derive(Debug, Clone, Copy)]
pub enum PerfBreakpointType {
    /// `HW_BREAKPOINT_R`, trigger when we read the memory location.
    #[doc(alias = "HW_BREAKPOINT_R")]
    Read = HW_BREAKPOINT_R,
    /// `HW_BREAKPOINT_W`, trigger when we write the memory location.
    #[doc(alias = "HW_BREAKPOINT_W")]
    Write = HW_BREAKPOINT_W,
    /// `HW_BREAKPOINT_RW`, trigger when we read or write the memory location.
    #[doc(alias = "HW_BREAKPOINT_RW")]
    ReadWrite = HW_BREAKPOINT_RW,
}

impl PerfBreakpointType {
    pub(crate) const fn into_primitive(self) -> u32 {
        const _: [(); 4] = [(); size_of::<PerfBreakpointType>()];
        self as u32
    }
}

/// The number of bytes covered by a data breakpoint.
#[repr(u32)]
#[derive(Debug, Clone, Copy)]
pub enum PerfBreakpointLength {
    /// `HW_BREAKPOINT_LEN_1`
    #[doc(alias = "HW_BREAKPOINT_LEN_1")]
    Len1 = HW_BREAKPOINT_LEN_1,
    /// `HW_BREAKPOINT_LEN_2`
    #[doc(alias = "HW_BREAKPOINT_LEN_2")]
    Len2 = HW_BREAKPOINT_LEN_2,
    /// `HW_BREAKPOINT_LEN_4`
    #[doc(alias = "HW_BREAKPOINT_LEN_4")]
    Len4 = HW_BREAKPOINT_LEN_4,
    /// `HW_BREAKPOINT_LEN_8`
    #[doc(alias = "HW_BREAKPOINT_LEN_8")]
    Len8 = HW_BREAKPOINT_LEN_8,
}

impl PerfBreakpointLength {
    pub(crate) const fn into_primitive(self) -> u32 {
        const _: [(); 4] = [(); size_of::<PerfBreakpointLength>()];
        self as u32
    }
}

/// Type of hardware breakpoint, determines if we break on read, write, or
/// execute, or if there should be no breakpoint on the given address.
#[derive(Debug, Clone, Copy)]
pub enum BreakpointConfig {
    /// A memory access breakpoint.
    Data {
        /// The type of the breakpoint.
        r#type: PerfBreakpointType,
        /// The address of the breakpoint.
        address: u64,
        /// The contiguous byte window to monitor starting at `address`.
        length: PerfBreakpointLength,
    },
    /// A code execution breakpoint.
    #[doc(alias = "HW_BREAKPOINT_X")]
    Instruction {
        /// The address of the breakpoint.
        address: u64,
    },
}

/// Sample Policy
#[derive(Debug, Clone, Copy)]
pub enum SamplePolicy {
    /// Period
    Period(u64),
    /// Frequency
    Frequency(u64),
}

/// Wakeup Policy
#[derive(Debug, Clone, Copy)]
pub(crate) enum WakeupPolicy {
    /// Every N events
    Events(u32),
    /// Every N bytes
    #[expect(dead_code, reason = "TODO")]
    Watermark(u32),
}

/// The scope of a [`PerfEvent`].
#[derive(Debug, Clone, Copy)]
pub enum PerfEventScope {
    /// calling process
    CallingProcess {
        /// cpu id or any cpu if None
        cpu: Option<u32>,
    },
    /// one process
    OneProcess {
        /// process id
        pid: u32,
        /// cpu id or any cpu if None
        cpu: Option<u32>,
    },
    /// all processes, one cpu
    AllProcessesOneCpu {
        /// cpu id
        cpu: u32,
    },
}

/// A program that can be attached at a perf event.
///
/// # Minimum kernel version
///
/// The minimum kernel version required to use this feature is 4.9.
///
/// # Examples
///
/// ```no_run
/// # #[derive(Debug, thiserror::Error)]
/// # enum Error {
/// #     #[error(transparent)]
/// #     IO(#[from] std::io::Error),
/// #     #[error(transparent)]
/// #     Map(#[from] aya::maps::MapError),
/// #     #[error(transparent)]
/// #     Program(#[from] aya::programs::ProgramError),
/// #     #[error(transparent)]
/// #     Ebpf(#[from] aya::EbpfError)
/// # }
/// use aya::{
///     util::online_cpus,
///     programs::perf_event::{
///         PerfEvent, PerfEventConfig, PerfEventScope, SamplePolicy, SoftwareEvent,
///     },
/// };
///
/// # let mut bpf = aya::Ebpf::load(&[])?;
/// let prog: &mut PerfEvent = bpf.program_mut("observe_cpu_clock").unwrap().try_into()?;
/// prog.load()?;
///
/// let perf_type = PerfEventConfig::Software(SoftwareEvent::CpuClock);
/// for cpu in online_cpus().map_err(|(_, error)| error)? {
///     prog.attach(
///         perf_type,
///         PerfEventScope::AllProcessesOneCpu { cpu },
///         SamplePolicy::Period(1000000),
///         true,
///     )?;
/// }
/// # Ok::<(), Error>(())
/// ```
#[derive(Debug)]
#[doc(alias = "BPF_PROG_TYPE_PERF_EVENT")]
pub struct PerfEvent {
    pub(crate) data: ProgramData<PerfEventLink>,
}

impl PerfEvent {
    /// The type of the program according to the kernel.
    pub const PROGRAM_TYPE: ProgramType = ProgramType::PerfEvent;

    /// Loads the program inside the kernel.
    pub fn load(&mut self) -> Result<(), ProgramError> {
        load_program(BPF_PROG_TYPE_PERF_EVENT, &mut self.data)
    }

    /// Attaches to the given perf event.
    ///
    /// If `inherit` is `true`, any new processes spawned by those processes
    /// will also automatically be sampled.
    ///
    /// The returned value can be used to detach, see [`Self::detach`].
    pub fn attach(
        &mut self,
        config: PerfEventConfig,
        scope: PerfEventScope,
        sample_policy: SamplePolicy,
        inherit: bool,
    ) -> Result<PerfEventLinkId, ProgramError> {
        let prog_fd = self.fd()?;
        let prog_fd = prog_fd.as_fd();

        let perf_fd = perf_event_open(
            config,
            scope,
            sample_policy,
            WakeupPolicy::Events(1),
            inherit,
            0,
        )
        .map_err(|io_error| SyscallError {
            call: "perf_event_open",
            io_error,
        })?;

        let link = perf_attach(prog_fd, perf_fd, None /* cookie */)?;
        self.data.links.insert(PerfEventLink::new(link))
    }
}

impl_try_into_fdlink!(PerfEventLink, PerfLinkInner);

impl TryFrom<FdLink> for PerfEventLink {
    type Error = LinkError;

    fn try_from(fd_link: FdLink) -> Result<Self, Self::Error> {
        let info = bpf_link_get_info_by_fd(fd_link.fd.as_fd())?;
        if info.type_ == (bpf_link_type::BPF_LINK_TYPE_PERF_EVENT as u32) {
            return Ok(Self::new(PerfLinkInner::Fd(fd_link)));
        }
        Err(LinkError::InvalidLink)
    }
}

define_link_wrapper!(
    PerfEventLink,
    PerfEventLinkId,
    PerfLinkInner,
    PerfLinkIdInner,
    PerfEvent,
);