feat(starry): ARM PMUv3 hardware-PMU perf support (perf stat / record / report)#1395
Conversation
…follow (perf M3-pt)
…_SAMPLE (perf M3-pt-rec)
There was a problem hiding this comment.
PR 审核总结
变更概述
本 PR 为 StarryOS 补齐 ARM PMUv3 硬件 PMU perf 路径,使上游 perf 工具(stat / record / report)能在 aarch64 上运行。改动纯增量(39 文件,+8085/−29),原追踪 perf 路径不受影响。
实现逻辑
实现架构分层清晰:
- 硬件寄存器层(
axcpu/src/aarch64/pmu.rs):ARM PMUv3 完整封装,包含 PMCR/PMCCNTR/PMEVCNTRn/PMEVTYPERn 等全部原语,使用命名系统寄存器直接访问(不走 PMSELR 间接寻址),避免 IRQ 处理器竞争。 - hw.rs:counting(M1, perf stat)和 sampling(M2, perf record)事件,计数器分配器(
HwAlloc)跟踪专用周期计数器与可编程计数器的使用状态。 - sampling.rs:PMU 溢出 IRQ 处理器,每 CPU 注册表
[Option<SampleSlot>; 32]在NoPreemptIrqSave下读写,频率模式自适应控制(next_freq_period)使用 1/8 阻尼。ring_write在写入data_head前使用Release屏障。 - task.rs:per-task 事件通过
PerTaskCounter挂载在Thread.perf_counters,由perf_sched_in/perf_sched_out调度钩子驱动,热路径仅依赖原子操作和 sysreg 写入。 - sideband.rs:
PERF_RECORD_COMM/MMAP2/FORK/EXIT从进程上下文写入环,通过ring_write_process(mask 本地 IRQ)与溢出处理序列化。 - sysfs/procfs:PMU 设备枚举(
/sys/bus/event_source/devices/armv8_pmuv3_0/)、MIDR 解码的/proc/cpuinfo、perf knobs。
安全性分析
- IRQ 上下文纪律:溢出处理器不分配内存,不持锁,先读取中断上下文(ELR_EL1/SPSR_EL1),符合硬中断要求。
- 拆除顺序:mask IRQ → stop counter → unregister slot → drop Arc,防止 UAF。
Drop实现与disable共享teardown_sampling_irq(幂等)。 - 原始指针音效性:
SampleSlot::notify是*const IrqNotify,仅在注册的 slot 存活期间被解引用(unregister在Arc<IrqNotify>drop 之前执行),模块级文档明确论证了这一点。 - RAII 页面固定:ring 页面通过
Arc<GlobalPage>固定在 VMA retainer 中,Weak避免事件持有强引用导致的生命周期延长。 - 无
[patch.crates-io]:未引入任何 crates.io patch。
测试覆盖
12 个自包含 QEMU C 用例位于 test-suit/starryos/qemu-smp1/system/perf-hw-*/,覆盖:
- 计数(cycles、stat)、采样(sample、sample-task)、PMU sysfs 枚举
- ioctl 行为(record-ioctl)、频率模式(freq)、EL0 直读(rdpmc)
- 多事件环合并(ring-merge)、side-band 记录(sideband)
- fork/exit 生命周期(fork-exit)、事件继承(inherit)
测试不依赖 perf 二进制,可独立运行,打印 STARRY_PERF_HW_OK 成功哨兵。用例放置在正确的 test-suit 路径下。
CI 状态
CI 检查全部为 skipped(来自 fork PR 的典型行为,非 PR 引起的失败)。cargo fmt --check 本地通过。作者报告 clippy 45/45、aarch64 build、riscv64 交叉编译、nightly fmt 全部通过。
重复/重叠分析
检查了当前所有 open PR,无其他 PR 涉及 PMU/硬件 perf 功能。base 分支现有 perf 仅覆盖软件/追踪事件,本 PR 补齐硬件路径,纯增量无冲突。
已知限制(非 blocking)
- 仅单核;多核 + big.LITTLE 异构 PMU 为后续工作
attr.inherit每任务占一个可编程计数器槽(暂不时分复用)- 仅 aarch64;其他架构通过
#[cfg]门控不受影响
结论
实现质量高、文档详尽、安全论证完整、测试覆盖全面。无阻塞性问题。
Powered by mimo-v2.5-pro
There was a problem hiding this comment.
PR 审核总结
变更概述
本 PR 为 StarryOS 补齐 ARM PMUv3 硬件 PMU perf 路径,使上游 perf 工具(stat / record / report)能在 aarch64 上运行。改动纯增量(39 文件,+8093/−29),原追踪 perf 路径不受影响。
实现逻辑
实现架构分层清晰,各模块职责明确:
-
硬件寄存器层(
axcpu/src/aarch64/pmu.rs,621 行新增):ARM PMUv3 完整封装——PMCR/PMCCNTR/PMEVCNTRn/PMEVTYPERn/PMCNTENSET·CLR/PMINTENSET·CLR/PMOVSCLR/PMCEID/PMUSERENR/MIDR。使用命名系统寄存器直接访问(mrs/msr),避免 PMSELR 间接寻址的竞态,设计合理。 -
hw.rs(1195 行):counting(perf stat)和 sampling(perf record)事件的统一入口。
HwAlloc计数器分配器跟踪专用周期计数器与可编程计数器的使用状态。RingState使用Weak<GlobalPage>避免事件持有强引用导致页面生命周期过长。 -
sampling.rs(725 行):PMU 溢出 IRQ 处理器。每 CPU 固定
[Option<SampleSlot>; 32]在NoPreemptIrqSave下读写。频率模式自适应控制(next_freq_period)使用 1/8 阻尼,与 Linuxperf_adjust_period一致。ring_write在写入data_head前使用Release屏障,保证用户态观察到新data_head时也观察到记录内容。 -
task.rs(1300+ 行):per-task 事件通过
PerTaskCounter挂载在Thread.perf_counters,由既有perf_sched_in/perf_sched_out调度钩子驱动。热路径仅依赖原子操作和 sysreg 写入,PERF_TASK_ACTIVE计数器为 0 时 early-return,空闲系统开销仅为一次 relaxed 原子读。 -
sideband.rs:
PERF_RECORD_COMM/MMAP2/FORK/EXIT记录从进程上下文写入环,通过ring_write_process(mask 本地 IRQ)与溢出处理序列化。 -
sysfs/procfs:PMU 设备枚举(
/sys/bus/event_source/devices/armv8_pmuv3_0/)、MIDR 解码的/proc/cpuinfo、perf_event_*knobs。真实 perf 工具据此发现并打开 PMU。
安全性分析
- IRQ 上下文纪律:溢出处理器不分配内存、不持锁,先读取中断上下文(
ELR_EL1/SPSR_EL1)再操作 PMU,符合硬中断要求。 - 拆除顺序:
disablecounter →disable_irq→unregisterslot → dropArc,严格保证 IRQ 处理器不会访问已释放资源。Drop实现与disable共享teardown_sampling_irq(幂等)。 - 原始指针有效性:
SampleSlot::notify是*const IrqNotify,仅在注册的 slot 存活期间被解引用(unregister在Arc<IrqNotify>drop 之前执行),模块级文档明确论证了这一点。 - RAII 页面固定:ring 页面通过
Arc<GlobalPage>固定在 VMA retainer 中,Weak避免事件持有强引用导致生命周期延长。 - 无
[patch.crates-io]:未引入任何 crates.io patch,ABI 结构体复用kbpf-basic0.6。
测试覆盖
12 个自包含 QEMU C 用例位于 test-suit/starryos/qemu-smp1/system/perf-hw-*/:
- 计数(cycles、stat)、采样(sample、sample-task)
- PMU sysfs 枚举(pmu-sysfs)
- ioctl 行为(record-ioctl)、频率模式(freq)、EL0 直读(rdpmc)
- 多事件环合并(ring-merge)、side-band 记录(sideband)
- fork/exit 生命周期(fork-exit)、事件继承(inherit)
测试不依赖 perf 二进制,可独立运行。PR 作者还报告了真实上游 perf 6.6.0 的 QEMU 端到端验证和 OrangePi-5-Plus/RK3588 硬件验证结果。
CI 状态
- formatting: ✅ 通过
- clippy: ✅ 通过
- starry aarch64/x86_64/riscv64/loongarch64 qemu: ✅ 全部通过
- arceos aarch64/x86_64/riscv64/loongarch64 qemu: ✅ 全部通过
- axvisor aarch64/riscv64/loongarch64 qemu + x86_64 self-hosted: ✅ 全部通过
- board tests (orangepi-5-plus, visionfive2, roc-rk3568-pc, phytiumpi): ✅ 全部通过
- 唯一失败:
Test starry self-hosted board licheerv-nano-sg2002 / run_host— 板端网络 PHY 自协商超时(U-Boot DHCP 阶段未进入内核),已有跟踪 issue #1382,与本 PR 无关(本 PR 仅触及 aarch64 PMUv3 代码)。
重复/重叠分析
检查了当前所有 open PR(#1394, #1390, #1388, #1380, #1379, #1368 等),无其他 PR 涉及 PMU/硬件 perf 功能。base 分支现有 perf 仅覆盖软件/追踪事件,本 PR 补齐硬件路径,纯增量无冲突。
已有 review 考虑
mai-team-app[bot] 在旧 head(d11d084d8)提交了 APPROVE review。当前 head(f43949f4c)在该 review 之后又有两次 fix commit(修正 cross-arch grouped build 和 side-band 测试 sample period),改善了测试的跨架构兼容性和稳定性。
已知限制(非 blocking)
- 仅单核;多核 + big.LITTLE 异构 PMU 为后续工作
attr.inherit每任务占一个可编程计数器槽(暂不时分复用)- 仅 aarch64;其他架构通过
#[cfg]门控不受影响 - PMU IRQ 硬编码为 PPI 7/INTID 23(代码注释标注后续应从 FDT 读取)
结论
实现质量高、文档详尽、安全论证完整、测试覆盖全面。无阻塞性问题。
Powered by mimo-v2.5-pro
ZR233
left a comment
There was a problem hiding this comment.
本次复审基于 head f43949f4c576018f469d871a9df2be8b031ed190。
本地验证:
cargo fmt --check通过cargo xtask clippy --package ax-cpu28 项目标/feature 组合全部通过cargo xtask clippy --package starry-kernel17 项 feature 组合全部通过git diff --check origin/dev...HEAD通过
GitHub 当前检查也已到可合入状态,format/clippy/std、Starry/ArceOS/Axvisor QEMU 与 board 检查均为通过或预期跳过,mergeStateStatus=CLEAN。
我重点看了 PMUv3 sysreg 层、sampling ring 写入、PERF_EVENT_IOC_ID/SET_OUTPUT、per-task sched in/out、exec/clone/exit side-band 与新增 test-suit 用例。实现整体分层清楚,ring lifetime 通过 VMA retainer/anchor 固定,IRQ teardown 顺序也避免了 notify/ring UAF;新增 12 个 qemu-smp1/system/perf-hw-* 用例覆盖 counting、sampling、sysfs dynamic PMU type、freq、rdpmc、SET_OUTPUT/ring merge、side-band、fork/exit、inherit 等关键路径。
非阻塞建议:PR 描述里提到 PERF_TYPE_HW_CACHE,但当前实现实际支持的是 PERF_TYPE_HARDWARE、PERF_TYPE_RAW 和 sysfs 动态 armv8_pmuv3_0 type;HW_CACHE / BREAKPOINT 在 hw.rs 里仍明确返回 unsupported。常用命名事件通过 sysfs 动态 PMU type 暴露已经可以覆盖 cache_misses/cache_references 这类入口,但建议把 PR body 或后续文档里的范围表述收窄,避免给维护者留下 PERF_TYPE_HW_CACHE 静态 ABI 已支持的误解。
未发现阻塞问题,批准合入。
… / report) (#1395) * feat(starry): add PERF_TYPE_HARDWARE cpu-cycles via ARM PMUv3 (perf M0) * feat(starry): perf stat counting via PMUv3 programmable counters (perf M1) * feat(starry): perf record sampling via PMUv3 overflow IRQ (perf M2) * feat(starry): expose armv8_pmuv3 PMU via sysfs/procfs for perf (perf M2.5) * feat(starry): per-task perf counting via enable_on_exec + ctx-switch follow (perf M3-pt) * feat(starry): per-task perf-record sampling + multi-field PERF_RECORD_SAMPLE (perf M3-pt-rec) * fix(starry): perf_event_mmap_page version=1 + cpuid sysfs for real perf * feat(starry): real perf record via PERF_EVENT_IOC_ID + SET_OUTPUT (perf M3-rec-e2e) * feat(starry): perf frequency mode + multi-event ring-merge + rdpmc * feat(starry): perf side-band COMM + MMAP2 records for perf report symbolization * feat(starry): perf side-band FORK + EXIT task-lifetime records (attr.task) * feat(starry): perf attr.inherit — follow forked children into one ring * fix(starry): skip perf-hw C tests on non-aarch64 (cross-arch grouped build) * fix(starry): widen perf-hw side-band test sample period to prevent ring wrap
StarryOS 已实现
perf_event_open(2),但仅覆盖软件 / 追踪事件(kprobe / uprobe / tracepoint / eBPF software);硬件 PMU 路径完全缺失:PERF_TYPE_HARDWARE/HW_CACHE/RAW、read(perf_fd)、采样 + mmap 环形缓冲、rdpmc均返回Unsupported。本 PR 补齐硬件 PMU 路径,使真实上游perf工具(stat / record / report)能在 ARMv8 PMUv3(如 RK3588)上运行。改动纯增量(39 文件,+8009 / −29),原追踪 perf 路径不受影响、无回归。实现内容
硬件寄存器层(
components/axcpu/src/aarch64/pmu.rs,新增)ARM PMUv3 完整封装:PMCR / PMCCNTR / PMEVCNTRn / PMEVTYPERn / PMCNTENSET·CLR / PMINTENSET·CLR / PMOVSCLR / PMCEID / PMUSERENR / MIDR —— 覆盖计数、采样溢出、EL0 直读所需全部原语。
perf stat(计数)
PERF_TYPE_HARDWARE(cycles 走专用周期计数器,instructions / cache / branch 走可编程计数器)+PERF_TYPE_RAW;多计数器并行;read_format(value / time_enabled / time_running / id)。perf record(采样)
PMUv3 溢出 IRQ(PPI 7 / INTID 23,每-CPU 寄存器表)→ 按
sample_type写多字段PERF_RECORD_SAMPLE进 mmap 环;IRQ 安全的环写入 + 严格拆除顺序避免 UAF。PMU 枚举(sysfs / procfs)
/sys/bus/event_source/devices/armv8_pmuv3_0/{type,cpus,format,events}、/proc/sys/kernel/perf_event_*、MIDR 解码的/proc/cpuinfo—— 真实 perf 据此发现并打开 PMU。per-task(
pid > 0)事件随被监控线程上下文切换布防 / 撤防(复用既有
TaskExt调度钩子,不改 axtask);enable_on_exec;per-task 采样。真实 perf record 端到端
PERF_EVENT_IOC_ID+PERF_EVENT_IOC_SET_OUTPUT;perf_event_mmap_page.version=1;cpuid sysfs 节点。常用能力
频率模式(
-F,Linuxperf_adjust_period自适应);多事件共享环(-e a,b,SET_OUTPUT 重定向 + per-event id 区分);rdpmc/ EL0 直读(PMUSERENR + mmap_page index / pmc_width / cap_user_rdpmc)。side-band 记录(
perf report符号化)PERF_RECORD_COMM+MMAP2(exec 镜像 / 动态库真实路径);PERF_RECORD_FORK+EXIT(进程树)。事件继承(
attr.inherit)被监控任务 fork 子任务时事件随之克隆、写入同一环 ——
perf record默认的"跟随子进程"。设计要点
pid > 0→ per-task;pid ≤ 0(self /-a)→ 系统级。硬件类型判断置于原追踪 perf 调度之前,完全保留原路径,无回归。Thread.perf_counters,由既有调度钩子驱动,不触碰 axtask。kbpf-basic0.6。测试
12 个自包含 QEMU 用例(不依赖 perf 二进制,CI 可跑,全部 1/1 通过):
perf-hw-{cycles, stat, sample, pmu-sysfs, sample-task, record-ioctl, freq, rdpmc, ring-merge, sideband, fork-exit, inherit}。真实上游 perf 6.6.0(QEMU):
perf stat命名事件(经 sysfs 枚举)、perf record采集 724 样本。真实硬件(OrangePi-5-Plus / RK3588,单核):
perf stat为真实 PMU 值 —— 32,354,872 cycles、97,608,453 instructions、cycles + instructions 同时计数 IPC 0.89。clippy(starry-kernel + ax-cpu)45/45、nightly fmt、aarch64 build、riscv64 交叉编译(cfg 门控)全部通过。
范围与限制
#[cfg]门控,不受影响。attr.inherit每个被监控任务占一个硬件可编程计数器槽(暂不时分复用);后续工作。perf record的 mmap 修复已在 QEMU 验证,硬件上尚未在修复后复测。