You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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附加到进程采集功率事件,事后提取目标阶段的数据:

  1. 启动程序:./a.out &,记录PID
  2. 启动采集:sudo perf record -e power/energy-pkg/ -p <PID> -g
  3. 等程序运行完目标阶段后,按Ctrl+C停止采集
  4. 分析数据:用perf script提取事件时间戳,结合程序的阶段时间(比如代码中打印的时间点),计算目标区间的能耗差值。

方法3:结合tracepoint标记阶段

在代码中插入自定义tracepoint,用ftrace或perf跟踪,实现对目标代码块的能耗统计:

  1. 定义用户自定义tracepoint(需内核支持)
  2. 在代码的目标段前后触发tracepoint事件
  3. 启动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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 06:23:32