性能优化工具-perf实现火焰图
安装依赖
确保已安装 perf 和 FlameGraph 工具。
FlameGraph 通过以下命令下载
git clone https://github.com/brendangregg/FlameGraph.git |
原理
相关格式
调用栈文本格式
使用 sudo perf script > out.perf 生成的格式如下所示
|
文件包含多个块
每个块第一行表示采样执行的函数名,事件,计数,事件
第二行到块末尾表示的是调用栈,里面包含函数的名字,地址,是否内核函数
调用栈折叠文本格式
将采样数据中的调用栈折叠为一行一行的格式,每一行表示一个调用栈及其采样次数。使用 stackcollapse-perf.pl 脚本完成这一步骤:
./FlameGraph/stackcollapse-perf.pl out.perf > out.folded |
折叠后的文件格式如下:
perf-exec;[unknown];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_execve;do_execveat_common.isra.0;bprm_execve;load_elf_binary;begin_new_exec;perf_event_exec;perf_lock_task_context 339 |
- 每一行表示一个调用栈。
- 调用栈中的函数用分号
;分隔。 - 最后一个数字表示该调用栈的采样次数。
调用栈回溯
默认情况下,perf 使用 帧指针(Frame Pointer, FP) 进行调用栈回溯(Call-Graph)。帧指针是一种简单且高效的调用栈回溯方法,但它依赖于程序编译时保留帧指针信息。如果程序没有保留帧指针(例如使用了 -fomit-frame-pointer 编译选项),perf 可能无法正确获取调用栈。
为了应对这种情况,perf 提供了多种调用栈回溯方法,可以通过 --call-graph 参数进行配置。以下是常见的调用栈回溯方法及其配置方式:
帧指针(Frame Pointer, FP)
原理:通过帧指针寄存器(如 x86 架构的
RBP)遍历调用栈。优点:简单、高效。
缺点:依赖程序编译时保留帧指针信息,未使用
fomit-frame-pointer配置:
perf record --call-graph fp -p <PID> -- sleep 10
调试信息(DWARF)
原理:通过调试信息(如
.eh_frame或.debug_frame段)进行调用栈回溯。(编译时使用-g参数)优点:不依赖帧指针,适用于没有帧指针的程序。
缺点:性能开销较大,生成的采样数据较多。
配置:
perf record --call-graph dwarf -p <PID> -- sleep 10
最后分支记录(Last Branch Record, LBR)
原理:利用 CPU 的 LBR 功能记录最近的函数调用路径。
优点:高效、精确。
缺点:仅支持部分 CPU 架构(如 Intel x86)。
配置:
perf record --call-graph lbr -p <PID> -- sleep 10
选择哪种方式
- 当程序保留栈指针的时候,可以用栈指针进行回溯
- 当编译时没有保留栈指针
fomit-frame-pointer,则可以选择DWARF和LBR进行回溯 - 当调用栈层级发生了截断,可以用形如—call-graph fp,1024的方式指定深度
数据采集
使用 perf record 采集性能数据
sudo perf record -F 99 -g -p <PID> -- sleep 60 |
F 99:采样频率为 99 Hz。g:记录调用栈。p <PID>:指定进程 ID。- sleep 60:采集 60 秒。
生成报告
使用 perf script 生成报告。
sudo perf script > out.perf |
生成火焰图
使用 FlameGraph 工具生成火焰图。
./FlameGraph/stackcollapse-perf.pl out.perf > out.folded |
性能优化工具-perf实现火焰图