perf_event_open()性能计数器无输出问题排查及内核代码位置咨询
问题排查与解决
一、计数器返回0的可能原因及修复方案
1. 未检查perf_event_open的返回值
你的代码未验证fd是否有效——复杂应用中,可能因硬件计数器资源被抢占、线程级权限差异或内核限制,导致perf_event_open调用失败返回-1,后续读取无效fd会返回0值。
修复:添加错误检查逻辑:
int fd = syscall(__NR_perf_event_open, &PerfEvent, getpid(), -1, -1, 0); if (fd == -1) { perror("perf_event_open failed"); exit(EXIT_FAILURE); }
2. 事件未被正确启用
尽管设置了PerfEvent.disabled = 0,但多线程/复杂进程环境中,内核可能无法自动触发事件启用。显式手动控制启停更可靠:
// 创建事件后显式启用 ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); // 待Profile的代码 // 读取前禁用,确保计数稳定 ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); uint64_t RetVal[3]; const int32_t rc = read(fd, &RetVal, sizeof(RetVal)); close(fd);
3. 进程/线程监控范围不匹配
用getpid()监控整个进程,但如果待Profile的代码运行在其他线程,内核可能未将事件关联到目标线程,导致计数为0。可以尝试:
- 用
gettid()替换getpid(),仅监控当前线程; - 若需监控整个进程,确认
inherit=1的行为符合预期(会监控子进程,但可能分散计数资源)。
4. pinned=1导致事件无法绑定
pinned=1要求事件独占硬件计数器,若系统中已有其他perf事件(如应用自身监控、外部perf工具)占用所有可用计数器,当前事件会无法激活,最终计数为0。
修复:暂时去掉PerfEvent.pinned = 1,或关闭系统中其他正在运行的perf相关工具后重试。
二、Linux内核中性能计数器的更新代码位置
通用perf事件框架核心逻辑:
kernel/events/core.c
这里处理事件的创建、启用/禁用调度、计数汇总与读取逻辑,比如perf_event_read负责将硬件计数器原始值转换为用户空间可读结果,perf_event_task_sched_in/perf_event_task_sched_out负责上下文切换时的计数器更新。硬件架构相关实现:
以x86架构为例,代码位于arch/x86/events/目录:- Intel平台:
arch/x86/events/intel/core.c,处理Intel CPU的硬件事件计数、寄存器配置与更新; - AMD平台:
arch/x86/events/amd/core.c,对应AMD CPU的实现。
- Intel平台:
计数器中断处理:
硬件计数器溢出时,内核通过中断触发计数更新,相关逻辑在架构相关的中断处理函数中,比如x86的perf_event_overflow回调。
内容的提问来源于stack exchange,提问作者Kipje
相关产品推荐
相关产品推荐

