// SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2024 Dennis Kobert //! Performance Events Module //! //! This module contains event types for hardware and software performance counters. //! C header: [`include/linux/perf_event.h`](srctree/include/linux/perf_event.h) use crate::bindings; /// Main event type categories #[derive(Debug, Clone, Copy)] pub enum EventType { /// Hardware events like CPU cycles, instructions, cache misses Hardware(HardwareEvent), /// Software events like context switches, page faults Software(SoftwareEvent), /// Hardware cache events (L1D, L1I, LLC, etc.) HwCache(HwCacheEvent), /// Raw hardware-specific event configuration Raw(u64), } /// Hardware performance events that can be monitored #[derive(Debug, Clone, Copy)] pub enum HardwareEvent { /// Total CPU cycles (both used and idle) /// Does not include cycles when the CPU is idle CpuCycles, /// Total instructions executed by the CPU /// Can be used with CpuCycles to calculate Instructions per Cycle (IPC) Instructions, /// Cache operations that reference the CPU's cache hierarchy /// Includes all cache levels (L1, L2, LLC) CacheReferences, /// Cache operations that miss the CPU's cache hierarchy /// Requires memory access from RAM or other CPUs CacheMisses, /// Total branch instructions executed /// Used to monitor program flow changes BranchInstructions, /// Branch instructions that were mispredicted /// Indicates branch prediction efficiency BranchMisses, /// Bus cycles, indicating memory/system bus activity /// Useful for monitoring memory bus utilization BusCycles, /// Cycles where the CPU front-end is stalled /// Indicates instruction fetch or decode bottlenecks StalledCyclesFrontend, /// Cycles where the CPU back-end is stalled /// Indicates execution bottlenecks like resource conflicts StalledCyclesBackend, /// Total CPU cycles, including idle cycles /// Counts at a constant rate regardless of CPU frequency changes RefCpuCycles, } /// Software performance events that can be monitored #[derive(Debug, Clone, Copy)] pub enum SoftwareEvent { /// CPU clock, a high-resolution per-CPU timer /// Measures time spent on this CPU in nanoseconds CpuClock, /// Task clock, a high-resolution timer specific to the monitored task /// Measures time spent by this task on CPU in nanoseconds TaskClock, /// Total page faults (both minor and major) /// Triggered when a process accesses a memory page not currently mapped PageFaults, /// Process context switches /// Counts voluntary and involuntary context switches ContextSwitches, /// CPU migrations /// Counts when a process moves execution to a different CPU CpuMigrations, /// Minor page faults /// Page is in memory but not allocated to the process PageFaultsMin, /// Major page faults /// Page needs to be loaded from disk PageFaultsMaj, /// Memory alignment faults /// Occurs on unaligned memory accesses when they're not handled by hardware AlignmentFaults, /// Instruction emulation faults /// Occurs when the CPU needs to emulate an instruction in software EmulationFaults, /// Dummy software event /// Used for testing or placeholder purposes Dummy, /// BPF program output /// Counts output events from eBPF programs BpfOutput, /// CGroup switches /// Counts transitions between cgroups CGroupSwitches, } /// Hardware cache performance events that can be monitored #[derive(Debug, Clone, Copy)] pub enum HwCacheEvent { /// Level 1 data cache events L1D(HwCacheOp, HwCacheOpResult), /// Level 1 instruction cache events L1I(HwCacheOp, HwCacheOpResult), /// Last level cache events LLC(HwCacheOp, HwCacheOpResult), /// Data TLB events DTLB(HwCacheOp, HwCacheOpResult), /// Instruction TLB events ITLB(HwCacheOp, HwCacheOpResult), /// Branch prediction unit events BPU(HwCacheOp, HwCacheOpResult), /// NUMA node cache events Node(HwCacheOp, HwCacheOpResult), } /// Hardware cache operations to monitor #[derive(Debug, Clone, Copy)] pub enum HwCacheOp { /// Cache read operations Read, /// Cache write operations Write, /// Cache prefetch operations Prefetch, } /// Hardware cache operation result types #[derive(Debug, Clone, Copy)] pub enum HwCacheOpResult { /// Cache access (hit or miss) Access, /// Cache miss Miss, } /// Trait for configuring perf_event_attr based on event type pub trait EventConfig { /// Test fn configure_attr(&self, attr: &mut bindings::perf_event_attr); } impl EventConfig for EventType { fn configure_attr(&self, attr: &mut bindings::perf_event_attr) { match self { EventType::Hardware(hw) => hw.configure_attr(attr), EventType::Software(sw) => sw.configure_attr(attr), EventType::HwCache(cache) => cache.configure_attr(attr), EventType::Raw(raw) => { attr.type_ = bindings::perf_type_id_PERF_TYPE_RAW as u32; attr.config = *raw; } } } } impl EventConfig for HardwareEvent { fn configure_attr(&self, attr: &mut bindings::perf_event_attr) { attr.type_ = bindings::perf_type_id_PERF_TYPE_HARDWARE as u32; attr.config = (*self).into(); } } impl EventConfig for SoftwareEvent { fn configure_attr(&self, attr: &mut bindings::perf_event_attr) { attr.type_ = bindings::perf_type_id_PERF_TYPE_SOFTWARE as u32; attr.config = (*self).into(); } } impl EventConfig for HwCacheEvent { fn configure_attr(&self, attr: &mut bindings::perf_event_attr) { attr.type_ = bindings::perf_type_id_PERF_TYPE_HW_CACHE as u32; attr.config = (*self).into(); } } // Raw value conversions impl From for u64 { fn from(event: HardwareEvent) -> Self { use HardwareEvent::*; match event { CpuCycles => bindings::perf_hw_id_PERF_COUNT_HW_CPU_CYCLES as u64, Instructions => bindings::perf_hw_id_PERF_COUNT_HW_INSTRUCTIONS as u64, CacheReferences => bindings::perf_hw_id_PERF_COUNT_HW_CACHE_REFERENCES as u64, CacheMisses => bindings::perf_hw_id_PERF_COUNT_HW_CACHE_MISSES as u64, BranchInstructions => bindings::perf_hw_id_PERF_COUNT_HW_BRANCH_INSTRUCTIONS as u64, BranchMisses => bindings::perf_hw_id_PERF_COUNT_HW_BRANCH_MISSES as u64, BusCycles => bindings::perf_hw_id_PERF_COUNT_HW_BUS_CYCLES as u64, StalledCyclesFrontend => { bindings::perf_hw_id_PERF_COUNT_HW_STALLED_CYCLES_FRONTEND as u64 } StalledCyclesBackend => { bindings::perf_hw_id_PERF_COUNT_HW_STALLED_CYCLES_BACKEND as u64 } RefCpuCycles => bindings::perf_hw_id_PERF_COUNT_HW_REF_CPU_CYCLES as u64, } } } impl From for u64 { fn from(event: SoftwareEvent) -> Self { use SoftwareEvent::*; match event { CpuClock => bindings::perf_sw_ids_PERF_COUNT_SW_CPU_CLOCK as u64, TaskClock => bindings::perf_sw_ids_PERF_COUNT_SW_TASK_CLOCK as u64, PageFaults => bindings::perf_sw_ids_PERF_COUNT_SW_PAGE_FAULTS as u64, ContextSwitches => bindings::perf_sw_ids_PERF_COUNT_SW_CONTEXT_SWITCHES as u64, CpuMigrations => bindings::perf_sw_ids_PERF_COUNT_SW_CPU_MIGRATIONS as u64, PageFaultsMin => bindings::perf_sw_ids_PERF_COUNT_SW_PAGE_FAULTS_MIN as u64, PageFaultsMaj => bindings::perf_sw_ids_PERF_COUNT_SW_PAGE_FAULTS_MAJ as u64, AlignmentFaults => bindings::perf_sw_ids_PERF_COUNT_SW_ALIGNMENT_FAULTS as u64, EmulationFaults => bindings::perf_sw_ids_PERF_COUNT_SW_EMULATION_FAULTS as u64, Dummy => bindings::perf_sw_ids_PERF_COUNT_SW_DUMMY as u64, BpfOutput => bindings::perf_sw_ids_PERF_COUNT_SW_BPF_OUTPUT as u64, CGroupSwitches => bindings::perf_sw_ids_PERF_COUNT_SW_CGROUP_SWITCHES as u64, } } } impl From for u64 { fn from(event: HwCacheEvent) -> Self { use HwCacheEvent::*; let (cache_id, op, result) = match event { L1D(op, result) => ( bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_L1D, op, result, ), L1I(op, result) => ( bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_L1I, op, result, ), LLC(op, result) => ( bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_LL, op, result, ), DTLB(op, result) => ( bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_DTLB, op, result, ), ITLB(op, result) => ( bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_ITLB, op, result, ), BPU(op, result) => ( bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_BPU, op, result, ), Node(op, result) => ( bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_NODE, op, result, ), }; let op_id: u32 = match op { HwCacheOp::Read => bindings::perf_hw_cache_op_id_PERF_COUNT_HW_CACHE_OP_READ, HwCacheOp::Write => bindings::perf_hw_cache_op_id_PERF_COUNT_HW_CACHE_OP_WRITE, HwCacheOp::Prefetch => bindings::perf_hw_cache_op_id_PERF_COUNT_HW_CACHE_OP_PREFETCH, }; let result_id: u32 = match result { HwCacheOpResult::Access => { bindings::perf_hw_cache_op_result_id_PERF_COUNT_HW_CACHE_RESULT_ACCESS } HwCacheOpResult::Miss => { bindings::perf_hw_cache_op_result_id_PERF_COUNT_HW_CACHE_RESULT_MISS } }; // Encode the cache event ID according to the kernel's encoding scheme: // - cache_id << 0 // - op_id << 8 // - result_id << 16 (cache_id as u64) | ((op_id as u64) << 8) | ((result_id as u64) << 16) } } // Implement From for EventType impl From for EventType { fn from(event: HardwareEvent) -> Self { EventType::Hardware(event) } } impl From for EventType { fn from(event: SoftwareEvent) -> Self { EventType::Software(event) } } impl From for EventType { fn from(event: HwCacheEvent) -> Self { EventType::HwCache(event) } }