登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

perf stat -j 怎样导出硬件计数器 JSON

来源:17golang原创

时间:2026-11-07 00:11:23 333浏览 收藏

如果只在终端看 perf stat,表格很直观;一旦要把 cycles、instructions 或缓存事件交给脚本,按空格切列就会很脆弱。更稳妥的做法是使用 -j 输出 JSON,再根据字段名处理。

直接答案是:在 perf stat 后加入 -j,并注意统计结果通常写到标准错误。例如:

# -j 输出机器可读记录;2> 把 perf stat 的结果保存为 JSON 文件
perf stat -j -e cycles,instructions -- ./bin/demo 2> perf-stat.json

官方地址:https://man7.org/linux/man-pages/man1/perf-stat.1.html

这篇文章只讨论 JSON 输出与字段后处理,不把 -j 当成线程聚合或 CPU 核心聚合选项。输出格式变得稳定,并不代表硬件事件一定能同时运行或一定有权限访问。

先分清 perf stat JSON 的输出边界

perf stat 负责读取性能事件并打印统计信息,默认的人类可读表格适合快速查看,却不适合让程序依赖列位置。-j 把每条统计记录表达成 JSON 字段,后处理脚本就可以按键名取值。

另一个容易漏掉的点是输出流。很多 perf 统计结果会写到标准错误,所以只用 1> perf-stat.json 可能得到空文件,正确做法是同时重定向 stderr,或者使用 perf 提供的输出选项。

perf stat 事件集合与 JSON 记录之间的静态结构关系说明图
图1:perf stat 的事件集合、采集器与 JSON 记录之间的静态关系;这是原创说明图,不是终端截图。

图中把“事件定义”和“输出记录”分开看:JSON 只描述采集结果的字段,不替你决定应该测量哪个事件,也不保证不同处理器都支持同名事件。

用 -j 导出硬件计数器记录

最小命令可以从两个固定事件开始:

# 选择两个常见硬件事件,并把被测程序放在 -- 后面
perf stat -j -e cycles,instructions -- ./bin/demo 2> perf-stat.json

# 只查看 JSON 文件中的前几行,避免把它误当成普通表格解析
sed -n '1,5p' perf-stat.json

cycles 和 instructions 是事件名,-- 用来分隔 perf 参数与被测命令。若要统计一个已经存在的进程,可以改用 -p PID;若关注某个线程,则应使用与目标 TID 对应的参数,但这属于监控对象选择,不是 JSON 格式本身。

如果一次加入太多事件,内核可能需要在硬件计数器之间复用它们。此时 JSON 仍然可以生成,但结果中的运行比例会提醒你该事件并非全程占用计数器。

按字段解释 JSON 记录

常见记录可以按下面几组字段理解:

字段用途处理建议
event事件名称作为结果的稳定标识,不要依赖输出顺序
counter-value计数值读取前先确认不是空值或不可用标记
unit显示单位与计数值一起保存,避免跨事件误比较
event-runtime事件实际运行时间用于判断统计覆盖范围
pcnt-running事件运行占比低于 100% 时要考虑复用和调度影响

开启间隔统计、重复运行或其它聚合选项时,记录还可能带有时间、方差或聚合标识。后处理代码应允许字段缺失,不要把所有行都强制转换成同一种业务对象。

perf stat JSON 记录中事件、计数值和运行比例字段的静态关系图
图2:JSON 记录的核心计数、上下文字段和后处理关注点;这是字段关系示意图,不是运行结果截图。

交给 jq 或脚本做稳定后处理

如果只想提取有效的事件记录,可以先过滤具有 event 的对象,再读取计数值和运行比例:

# 过滤非事件行,并以制表符输出便于继续进入脚本或表格
jq -r 'select(.event != null) | [.event, ."counter-value", .unit, ."pcnt-running"] | @tsv' \
  perf-stat.json

这里没有把 JSON 转回“按列切分”的文本,而是保留字段语义。生产脚本还应区分三种情况:事件存在且有计数值、事件存在但计数值不可用、以及这一行只是额外的摘要或上下文信息。

import json
from pathlib import Path

# 只接收有事件名的记录,并保留运行比例供后续判断
records = []
for line in Path("perf-stat.json").read_text(encoding="utf-8").splitlines():
    try:
        item = json.loads(line)
    except json.JSONDecodeError:
        # 日志中若混入非 JSON 行,跳过并交给上层记录告警
        continue
    if item.get("event") is not None:
        records.append({
            "event": item.get("event"),
            "value": item.get("counter-value"),
            "unit": item.get("unit"),
            "running": item.get("pcnt-running"),
        })

代码故意保留 None,因为“字段缺失”和“计数值为零”不是同一件事。若业务要比较两个事件,还应先统一单位并检查运行比例,而不是只比较数值大小。

处理事件组与硬件限制

JSON 解决的是输出表达,不会绕过 PMU 的硬件限制。加入的事件太多时,perf 会进行时间复用;事件组则要求相关事件尽量一起调度。若记录中的 pcnt-running 较低,说明该事件并没有在整个测量期间运行,结果应标记为“需要谨慎解释”。

# 用事件组表达需要尽量同时调度的两个事件,并保留 JSON 输出
perf stat -j -e '{cycles,instructions}' -- ./bin/demo 2> grouped.json

# 记录命令本身,便于把事件集合与 JSON 结果一起归档
printf '%s\n' 'perf stat -j -e {cycles,instructions} -- ./bin/demo' > grouped.command.txt

另外,权限、内核配置、虚拟机环境和处理器型号都可能影响事件是否可用。出现权限错误或某个事件不可用时,应先处理环境与事件选择,不要把问题误归咎于 JSON 解析。

几个容易踩到的边界

现象原因处理方式
重定向文件为空只重定向了 stdout使用 2> 接收统计输出
脚本偶尔取不到值把摘要行当成事件行先判断 event 是否存在
计数值看起来偏小事件复用或运行比例不足结合 pcnt-running 与事件数量判断
不同机器结果不可比硬件事件和单位存在差异保存 CPU、命令和事件集合元数据

总结:把 JSON 当作带边界的结果接口

perf stat -j 适合把硬件计数器结果交给脚本:先正确接收标准错误,再按字段名读取 event、counter-value 与 pcnt-running。后处理时允许字段缺失,测量解释时同时关注事件复用、权限和处理器差异。

最重要的边界是:JSON 只让输出更适合机器读取,不会让硬件事件自动变得可用,也不会替代对测量覆盖率的判断。

相关问题

为什么 perf stat -j 生成的文件不是标准单个 JSON 数组?

perf stat 的 JSON 输出通常按记录逐行表达,便于持续输出和增加上下文字段。读取时可以按行解析,不要先假设整个文件一定包在数组中。

pcnt-running 低于 100% 还能比较吗?

可以作为线索,但不能直接当作同等可信的完整测量。先减少事件数量或使用合理的事件组,再结合运行比例解释结果。

只想看某个线程时要改 JSON 解析吗?

通常不用。监控对象由 perf 的进程或线程参数决定,JSON 解析仍按字段名处理;若开启聚合选项,则额外保存对应的线程标识。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>