perf stat -p 指定PID无法采集PMU电源事件,求解决办法
问题描述
我尝试用perf stat测量代码特定段的能耗:先启动应用程序,等它运行到指定阶段后,用perf stat -p <PID>命令指定进程PID进行测量。这种方法对常规性能计数器有效,但无法采集power/energy-pkg/或power/energy-ram这类PMU功率事件,请问如何实现代码块的能耗采集?
可复现示例
测试代码
#include <stdio.h> #include <unistd.h> int main() { int pid= getpid(); printf("my pid is %d\n", pid); sleep(15); return 0; }
正常运行的命令(直接启动程序测量)
$ gcc example.c $ sudo perf stat -e power/energy-pkg/ ./a.out # 此命令可正常输出能耗数据
无效的命令(附加到已有进程)
$ ./a.out & $ pid=$! $ sudo perf stat -e power/energy-pkg/ -p $pid # 此命令无法采集功率事件 Performance counter stats for process id '1749416': <not supported> Joules power/energy-pkg/
解决方案
方法1:手动调用perf_event_open系统控制计数
由于power/energy-pkg/这类事件是系统级的,无法通过perf stat -p直接绑定进程,可在代码中通过perf_event_open系统调用直接操作计数器,在目标代码块前后启停并计算差值:
示例修改后的代码(需要root或CAP_PERFMON权限):
#include <stdio.h> #include <unistd.h> #include <sys/syscall.h> #include <linux/perf_event.h> #include <stdint.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) { return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); } int main() { int pid = getpid(); printf("my pid is %d\n", pid); struct perf_event_attr attr; memset(&attr, 0, sizeof(struct perf_event_attr)); attr.type = PERF_TYPE_POWER; attr.size = sizeof(struct perf_event_attr); attr.config = PERF_COUNT_POWER_ENERGY_PKG; // 对应power/energy-pkg/ attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING; int fd = perf_event_open(&attr, pid, -1, -1, 0); if (fd == -1) { perror("perf_event_open failed"); exit(EXIT_FAILURE); } // 等待进入目标阶段(这里用sleep模拟) sleep(5); // 读取初始值 uint64_t start[3]; read(fd, start, sizeof(start)); // 目标代码块:这里用sleep模拟耗时操作 sleep(5); // 读取结束值 uint64_t end[3]; read(fd, end, sizeof(end)); // 计算实际能耗(考虑计数器的启用时间) uint64_t energy = end[0] - start[0]; uint64_t enabled = end[1] - start[1]; uint64_t running = end[2] - start[2]; if (enabled > 0) { energy = (energy * enabled) / running; } printf("Package energy consumed: %lu Joules\n", energy); close(fd); return 0; }
编译运行:
$ gcc example.c -o energy_test $ sudo ./energy_test
方法2:用perf record采集后分析
虽然perf stat -p无法直接获取,但可以用perf record附加到进程采集功率事件,事后提取目标阶段的数据:
- 启动程序:
./a.out &,记录PID - 启动采集:
sudo perf record -e power/energy-pkg/ -p <PID> -g - 等程序运行完目标阶段后,按Ctrl+C停止采集
- 分析数据:用
perf script提取事件时间戳,结合程序的阶段时间(比如代码中打印的时间点),计算目标区间的能耗差值。
方法3:结合tracepoint标记阶段
在代码中插入自定义tracepoint,用ftrace或perf跟踪,实现对目标代码块的能耗统计:
- 定义用户自定义tracepoint(需内核支持)
- 在代码的目标段前后触发tracepoint事件
- 启动
perf stat -e power/energy-pkg/ -a,同时跟踪tracepoint,通过脚本统计两个tracepoint之间的能耗差值。
原因说明
power/energy-pkg/和power/energy-ram属于系统级PMU事件,这类计数器是全局统计整个CPU封装或内存的能耗,而非绑定单个进程的上下文。perf stat -p参数主要针对进程上下文的性能事件(如指令数、缓存命中),对全局功率事件不支持,因此会出现<not supported>的提示。
内容的提问来源于stack exchange,提问作者Hamidreza Ramezani
相关产品推荐
相关产品推荐

