kernel/events 性能事件(Perf Events)机制与原理详解

kernel/events 性能事件(Perf Events)机制与原理详解

源码路径rk3588/kernel-6.1/kernel/events/
内核版本:Linux 6.1(RK3588 平台)
平台:RK3588(4×Cortex-A76 + 4×Cortex-A55 big.LITTLE,ARM64)

该目录实现 Linux 内核 Perf Events(性能事件) 子系统的核心框架,为 perfftrace、BPF、KVM PMU 等提供统一的性能计数、采样、追踪基础设施。用户态通过 perf_event_open(2) 创建事件,内核将采样数据写入 mmap 环形缓冲区,供 perf record 等工具消费。


目录


一、源码目录结构

1.1 编译依赖(Makefile)

1
2
3
4
5
obj-y := core.o ring_buffer.o callchain.o

obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
obj-$(CONFIG_HW_BREAKPOINT_KUNIT_TEST) += hw_breakpoint_test.o
obj-$(CONFIG_UPROBES) += uprobes.o
配置项 说明
CONFIG_PERF_EVENTS 启用 perf 子系统(kernel/Makefileobj-$(CONFIG_PERF_EVENTS) += events/
CONFIG_HAVE_HW_BREAKPOINT 硬件断点支持(ARM64 在 PERF_EVENTS 下 select)
CONFIG_UPROBES 用户态探针(trace Kconfig 中 select)
CONFIG_UPROBE_EVENTS 将 uprobes 注册为 perf PMU
CONFIG_HW_BREAKPOINT_KUNIT_TEST 硬件断点 KUnit 单元测试

1.2 源文件

文件 行数 功能
core.c ~13825 perf 核心:syscall、PMU 注册、上下文调度、采样、mmap、BPF 集成
ring_buffer.c ~972 mmap 环形缓冲区读写、AUX 区域、内存屏障同步
callchain.c ~253 采样调用栈收集与缓冲区管理
hw_breakpoint.c ~1051 硬件断点 slot 约束、perf_event 集成
uprobes.c ~2360 用户态断点探针、XOL 单步执行
hw_breakpoint_test.c ~333 KUnit 硬件断点测试(非生产路径)
internal.h ~247 环形缓冲区内部 API、递归上下文保护

相关头文件

文件 功能
include/linux/perf_event.h 公共 API:struct pmustruct perf_event、内核/用户接口
include/linux/hw_breakpoint.h 硬件断点 API
include/linux/uprobes.h uprobes 公共接口

架构/驱动层(RK3588 实际硬件 PMU)

文件 功能
drivers/perf/arm_pmu.c ARM 通用 PMU 驱动框架
drivers/perf/arm_pmu_platform.c 平台 PMU 注册(含 Cortex-A76/A55)
arch/arm64/kernel/perf_event.c ARM64 PMU 架构实现
arch/arm64/kvm/pmu-emul.c KVM guest PMU 模拟

二、整体架构

2.1 分层模型

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
┌─────────────────────────────────────────────────────────────┐
│ 用户态:perf / trace-cmd / BPF / gdb │
│ perf_event_open(2) + mmap + poll/read │
└───────────────────────────┬─────────────────────────────────┘

┌───────────────────────────▼─────────────────────────────────┐
│ kernel/events/core.c │
│ 事件分配 · 上下文调度 · 采样输出 · 权限控制 · sysfs │
└───────────────────────────┬─────────────────────────────────┘

┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ software │ │ tracepoint │ │ armv8_pmuv3 │
│ PMU │ │ / kprobe │ │ (CPU PMU) │
│ │ │ / uprobe │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
└──────────────────┴──────────────────┘

┌───────────────────────────▼─────────────────────────────────┐
│ kernel/events/ring_buffer.c │
│ perf_buffer 环形缓冲区 → mmap 映射到用户态 │
└─────────────────────────────────────────────────────────────┘

2.2 事件类型(type 字段)

type PMU 名称 说明
PERF_TYPE_HARDWARE armv8_pmuv3 CPU 硬件计数器(cycles、cache miss 等)
PERF_TYPE_SOFTWARE software 软件事件(page fault、context switch 等)
PERF_TYPE_TRACEPOINT tracepoint 内核 tracepoint
动态分配 kprobe / uprobe 内核/用户态动态探针
自定义 各 SoC uncore PMU DDR、CCI 等(RK3588 通用 ARM PMU 为主)

2.3 与其他子系统的关系

子系统 关系
kernel/trace/ tracepoint PMU 依赖 perf 框架;ftrace 与 perf 共享 tracepoint
kernel/bpf/ BPF 程序可 attach 到 perf event 作为 overflow handler
kernel/cgroup/ CONFIG_CGROUP_PERF 支持按 cgroup 过滤事件
kernel/entry/ 采样时获取 pt_regs;uprobe 通过 TIF_UPROBE 在返回用户态时处理

三、核心数据结构

3.1 struct pmu — 性能监控单元抽象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct pmu {
struct list_head entry;
const char *name;
int type; // PERF_TYPE_* 或动态 ID
int capabilities; // PERF_PMU_CAP_*

// 生命周期回调
int (*event_init)(struct perf_event *event);
int (*add)(struct perf_event *event, int flags);
void (*del)(struct perf_event *event, int flags);
void (*start)(struct perf_event *event, int flags);
void (*stop)(struct perf_event *event, int flags);
void (*read)(struct perf_event *event); // 读取计数值

struct perf_cpu_context __percpu *pmu_cpu_context;
// ...
};

每个 PMU(software、tracepoint、armv8_pmuv3 等)通过 perf_pmu_register() 注册到全局 PMU 链表。

3.2 struct perf_event — 单个性能事件

1
2
3
4
5
6
7
8
9
10
11
struct perf_event {
struct pmu *pmu;
struct perf_event_attr attr; // 用户态传入的属性
struct perf_event_context *ctx; // 所属上下文
struct perf_event *group_leader;
struct list_head event_entry;

struct hw_perf_event hw; // 硬件/软件具体状态
struct perf_buffer *rb; // 输出环形缓冲区
// overflow_handler, prog (BPF), ...
};

struct perf_event_attr 定义采样类型(sample_type)、采样周期(sample_period / sample_freq)、过滤条件(exclude_kernelexclude_user)等。

3.3 struct perf_event_context — 事件上下文

1
2
3
4
5
6
7
8
9
10
struct perf_event_context {
struct pmu *pmu;
struct task_struct *task; // NULL 表示 per-CPU 上下文
struct list_head event_list;
struct mutex mutex;
raw_spinlock_t lock;
int nr_events;
int nr_active;
// ...
};
  • task context:绑定到特定进程/线程的事件集合
  • cpu context:绑定到特定 CPU 的事件集合(task == NULL

3.4 struct perf_cpu_context — 每 CPU 上下文

1
2
3
4
5
6
struct perf_cpu_context {
struct perf_event_context ctx; // CPU 级上下文
struct perf_event_context *task_ctx; // 当前运行任务的上下文
struct pmu *pmu;
// active_ctx_list, hrtimer (mux rotation), ...
};

每个 PMU 在每个 CPU 上有一个 perf_cpu_context,管理该 PMU 在该 CPU 上的所有事件调度。

3.5 struct perf_buffer — 环形缓冲区(internal.h)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct perf_buffer {
refcount_t refcount;
int nr_pages;
int overwrite;
local_t head; // 内核写指针
local_t lost; // 丢失记录数
long watermark; // 唤醒水位线
unsigned int nest; // 嵌套写计数
struct perf_event_mmap_page *user_page; // 用户可见 meta page
void *data_pages[]; // 数据页数组

// AUX 区域(Intel PT、ARM SPE 等)
long aux_head;
int aux_nr_pages;
void **aux_pages;
};

四、PMU 抽象与注册

4.1 初始化顺序(perf_event_init)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void __init perf_event_init(void)
{
idr_init(&pmu_idr);
perf_event_init_all_cpus();
init_srcu_struct(&pmus_srcu);

perf_pmu_register(&perf_swevent, "software", PERF_TYPE_SOFTWARE);
perf_pmu_register(&perf_cpu_clock, NULL, -1);
perf_pmu_register(&perf_task_clock, NULL, -1);
perf_tp_register(); // tracepoint / kprobe / uprobe PMU

perf_event_init_cpu(smp_processor_id());
init_hw_breakpoint();
perf_event_cache = KMEM_CACHE(perf_event, SLAB_PANIC);
}

4.2 perf_pmu_register 流程

1
2
3
4
5
6
perf_pmu_register(pmu, name, type)
→ 分配 pmu_disable_count (percpu)
→ idr_alloc 分配 type ID(非 SOFTWARE 类型)
→ 分配/关联 perf_cpu_context (percpu)
→ 加入全局 pmus 链表
→ 注册 sysfs 设备(device_initcall 阶段)

4.3 已注册 PMU 一览

PMU type 注册位置
software PERF_TYPE_SOFTWARE core.c
cpu_clock / task_clock -1(无 type) core.c
tracepoint PERF_TYPE_TRACEPOINT core.c
kprobe 动态 core.c (CONFIG_KPROBE_EVENTS)
uprobe 动态 core.c (CONFIG_UPROBE_EVENTS)
armv8_pmuv3 PERF_TYPE_HARDWARE arch/arm64 + drivers/perf

五、事件生命周期

5.1 perf_event_open 系统调用

1
2
3
SYSCALL_DEFINE5(perf_event_open,
struct perf_event_attr __user *, attr_uptr,
pid_t, pid, int, cpu, int, group_fd, unsigned long, flags)

主要步骤:

1
2
3
4
5
6
7
8
1. perf_copy_attr()           — 拷贝并校验 attr
2. security_perf_event_open() — LSM 安全检查
3. perf_allow_kernel() — paranoid 权限检查
4. find_lively_task_by_vpid() — 解析目标 task(pid != -1)
5. perf_event_alloc() — 分配 perf_event,匹配 PMU
6. perf_event_set_output() — 关联输出 ring buffer
7. perf_install_in_context() — 安装到 context 并 enable
8. anon_inode 创建 fd — 返回 event fd

5.2 perf_event_alloc 流程

1
2
3
4
5
6
perf_event_alloc(attr, cpu, task, group_leader, ...)
→ 遍历 pmus 链表,调用 pmu->event_init(event)
→ 匹配第一个返回 0 的 PMU
→ 分配 perf_event_context(task 或 cpu)
→ 初始化 hw_perf_event 状态
→ inherit 处理(子进程继承)

5.3 安装到上下文

1
2
3
4
5
6
7
8
perf_install_in_context(ctx, event, cpu)
→ 若在目标 CPU 上:直接 __perf_install_in_context()
→ 否则:smp_call_function_single / task_function_call 远程安装

__perf_install_in_context():
→ list_add_event() + perf_group_attach()
→ ctx_sched_in() — 调度事件到硬件
→ event->state = PERF_EVENT_STATE_ACTIVE

5.4 事件状态机

1
2
3
4
PERF_EVENT_STATE_INACTIVE  →  add/start  →  PERF_EVENT_STATE_ACTIVE
PERF_EVENT_STATE_ACTIVE → stop/del → PERF_EVENT_STATE_INACTIVE
PERF_EVENT_STATE_ERROR — 初始化失败
PERF_EVENT_STATE_OFF — 被显式 disable

六、上下文调度机制

6.1 调度优先级

硬件 PMU 计数器数量有限,多个事件竞争时需按优先级调度:

1
2
3
4
5
优先级(高 → 低):
1. CPU pinned (EVENT_CPU | EVENT_PINNED)
2. task pinned (EVENT_PINNED)
3. CPU flexible (EVENT_CPU | EVENT_FLEXIBLE)
4. task flexible (EVENT_FLEXIBLE)

6.2 ctx_resched 流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static void ctx_resched(struct perf_cpu_context *cpuctx,
struct perf_event_context *task_ctx,
enum event_type_t event_type)
{
perf_pmu_disable(cpuctx->ctx.pmu);

// 1. 按优先级 sched_out 低优先级事件
task_ctx_sched_out(cpuctx, task_ctx, event_type);
cpu_ctx_sched_out(cpuctx, ctx_event_type);

// 2. 按优先级 sched_in 高优先级事件
perf_event_sched_in(cpuctx, task_ctx);

perf_pmu_enable(cpuctx->ctx.pmu);
}

6.3 任务切换时的 perf 处理

1
2
3
4
context_switch(old, new)
→ perf_event_context_sched_out(old) // 卸载 old task 的 perf events
→ perf_event_context_sched_in(new) // 加载 new task 的 perf events
→ perf_cgroup_switch() // cgroup perf 时间戳更新

6.4 Mux 轮转(hrtimer)

当 flexible 事件数超过硬件 counter 数量时,通过 hrtimer 周期性轮转计数器,使各事件共享硬件资源:

1
2
3
perf_mux_hrtimer_handler()
→ perf_rotate_context(cpuctx)
→ ctx_resched() — 切换 active 事件组

七、环形缓冲区(ring_buffer.c)

7.1 设计目标

  • 内核 NMI/IRQ 上下文写入,用户态 mmap 读取
  • 支持 overwrite 模式(环形覆盖)和 lossless 模式(缓冲区满则丢弃)
  • 支持 AUX 区域(ARM SPE、Intel PT 等辅助追踪数据)
  • 正确的内存屏障保证无锁 SPSC(单生产者单消费者)语义

7.2 生产者-消费者同步

1
2
3
4
5
6
7
8
kernel (producer)              userspace (consumer)
───────────────── ─────────────────────
if (LOAD ->data_tail) { LOAD ->data_head
(A) control dep smp_rmb() (C)
STORE $data LOAD $data
smp_wmb() (B) smp_mb() (D)
STORE ->data_head STORE ->data_tail
}
  • A ↔ D:control dependency / full barrier
  • B ↔ C:WMB / RMB 配对

7.3 嵌套写入(nest 计数)

NMI 可能嵌套在 IRQ 采样中,rb->nest 确保只有最外层写入完成后才发布 data_head

1
2
3
perf_output_get_handle()  → rb->nest++
// ... 写入 sample record ...
perf_output_put_handle() → 仅 nest==1 时更新 user_page->data_head

7.4 关键 API

函数 功能
rb_alloc() 分配 perf_buffer 及 data pages
perf_output_begin/end() 开始/结束一次 sample 写入
perf_event_wakeup() 水位线到达时 irq_work 唤醒 poll
rb_alloc_aux() 分配 AUX 追踪区域
perf_mmap_to_page() mmap fault 处理,映射 data page

八、采样与输出(core.c)

8.1 溢出处理链

1
2
3
4
5
6
7
8
9
10
PMU 中断 / hrtimer / tracepoint 触发
→ __perf_event_overflow(event, data, regs)
→ 频率节流检查
→ 调用 event->overflow_handler()
→ 默认:perf_event_output()
→ BPF attach:bpf_overflow_handler() → bpf_prog_run()
→ perf_prepare_sample() — 填充 sample 字段
→ output_begin() — 获取 ring buffer 空间
→ perf_output_sample() — 写入 PERF_RECORD_SAMPLE
→ perf_output_end() — 发布 head

8.2 sample_type 字段

标志 内容
PERF_SAMPLE_IP 指令指针
PERF_SAMPLE_TID pid/tid
PERF_SAMPLE_TIME 时间戳
PERF_SAMPLE_CALLCHAIN 调用栈
PERF_SAMPLE_REGS 寄存器快照
PERF_SAMPLE_STACK_USER 用户栈 dump
PERF_SAMPLE_DATA_SRC 内存访问来源(cache level 等)

8.3 权限与安全

sysctl 默认值 说明
kernel.perf_event_paranoid 2 ≥2 仅允许用户态事件;0 允许内核态;<0 无限制
kernel.perf_event_mlock ~516 KB 每用户 perf mmap 锁定内存上限
kernel.perf_event_max_sample_rate 100000 最大采样频率 (Hz)
kernel.perf_cpu_time_max_percent 25 perf 采样占用 CPU 时间上限 (%)

九、调用栈回溯(callchain.c)

9.1 功能

sample_type 包含 PERF_SAMPLE_CALLCHAIN 时,在采样点收集内核/用户态调用栈。

9.2 架构 hook

1
2
3
4
__weak void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
struct pt_regs *regs);
__weak void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
struct pt_regs *regs);

