Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions components/axcpu/src/aarch64/pmu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,52 @@ pub fn hw_event_to_arm(hw_id: u32) -> Option<u16> {
}
}

/// Maps a `PERF_TYPE_HW_CACHE` config to an ARM PMUv3 event number, or `None`
/// for a combination ARM PMUv3 does not define (matching Linux's
/// `CACHE_OP_UNSUPPORTED`, which rejects the event at open).
///
/// The config packs `cache_id | (op << 8) | (result << 16)`: `cache_id` is
/// L1D(0)/L1I(1)/LL(2)/DTLB(3)/ITLB(4)/BPU(5)/NODE(6), `op` is READ(0)/WRITE(1)/
/// PREFETCH(2), `result` is ACCESS(0)/MISS(1). ARM's basic cache events do not
/// split read vs write, so both map to the same event; PREFETCH and NODE have no
/// architectural counterpart and are rejected.
pub fn hw_cache_to_arm(config: u64) -> Option<u16> {
const READ: u8 = 0;
const WRITE: u8 = 1;
const ACCESS: u8 = 0;
const MISS: u8 = 1;

let cache_id = (config & 0xff) as u8;
let op = ((config >> 8) & 0xff) as u8;
let result = ((config >> 16) & 0xff) as u8;

Some(match (cache_id, op, result) {
// L1D: L1D_CACHE / L1D_CACHE_REFILL.
(0, READ | WRITE, ACCESS) => 0x04,
(0, READ | WRITE, MISS) => 0x03,
// L1I: L1I_CACHE / L1I_CACHE_REFILL.
(1, READ, ACCESS) => 0x14,
(1, READ, MISS) => 0x01,
// LL (last level): LL_CACHE_RD / LL_CACHE_MISS_RD for reads, else
// LL_CACHE / LL_CACHE_MISS.
(2, READ, ACCESS) => 0x36,
(2, READ, MISS) => 0x37,
(2, WRITE, ACCESS) => 0x32,
(2, WRITE, MISS) => 0x33,
// DTLB: L1D_TLB / L1D_TLB_REFILL.
(3, READ | WRITE, ACCESS) => 0x25,
(3, READ | WRITE, MISS) => 0x05,
// ITLB: L1I_TLB / L1I_TLB_REFILL.
(4, READ, ACCESS) => 0x26,
(4, READ, MISS) => 0x02,
// BPU (branch prediction): BR_PRED / BR_MIS_PRED.
(5, READ | WRITE, ACCESS) => 0x12,
(5, READ | WRITE, MISS) => 0x10,
// PREFETCH ops, NODE cache, and every other combination are unsupported.
_ => return None,
})
}

/// The generic programmable event counters (`PMEVCNTRn_EL0` / `PMEVTYPERn_EL0`).
///
/// `n` is the logical counter index in `0..num_counters` (from
Expand Down
4 changes: 4 additions & 0 deletions os/StarryOS/kernel/src/mm/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,10 @@ fn handle_page_fault(vaddr: VirtAddr, access_flags: MappingFlags) -> bool {
return false;
};

// Count this fault for any `PERF_COUNT_SW_PAGE_FAULTS` event on the thread
// (cheap no-op when none exists).
crate::perf::sw::on_page_fault(thr);

