Zephyr性能分析工具 Perf
概述 链接到标题
Zephyr 项目中实现了Perf用于性能分析的工具,通过栈回溯实现轻量级的性能分析。该工具配置简单,使用方便,适合对代码性能进行快速分析和优化。
工作原理 链接到标题
Perf 工具的工作原理如下:
- 使用
perf record命令启动定时器,定时器通过中断触发 Perf 跟踪函数 - Zephyr 内核在调用中断处理程序之前,会将返回地址和帧指针保存在中断栈或
callee_saved结构中 - Perf 跟踪函数利用这些返回地址和帧指针生成栈回溯
- 可通过
scripts/profiling/stackcollapse.py脚本将栈回溯中的返回地址转换为函数名,并以 FlameGraph 所需格式输出
配置选项 链接到标题
主要配置项 链接到标题
CONFIG_PROFILING: 启用 PROFILINGCONFIG_PROFILING_PERF:启用该模块,并在 shell 中添加perf命令CONFIG_PROFILING_PERF_BUFFER_SIZE:设置 Perf 缓冲区大小,用于存储采样数据,不配置时默认是2048,该值比较小,需要根据要perf的数据量进行修改
后端配置(根据架构自动选择) 链接到标题
CONFIG_PROFILING_PERF_BACKEND_RISCVCONFIG_PROFILING_PERF_BACKEND_X86CONFIG_PROFILING_PERF_BACKEND_X86_64
使用方法 链接到标题
配置步骤 链接到标题
在项目配置文件中添加以下配置:
CONFIG_PROFILING=y
CONFIG_PROFILING_PERF=y
CONFIG_FRAME_POINTER=y
CONFIG_THREAD_STACK_INFO=y
CONFIG_PROFILING_PERF_BUFFER_SIZE=10240
CONFIG_SHELL=y
注意:由于 Zephyr 的 Perf 被集成到 shell 中,因此需要开启 shell。
数据采集 链接到标题
编译完成后,在 shell 中执行 perf 命令进行数据采集:
perf record <duration> <frequency>
参数说明:
<duration>:数据收集的持续时间(毫秒),该时间要覆盖你要分析的性能问题的时间点。<frequency>:采样频率(Hz),频率约高约精确
等待完成消息 perf done!,如果 perf 缓冲区大小小于所需大小,则会出现 perf buf override!。
需要注意的是duration/frequency越大,perf buffer就需要越大,另外也受到调用堆栈深度的影响。
数据处理 链接到标题
- 打印数据:收集完成后,使用以下命令打印 Perf 缓冲区中的数据:
perf printbuf
输出示例:
Perf buf length 6191
0000000000000002
0000000042018a22
0000000042018cb2
0000000000000002
....
-
保存数据:将输出内容复制下来,以文本格式保存(例如文件名为
perf_data) -
下载火焰图工具:
git clone https://github.com/brendangregg/FlameGraph.git
- 生成火焰图:
python zephyr/scripts/profiling/stackcollapse.py perf_data build/zephyr/zephyr.elf | ~/workspace/tools/FlameGraph/flamegraph.pl > graph2.svg
这个演示结果可以看到采样期间sys_clock_cycle_get_32调用的次数最多,而其呼叫的两个函数占用次数相当。如果这段出现了性能问题,可能是这两个函数的执行时间过长。
实现原理 链接到标题
核心机制 链接到标题
Perf利用中断机制对执行过程进行“采样”,在采样时刻捕获返回ra和sp,和当时的栈回溯信息。使用脚本将其转换为函数名,以通过火焰图将其以可视化形式展示。

采样起始时间(end-start = duration),决定了要Perf的内容,例如你发现在特定时间点出现性能问题,可以将起始时间放在出性能问题的时间点附近。采样频率决定了你收集数据的完整性,频率越高,数据越完整。从上图可以看到频率不够时会漏采样线程A中FA1()调用FB1()。因此Perf是统计学意义上的性能分析,并不是精确的性能分析。
代码结构 链接到标题
zephyr/subsys/profiling/perf/perf.c 提供了框架代码,以 shell 命令的形式进行调用:
-
cmd_perf_record:
- 按照 frequency 创建 k_timer,周期性地进入中断
- 在中断中执行 perf_tracer,perf_tracer 调用 arch_perf_current_stack_trace 获取当前线程的 backtrace,并存放到 perf_data.buf 内
- perf_tracer 发现 perf_data.buf 满后停止 timer
- 按照 duration 创建 k_work,k_work 到期后调用 perf_dwork_handler 停止 timer
-
cmd_perf_clear:
- 抓取数据过程中不能清除,perf 结束后将 perf_data.buf 清空,perf_data.idx = 0
-
cmd_perf_info:
- 显示 perf_data.buf 的信息:总计多少,已经有多少数据
-
cmd_perf_print:
- 将记录的数据打印出来,调用序列是依次打印fp地址,每个调用序列之间用
0000000000000002分隔
- 将记录的数据打印出来,调用序列是依次打印fp地址,每个调用序列之间用
数据转换 链接到标题
zephyr/scripts/profiling/stackcollapse.py 脚本将 perf 记录的fp数据从elf查找出符号并转换为 FlameGraph 所需格式。
火焰图脚本flamegraph.pl将文本格式转换成svg,通过svg可以可视化展示调用序列和其频繁度。
架构相关实现 链接到标题
不同的架构 arch_perf_current_stack_trace 实现会不同,都集中在 zephyr/subsys/profiling/perf/backends 下:
perf_riscv.cperf_x86_64.cperf_x86.c
参考 链接到标题
https://docs.zephyrproject.org/latest/services/profiling/perf.html https://lgl88911.github.io/2020/03/19/Perf%E5%92%8C%E7%81%AB%E7%84%B0%E5%9B%BE/