ARM64 在 arch/arm64/kernel/perf_callchain.c 中实现栈展开(frame pointer / ORC)。

9.3 缓冲区管理

1
2
3
4
5
6
perf_callchain(event, regs)
→ get_recursion_context() — NMI/IRQ/softirq 递归保护
→ get_callchain_entry() — 获取 per-CPU 预分配 buffer
→ perf_callchain_kernel() — 内核栈
→ perf_callchain_user() — 用户栈
→ put_callchain_entry()
  • 缓冲区按 CPU 预分配(NMI 安全,不能用 percpu API)
  • sysctl perf_event_max_stack 控制最大栈深度(默认 127)

十、硬件断点(hw_breakpoint.c)

10.1 概述

利用 CPU Debug 寄存器(ARM64 DBGBCR/DBGBVR)实现数据/指令断点,集成到 perf_event 框架。

10.2 Slot 约束管理

硬件断点寄存器数量有限(ARM64 通常每 CPU 若干组),需全局约束:

1
2
3
4
struct bp_cpuinfo {
unsigned int cpu_pinned; // 该 CPU 上 pinned 断点数
struct bp_slots_histogram tsk_pinned; // 任务级 pinned 直方图
};
函数 功能
reserve_bp_slot() 注册前检查并预留 slot
release_bp_slot() 释放 slot
register_user_hw_breakpoint() 用户态 API:注册任务级断点
register_wide_hw_breakpoint() 全 CPU 范围断点

