深入解析ARM架构下perf_event相关模块的功能与调用流程

电子说

1.4w人已加入

描述

 

 

 Linux 系统性能分析领域,perf 工具扮演着至关重要的角色,而其底层依赖的 perf_event 相关模块更是核心中的核心。本文将围绕 perf_event.cperf_regs.c 和 perf_callchain.c 三个关键文件,深入解析 ARM 架构下性能监控的实现机制、核心功能及调用流程,并重点说明开发者关注这部分内容的意义与调试价值。

 

 

一、核心模块功能概述

 

1. perf_event.c:性能事件管理的核心

 

该文件是 ARM 架构下性能监控单元(PMU)驱动的核心实现,主要负责:

 

 

不同 ARMv8 处理器(如 Cortex-A53/A57/A72 等)PMU 的初始化与设备探测

 

 

性能事件的创建、映射、启用、禁用及计数器读写

 

 

事件属性的可见性控制与系统调用接口实现

 

 

中断处理与溢出检测等核心逻辑

 

 

2. perf_regs.c:寄存器数据处理

 

专注于处理性能事件采样时的寄存器数据:

 

 

提供寄存器值的读取接口,适配 32 位 / 64 位环境

 

 

处理不同特权级(EL0/EL1 等)下的寄存器访问

 

 

实现用户态与内核态寄存器数据的正确映射

 

 

3. perf_callchain.c:调用链追踪

 

负责记录性能事件发生时的函数调用链:

 

 

实现内核态与用户态调用链的回溯

 

 

处理 AArch32/AArch64 两种模式下的栈帧解析

 

 

提供调用链存储与格式转换功能

 

 

二、核心功能模块解析

 

1. PMU 设备初始化流程

 

ARMv8 架构下不同处理器的 PMU 初始化通过统一框架实现,以armv8_pmu_init为核心入口:

 

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
// 通用初始化函数static int armv8_pmu_init(struct arm_pmu *cpu_pmu, char *name,                          int (*map_event)(struct perf_event *event),                          const struct attribute_group *events,                          const struct attribute_group *format,                          const struct attribute_group *caps) {    // 1. 探测PMU硬件特性    int ret = armv8pmu_probe_pmu(cpu_pmu);    if (ret) return ret;        // 2. 初始化PMU操作函数集    cpu_pmu->handle_irq = armv8pmu_handle_irq;    cpu_pmu->enable = armv8pmu_enable_event;    cpu_pmu->disable = armv8pmu_disable_event;    // ... 其他函数赋值        // 3. 设置名称与事件映射函数    cpu_pmu->name = name;    cpu_pmu->map_event = map_event;        // 4. 注册属性组与系统控制表    // ...    armv8_pmu_register_sysctl_table();    return 0;}

不同处理器通过特定初始化函数调用通用框架:

 

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
// Cortex-A53初始化static int armv8_a53_pmu_init(struct arm_pmu *cpu_pmu) {    return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cortex_a53",                                   armv8_a53_map_event);}// 无特殊属性组的初始化封装static int armv8_pmu_init_nogroups(struct arm_pmu *cpu_pmu, char *name,                                  int (*map_event)(struct perf_event *event)) {    return armv8_pmu_init(cpu_pmu, name, map_event, NULLNULLNULL);}

2. 性能事件生命周期管理

 

事件创建与映射

 

事件映射是将用户空间的事件类型(如PERF_COUNT_HW_CPU_CYCLES)转换为硬件可识别的事件编码的过程:

 

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
// 事件映射核心实现static int __armv8_pmuv3_map_event(struct perf_event *event, ...) {    // 1. 基础事件映射    int hw_event_id = armpmu_map_event(event, &armv8_pmuv3_perf_map,                                      &armv8_pmuv3_perf_cache_map,                                      ARMV8_PMU_EVTYPE_EVENT);        // 2. 64位事件标记    if (armv8pmu_event_is_64bit(event))        event->hw.flags |= ARMPMU_EVT_64BIT;        // 3. 用户态访问控制    if (armv8pmu_event_want_user_access(event)) {        // 验证权限与硬件支持        event->hw.flags |= PERF_EVENT_FLAG_USER_READ_CNT;    }        // 4. 检查事件是否被硬件支持    if ((hw_event_id > 0) && test_bit(hw_event_id, armpmu->pmceid_bitmap)) {        return hw_event_id;    }    return ...;}