if unlikely(!thr.is_accessing_user_memory()) {
// Still try to handle kernel-mode faults on user-space addresses.
// Several syscall sites (e.g. event.rs, net/io.rs, fs/lock.rs) obtain
Expand Down
13 changes: 13 additions & 0 deletions os/StarryOS/kernel/src/perf/hw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ fn resolve_hw_event(hw_id: u32) -> Option<u16> {
fn decode_arm_event(type_: u32, config: u64) -> Option<u16> {
if type_ == perf_type_id::PERF_TYPE_HARDWARE as u32 {
resolve_hw_event(config as u32)
} else if type_ == perf_type_id::PERF_TYPE_HW_CACHE as u32 {
ax_cpu::pmu::hw_cache_to_arm(config)
} else if type_ == perf_type_id::PERF_TYPE_RAW as u32
|| type_ == ARMV8_PMUV3_PERF_TYPE
|| type_ == ARMV8_CORTEX_A55_TYPE
Expand Down Expand Up @@ -1413,6 +1415,17 @@ pub fn perf_event_open_hw(attr: &perf_event_attr, pid: i32, cpu: i32) -> AxResul
};
alloc_programmable(event, exclude_user, exclude_kernel)?
}
} else if attr.type_ == perf_type_id::PERF_TYPE_HW_CACHE as u32 {
// Combinatorial cache events (`perf stat -e L1-dcache-load-misses` etc.):
// decode `config` (cache_id | op<<8 | result<<16) to an ARM PMUv3 event.
let Some(event) = ax_cpu::pmu::hw_cache_to_arm(attr.config) else {
warn!(
"perf_event_open: unsupported HW_CACHE config {:#x}",
attr.config
);
return Err(AxError::Unsupported);
};
alloc_programmable(event, exclude_user, exclude_kernel)?
} else if attr.type_ == perf_type_id::PERF_TYPE_RAW as u32
|| attr.type_ == ARMV8_PMUV3_PERF_TYPE
|| attr.type_ == ARMV8_CORTEX_A55_TYPE
Expand Down
19 changes: 17 additions & 2 deletions os/StarryOS/kernel/src/perf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ pub mod sampling;
/// gated like `sampling`.
#[cfg(target_arch = "aarch64")]
pub mod sideband;
/// Software events (`PERF_TYPE_SOFTWARE`) as real per-task counters — the default
/// `perf stat` rows (cpu-clock / task-clock / context-switches / cpu-migrations /
/// page-faults). Pure accounting driven by the scheduler + fault hooks, no PMU,
/// so it is arch-independent.
pub mod sw;
/// Per-task hardware-PMU counting (`perf stat -- cmd`, M3). ARM PMUv3 only; the
/// scheduler hooks call into CPU PMU register helpers, so it is gated like
/// `sampling`.
Expand Down Expand Up @@ -64,7 +69,7 @@ pub use bpf::BpfPerfEventWrapper;
use hashbrown::HashMap;
use kbpf_basic::{
linux_bpf::{PERF_FLAG_FD_CLOEXEC, perf_event_attr},
perf::{PerfEventIoc, PerfProbeArgs, PerfTypeId},
perf::{PerfEventIoc, PerfProbeArgs, PerfProbeConfig, PerfTypeId},
};

use crate::{
Expand Down Expand Up @@ -509,6 +514,7 @@ pub fn perf_event_open(
// `PerfProbeArgs::try_from_perf_attr`, which maps any non-probe type through
// `perf_sw_ids` and rejects hardware configs with `EINVAL`.
let event: Box<dyn PerfEventOps> = if attr.type_ == PerfTypeId::PERF_TYPE_HARDWARE as u32
|| attr.type_ == PerfTypeId::PERF_TYPE_HW_CACHE as u32
|| attr.type_ == PerfTypeId::PERF_TYPE_RAW as u32
|| attr.type_ == hw::ARMV8_PMUV3_PERF_TYPE
|| attr.type_ == hw::ARMV8_CORTEX_A55_TYPE
Expand All @@ -527,7 +533,16 @@ pub fn perf_event_open(
.into_ax_result()?;
match args.type_ {
PerfTypeId::PERF_TYPE_KPROBE => Box::new(kprobe::perf_event_open_kprobe(args)?),
PerfTypeId::PERF_TYPE_SOFTWARE => Box::new(bpf::perf_event_open_bpf(args)),
// The five counting software events (`perf stat`'s default rows) become
// real per-task counters; every other software config (e.g.
// `PERF_COUNT_SW_DUMMY`, `perf record`'s tracking event) keeps the
// BPF/ring path.
PerfTypeId::PERF_TYPE_SOFTWARE => match &args.config {
PerfProbeConfig::PerfSwIds(sw_id) if sw::is_counting_sw(*sw_id) => {
Box::new(sw::perf_event_open_sw(attr, *sw_id, pid)?)
}
_ => Box::new(bpf::perf_event_open_bpf(args)),
},
PerfTypeId::PERF_TYPE_TRACEPOINT => {
Box::new(tracepoint::perf_event_open_tracepoint(args)?)
}
Expand Down
Loading
Loading