10.3 断点类型

类型 说明
HW_BREAKPOINT_R 读访问断点
HW_BREAKPOINT_W 写访问断点
HW_BREAKPOINT_RW 读写断点
HW_BREAKPOINT_X 指令执行断点

10.4 与 perf 集成

1
2
3
4
5
6
7
// hw_breakpoint.c
static struct pmu perf_breakpoint = {
.event_init = hw_breakpoint_event_init,
.add = hw_breakpoint_add,
.del = hw_breakpoint_del,
// ...
};

用户态通过 perf_event_open 设置 type=PERF_TYPE_BREAKPOINT 创建硬件断点事件。


十一、用户态探针(uprobes.c)

11.1 概述

Uprobes 在用户态可执行文件的指定偏移插入软件断点,命中时执行 probe handler(如 perf 采样、kprobe 式追踪)。

11.2 核心数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct uprobe {
struct rb_node rb_node; // 按 (inode, offset) 索引
struct inode *inode;
loff_t offset;
struct arch_uprobe arch; // 原始/修改指令
struct uprobe_consumer *consumers;
struct list_head pending_list;
};

struct uprobe_task {
struct uprobe *active_uprobe;
enum uprobe_task_state state;
// XOL (execute out of line) 区域
};

11.3 工作流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
注册阶段:
uprobe_register(inode, offset, consumer)
→ 插入 uprobes_tree (rbtree)
→ uprobe_apply() — 对已映射的 VMA 插入 SW breakpoint