事件启用与禁用

 

事件启用过程涉及计数器配置、中断使能等硬件操作:

 

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
static void armv8pmu_enable_event(struct perf_event *event) {    // 1. 先禁用计数器确保配置安全    armv8pmu_disable_event_counter(event);        // 2. 配置事件类型    armv8pmu_write_event_type(event);        // 3. 使能中断    armv8pmu_enable_event_irq(event);        // 4. 启用计数器    armv8pmu_enable_event_counter(event);}

3. 计数器读写机制

 

根据计数器类型(周期计数器 / 普通事件计数器)提供不同读写实现:

 

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
// 读取计数器值static u64 armv8pmu_read_counter(struct perf_event *event) {    struct hw_perf_event *hwc = &event->hw;    int idx = hwc->idx;    u64 value;    // 周期计数器特殊处理    if (idx == ARMV8_IDX_CYCLE_COUNTER)        value = read_sysreg(pmccntr_el0);    else        value = armv8pmu_read_hw_counter(event);    return armv8pmu_unbias_long_counter(eventvalue);}// 64位事件的链式计数器读取static u64 armv8pmu_read_hw_counter(struct perf_event *event) {    int idx = event->hw.idx;    u64 val = armv8pmu_read_evcntr(idx);    // 链式事件需要合并两个32位计数器    if (armv8pmu_event_is_chained(event))        val = (val << 32) | armv8pmu_read_evcntr(idx - 1);    return val;}

4. 调用链追踪实现

 

调用链追踪通过解析栈帧结构实现函数调用路径的记录:

 

 

内核态调用链

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,                          struct pt_regs *regs) {    if (perf_guest_state()) return;  // 暂不支持客户机OS        // 遍历内核栈获取调用链    arch_stack_walk(callchain_trace, entry, current, regs);}

用户态调用链

 

区分 AArch32/AArch64 模式处理:

 

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
void perf_callchain_user(struct perf_callchain_entry_ctx *entry,                        struct pt_regs *regs) {    perf_callchain_store(entry, regs->pc);  // 存储当前PC    if (!compat_user_mode(regs)) {        // AArch64模式栈帧解析        struct frame_tail __user *tail = (struct frame_tail __user *)regs->regs[29];        while (entry->nr < entry->max_stack && tail && !((unsigned long)tail & 0x7))            tail = user_backtrace(tail, entry);    } else {        // AArch32兼容模式栈帧解析#ifdef CONFIG_COMPAT        struct compat_frame_tail __user *tail =             (struct compat_frame_tail __user *)regs->compat_fp - 1;        while ((entry->nr < entry->max_stack) && tail && !((unsigned long)tail & 0x3))            tail = compat_user_backtrace(tail, entry);#endif    }}

三、核心调用流程

 

1. PMU 初始化流程

 

寄存器

2. 性能事件处理流程

 

寄存器

四、模块关系脑图

 

寄存器

五、开发者关注该模块的核心意义

 

1. 掌握底层性能监控原理,突破工具使用局限

 

perf 工具是开发者分析性能问题的常用工具,但默认功能可能无法满足定制化需求。例如:

 

 

当需要监控 ARM 处理器特有的硬件事件(如 L2 缓存缺失率、分支预测失败数)时,需理解perf_event.c中事件映射逻辑(__armv8_pmuv3_map_event),才能自定义事件编码

 

 

面对 32 位与 64 位应用混合部署的场景,需通过perf_regs.c的寄存器适配逻辑,确保采样数据在不同模式下的准确性

 

 

若需扩展 perf 工具功能(如增加自定义调用链过滤规则),需深入perf_callchain.c的栈帧解析机制,避免破坏原有回溯逻辑

 

 

2. 解决跨处理器架构的兼容性问题

 

ARMv8 架构下处理器型号多样(Cortex-A53/A72Neoverse-N1 等),不同处理器的 PMU 硬件特性存在差异:

 

 

部分处理器支持 64 位事件计数(需链式合并两个 32 位计数器),部分仅支持 32 位计数,理解armv8pmu_read_hw_counter的链式处理逻辑,可避免在不同处理器上出现计数溢出或数据错误

 

 

新处理器可能新增硬件事件(如 Neoverse 的 DDR 带宽监控事件),开发者需基于armv8_pmu_init框架扩展初始化函数,实现新事件的支持,确保软件在新硬件上的兼容性

 

 

3. 优化内核驱动与高性能应用

 

对于内核驱动开发者或高性能应用(如数据库、实时控制系统)开发者:

 

 

可通过perf_event.c的计数器读写接口,在驱动中集成自定义性能监控逻辑,实时跟踪驱动关键路径的执行耗时

 

 

结合perf_callchain.c的调用链追踪,定位应用中频繁调用的内核函数,优化系统调用或内核态操作,减少上下文切换开销

 

 

六、该模块对调试的实际帮助

 

1. 定位性能瓶颈:从 现象” 到 根源” 的突破

 

传统调试工具(如 printflog)仅能记录代码执行流程,无法量化性能问题,而 perf_event 模块提供的能力可精准定位瓶颈:

 

 

案例 1:应用 CPU 使用率过高

 

 

通过perf_event.c的周期计数器(pmccntr_el0),统计不同函数的 CPU 周期消耗;结合perf_callchain_user的调用链,发现某排序函数被频繁调用,且每次调用触发多次 L1 缓存缺失(通过 PMU 的缓存事件计数),最终定位到排序算法的时间复杂度问题,优化后 CPU 使用率下降 。

 

 

案例 2:内核驱动响应延迟

 

 

利用perf_callchain_kernel记录驱动函数的调用链,结合armv8pmu_handle_irq的中断处理逻辑,发现驱动中断处理函数中存在自旋锁争用,导致中断响应延迟。通过调整锁策略(如改用互斥锁、减少锁持有时间),将中断延迟降低。

 

 

2. 解决疑难杂症:捕捉偶发的硬件相关问题

 

部分偶发问题(如寄存器值异常、硬件事件溢出)难以通过常规调试复现,该模块可提供关键线索:

 

 

案例:32 位应用崩溃且日志无明确错误

 

 

分析perf_regs.c的寄存器采样数据,发现崩溃时程序计数器(PC)指向无效地址,且栈指针(SP)未按 32 位模式对齐(未满足 字节对齐要求)。进一步查看compat_user_backtrace的栈帧解析逻辑,发现应用在调用汇编函数时未正确保存 SP,导致栈帧错乱,修复栈对齐问题后崩溃消失。

 

 

案例:PMU 计数器数据异常

 

 

调试时发现某硬件事件计数为 0,排查__armv8_pmuv3_map_event的事件映射逻辑,发现该事件 ID 未在armv8pmu_probe_pmu探测的pmceid_bitmap中置位,说明当前处理器不支持该事件,避免开发者在不支持的硬件上浪费调试时间。

 

 

3. 验证优化效果:量化调试成果

 

调试后的优化效果需量化验证,该模块提供客观的评估依据:

 

 

优化前后,通过armv8pmu_read_counter读取同一事件的计数(如 CPU 周期、缓存缺失数),对比优化效果

 

 

结合调用链追踪,统计优化后关键函数的调用次数变化,验证优化是否减少了冗余调用

 

 

对于实时系统,通过中断计数器(armv8pmu_enable_event_irq),验证优化后中断响应时间是否满足实时性要求

 

 

七、总结

 

ARM 架构下的 perf_event 相关模块不仅是 perf 工具的底层支撑,更是开发者理解硬件特性、优化软件性能、解决疑难调试问题的核心工具。其分层设计(硬件适配层、事件管理层、数据采集层)既保证了对不同 ARM 处理器的兼容性,又为定制化扩展提供了灵活接口。

 

 

对于开发者而言,深入掌握该模块:

 

 

可突破工具使用局限,实现定制化性能监控

 

 

能快速定位性能瓶颈与疑难问题,提升调试效率

 

 

为内核驱动与高性能应用的优化提供底层依据

 

 

无论是日常的应用开发调试,还是底层的硬件软件适配,该模块的知识都能为开发者提供关键支撑,是 ARM 架构下 Linux 系统开发与调试的重要基础。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分