头图

01 前言

软件同学在进行性能分析时,通常需要查看 CPU 耗时,用于了解性能瓶颈在哪里,从而进行针对性的优化。火焰图(Flame Graph)是常用的性能分析工具。总结来看,火焰图通常用于以下场景:

  • 代码性能调优:发现和优化代码的性能瓶颈。
  • 资源使用监控:在生产环境中监控应用程序的资源使用情况。
  • 故障排查:定位异常情况,例如 CPU 使用率突增的原因。

火焰图能够显示代码的调用堆栈、各个函数的执行频率以及它们之间的调用关系。整个图形看起来就像一个跳动的火焰,这就是它名字的由来。

火焰图有以下特征:

  • 纵轴:表示函数调用堆栈的深度,每一列代表一个调用栈,每一个格子代表一个函数。最顶上格子代表采样时,正在占用 cpu 的函数。纵轴也展示了栈的深度,按照调用关系从下到上排列。
  • 横轴:火焰图将采集的多个调用栈信息,通过按字母横向排序的方式将众多信息聚合在一起。如果一个函数在 x 轴占据的宽度越宽,就表示它被抽到的次数多,即执行的时间长。需要注意:横轴不代表时间。
  • 颜色:颜色是随机的暖色调,方便区分不同的线程、代码模块或特定函数类型,没有其他特殊含义。
  • 瓶颈:顶层的哪个函数占据的宽度最大,通常瓶颈就发生在这个"平顶"。

02 火焰图理解示例

本节参考:https://www.ruanyifeng.com/blog/2017/09/flame-graph.html

下面是一个用于帮助理解火焰图怎么形成的小例子。
首先,CPU 抽样得到了三个调用栈。

func_c 
func_b 
func_a 
start_thread 

func_d 
func_a 
start_thread 

func_d 
func_a 
start_thread

上面代码中,start_thread 是启动线程,调用了 func_a。后者又调用了 func_b 和 func_d,而 func_b 又调用了 func_c。经过合并处理后,得到了下面的结果,即存在两个调用栈,第一个调用栈抽中 1 次,第二个抽中 2 次。

start_thread;func_a;func_b;func_c 1 
start_thread;func_a;func_d 2

有了这个调用栈统计,火焰图工具就能生成 SVG 图片。
图片
上面图片中,最顶层的函数 g()占用 CPU 时间最多。d()的宽度最大,但是它直接耗用 CPU 的部分很少。b()和 c()没有直接消耗 CPU。因此,如果要调查性能问题,首先应该调查 g(),其次是 i()。
另外,从图中可知 a()有两个分支 b()和 h(),这表明 a()里面可能有一个条件语句,而 b()分支消耗的 CPU 大大高于 h()。

不知道有没有这样的疑问:为什么上数第二层 e()+f()的总宽度小于下层 d()的总宽度?
因为不能有无父之子,下一层父宽度必大于等于上一层子宽度。
整个火焰图是纵向 n 条调用栈、然后做横向合并而来,合并得到的宽度代表了调用栈中的栈片段重复情况。调用栈是一个非常强调父子关系的数据结构,如:
a->b->c 的调用,在这“一个”调用栈上,不可能[父 b]被调用了 3 次而[子 c]被调用了 4 次。
但[子 c]可以调用 3 次,[父 b]被调用了 4 次:这代表了 a->b->c 的调用了出现了 3 次,a->b 出现了 1 次。

03 火焰图如何查看

生成火焰图通常需要两步:

  1. 收集性能数据:使用性能分析工具(如 perf、BPF、dtrace 等)收集 CPU 采样数据,捕获每个函数的调用堆栈和消耗时间。
  2. 生成火焰图:通过工具将采集的数据转换为火焰图。例如,Flamegraph.pl 是一个 Perl 脚本,可以解析 perf 的输出数据并生成 SVG 格式的火焰图。用浏览器打开 svg 格式的图片,否则无法用鼠标点击查看每个方块内详细信息;

在线火焰图示例:https://queue.acm.org/downloads/2016/Gregg4.svg

(1)鼠标悬浮
火焰的每一层都会标注函数名,鼠标悬浮时会显示完整的函数名、抽样抽中的次数、占据总抽样次数的百分比。
(2)点击放大
在某一层点击,火焰图会水平放大,该层会占据所有宽度,显示详细信息。左上角会同时显示"Reset Zoom",点击该链接,图片就会恢复原样。
(3)搜索
Ctrl + F 会显示一个搜索框,可以输入关键词或正则表达式,所有符合条件的函数名会高亮显示。
图片

04 征程 6 火焰图实例

4.1 检查环境

检查系统内核是否开启 CONFIG_PERF_EVENTS,在 征程 5 上可以运行以下指令进行检测

sysctl -a |grep -i "perf"

开启状态下会有以下结果,如果是关闭状态则需要编译内核镜像开启 perf。

kernel.perf_cpu_time_max_percent = 25
kernel.perf_event_max_contexts_per_stack = 8
kernel.perf_event_max_sample_rate = 100000
kernel.perf_event_max_stack = 127
kernel.perf_event_mlock_kb = 576
kernel.perf_event_paranoid = 2
kernel.perf_user_access = 0

4.2 工具使用

采样:

# -p pid 进程号,找到需要分析的进程id
# -- sleep 采样时长 s, perf.data 一般较大,运行需要放在空间较大的目录,不要运行过久
# -a 所有 cpu 信息
# -g 得到函数调用关系
perf record -p 11229 -ag -- sleep 10

处理采样结果,生成火焰图所需的 perf.unfold 文件:

perf script -i perf.data > perf.unfold

绘图:

获取绘图工具:https://github.com/brendangregg/FlameGraph.git,下载到开发机上,文件夹中会有对应的工具

运行如下两行命令,最后生成的 svg 文件即为火焰图.

./FlameGraph/stackcollapse-perf.pl ./perf.unfold > perf.folded
./FlameGraph/flamegraph.pl perf.folded > perf.svg

image.png

如果想更深入了解火焰图,可以参考:http://www.brendangregg.com/perf.html#FlameGraphs


地平线智驾开发者
1 声望2 粉丝

地平线智能驾驶开发者社区旨在连接智能驾驶领域的开发者和对相关技术感兴趣的其他行业开发者、从业者。