命中阶段:
用户态执行到断点 → CPU debug exception
→ uprobe_pre_sstep_notifier() — 设置 TIF_UPROBE
→ 返回用户态路径:uprobe_notify_resume()
→ handle_swbp()
→ 分配 XOL slot
→ 复制原始指令到 XOL 区域
→ 单步执行原始指令
→ uprobe_post_sstep_notifier() — 单步完成
→ handle_singlestep() — 恢复断点,调用 consumer handler

进程 fork/mmap:
uprobe_mmap() — 新映射时自动插入断点
uprobe_dup_mmap() — fork 时复制 uprobe 状态

11.4 XOL(Execute Out of Line)

由于断点指令覆盖了原始指令,需在独立的匿名可执行页中保存并执行原始指令:

1
2
3
原始 VMA:  [SW BREAKPOINT] [next insn...]
XOL area: [original insn] [jump back]
↑ 单步在此执行

11.5 与 perf 集成

1
2
3
4
// core.c (CONFIG_UPROBE_EVENTS)
perf_pmu_register(&perf_uprobe, "uprobe", -1);

// 用户态:perf probe -x /path/to/binary:offset

十二、用户态接口

12.1 系统调用

调用 功能
perf_event_open(2) 创建/打开 perf event
perf_event_read(2) 读取计数值(部分架构)
perf_event_read_value(2) 读取 enabled/running/count
perf_event_query_bpf(2) 查询/attach BPF 程序

12.2 mmap 映射

1
2
fd = perf_event_open(&attr, pid, cpu, -1, 0);
mmap(addr, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

映射布局:

pgoff 内容
0 perf_event_mmap_page(meta page:data_head/data_tail/time/…)
1..N 数据页(sample records)
aux_offset.. AUX 区域(可选,ARM SPE 等)

12.3 常用 perf 命令对应

命令 内核路径
perf stat -e cycles PERF_TYPE_HARDWARE → armv8_pmuv3 PMU
perf record -e syscalls:sys_enter_openat PERF_TYPE_TRACEPOINT
perf probe -x ./a.out:main uprobe PMU
perf record -e cpu-cycles -g callchain.c 栈回溯
perf record -e 'mem:0x1000' hw_breakpoint PMU

十三、RK3588/ARM64 平台说明

13.1 Kconfig 支持

1
2
3
4
5
6
7
# arch/arm64/Kconfig
select HAVE_PERF_EVENTS
select HAVE_HW_BREAKPOINT if PERF_EVENTS
select HAVE_PERF_REGS
select HAVE_PERF_USER_STACK_DUMP
config ARCH_SUPPORTS_UPROBES
def_bool y

RK3588 默认启用 CONFIG_PERF_EVENTSCONFIG_HAVE_HW_BREAKPOINTCONFIG_UPROBES

13.2 CPU PMU(Cortex-A76 + A55)

特性 说明
PMU 版本 ARMv8 PMUv3(armv8_pmuv3
计数器数 每 CPU 通常 6 个通用计数器 + 1 个 cycle counter
big.LITTLE PERF_PMU_CAP_HETEROGENEOUS_CPUS — A76/A55 事件编码可能不同
驱动路径 drivers/perf/arm_pmu.c + arch/arm64/kernel/perf_event.c
常见事件 cyclesinstructionscache-refillsbranch-misses

13.3 big.LITTLE 注意事项

RK3588 的 4×A76 + 4×A55 属于异构 CPU:

  • 同一 attr.config 在不同核心上可能映射到不同硬件事件
  • perf stat 跨核心统计时需注意 event 兼容性
  • 可通过 -C 绑定特定 CPU 或 --no-aggr 分核统计

13.4 KVM PMU

arch/arm64/kvm/pmu-emul.c 为 guest 提供 PMU 模拟:

  • guest 访问 PMUSERENR/PMCR 等寄存器时 trap 到 host
  • host 侧创建真实 perf_event 计数 guest 活动
  • kvm_arm_pmu_available static key 控制是否启用

13.5 硬件断点(ARM64)

  • 使用 Debug 架构的 Breakpoint Value/Control 寄存器
  • 每 CPU 有限数量的 breakpoint/watchpoint slot
  • hw_breakpoint.c 统一管理 slot 分配,防止超限
  • gdb watch/break 命令底层使用此机制

13.6 uprobes(ARM64)

  • arch/arm64/include/asm/uprobes.h 定义 struct arch_uprobe
  • 断点指令:BRK #0x004d(UPROBE_SWBP_INSN)
  • XOL 单步:arch_uprobe_skip_sstep() 处理 PC 对齐

十四、完整 perf record 时序

perf record -e cycles -c 10000 ./app 为例:

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
[用户态 perf]
perf_event_open(PERF_TYPE_HARDWARE, config=cycles, sample_period=10000)
→ core.c: SYSCALL_DEFINE5(perf_event_open)
→ 匹配 armv8_pmuv3 PMU
→ perf_event_alloc() + perf_install_in_context()
→ ctx_sched_in() → pmu->add() → 编程 PMU 计数器

mmap(fd) → ring_buffer.c: perf_mmap()
→ 分配 perf_buffer + data pages
→ 映射 meta page (data_head/data_tail) + data pages

[app 运行]
CPU 执行 app 指令,PMU cycle counter 递减
counter 归零 → PMU 溢出中断
→ arch/arm64/kernel/perf_event.c: arm_pmu interrupt handler
→ __perf_event_overflow()
→ perf_prepare_sample(IP, TID, TIME, ...)
→ perf_callchain() (若 -g 启用)
→ perf_output_begin/end() → 写入 ring buffer
→ perf_event_wakeup() → irq_work → poll 唤醒

[用户态 perf]
poll(fd) 返回 POLLIN
读取 ring buffer: data_tail 追赶 data_head
解析 PERF_RECORD_SAMPLE records
写入 perf.data

close(fd) → perf_event_release()
→ perf_remove_from_context()
→ ctx_sched_out() → pmu->del()
→ rb_free() 释放 ring buffer

十五、总结

kernel/events 是 Linux 性能分析与追踪 的核心基础设施:

  1. PMU 抽象struct pmu 统一 software、tracepoint、hardware 等各类事件源
  2. 上下文调度 — pinned/flexible 优先级 + mux 轮转,在有限硬件计数器上 multiplex 多事件
  3. 环形缓冲区 — 无锁 SPSC mmap 缓冲区,NMI 安全的嵌套写入
  4. 采样链 — overflow → prepare_sample → callchain → output → wakeup
  5. 硬件断点 — Debug 寄存器 slot 约束管理,集成 perf_event 框架
  6. Uprobes — 用户态软件断点 + XOL 单步,支持 perf probe 动态追踪
  7. 安全控制 — paranoid 级别、mlock 限制、LSM 集成

RK3588 作为 ARM64 big.LITTLE 平台,通过 drivers/perf/arm_pmu.carch/arm64/kernel/perf_event.c 提供 CPU PMU 支持;kernel/events 中的 core、ring_buffer、callchain、hw_breakpoint、uprobes 等模块在 RK3588 上完整可用,是 perf stat/record/top、eBPF 性能工具、gdb 硬件断点等功能的内核基础。


附录:源文件清单

文件 行数 分类
core.c ~13825 perf 核心框架
ring_buffer.c ~972 mmap 环形缓冲区
callchain.c ~253 调用栈回溯
hw_breakpoint.c ~1051 硬件断点
uprobes.c ~2360 用户态探针
hw_breakpoint_test.c ~333 KUnit 测试
internal.h ~247 内部头文件

RK3588 相关架构/驱动文件

文件 功能
arch/arm64/kernel/perf_event.c ARM64 PMU 驱动
arch/arm64/kernel/perf_callchain.c ARM64 栈展开
arch/arm64/kvm/pmu-emul.c KVM PMU 模拟
drivers/perf/arm_pmu.c ARM PMU 通用框架
drivers/perf/arm_pmu_platform.c 平台 PMU 注册
include/linux/perf_event.h 公共 API(~1766 行)

文章互动

阅读 --

留言

0 条留言

正在加载留言…