1. 前言
软件同学在进行性能分析时,通常需要查看 CPU 耗时,用于了解性能瓶颈在哪里,从而进行针对性的优化。火焰图(Flame Graph)是常用的性能分析工具。总结来看,火焰图通常用于以下场景:
代码性能调优:发现和优化代码的性能瓶颈。
资源使用监控:在生产环境中监控应用程序的资源使用情况。
故障排查:定位异常情况,例如 CPU 使用率突增的原因。
火焰图能够显示代码的调用堆栈、各个函数的执行频率以及它们之间的调用关系。整个图形看起来就像一个跳动的火焰,这就是它名字的由来。
火焰图有以下特征:
- 纵轴:表示函数调用堆栈的深度,每一列代表一个调用栈,每一个格子代表一个函数。最顶上格子代表采样时,正在占用 cpu 的函数。纵轴也展示了栈的深度,按照调用关系从下到上排列。
- 横轴:火焰图将采集的多个调用栈信息,通过按字母横向排序的方式将众多信息聚合在一起。如果一个函数在 x 轴占据的宽度越宽,就表示它被抽到的次数多,即执行的时间长。需要注意:横轴不代表时间。
- 颜色:颜色是随机的暖色调,方便区分不同的线程、代码模块或特定函数类型,没有其他特殊含义。
- 瓶颈:顶层的哪个函数占据的宽度最大,通常瓶颈就发生在这个"平顶"。
2. 火焰图理解示例
下面是一个用于帮助理解火焰图怎么形成的小例子。
首先,CPU 抽样得到了三个调用栈。
首先,CPU 抽样得到了三个调用栈。
上面代码中,start_thread是启动线程,调用了func_a。后者又调用了func_b和func_d,而func_b又调用了func_c。
经过合并处理后,得到了下面的结果,即存在两个调用栈,第一个调用栈抽中1次,第二个抽中2次。
经过合并处理后,得到了下面的结果,即存在两个调用栈,第一个调用栈抽中1次,第二个抽中2次。
有了这个调用栈统计,火焰图工具就能生成 SVG 图片。

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

上面图片中,最顶层的函数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次。
因为不能有无父之子,下一层父宽度必大于等于上一层子宽度。
整个火焰图是纵向n条调用栈、然后做横向合并而来,合并得到的宽度代表了调用栈中的栈片段重复情况。调用栈是一个非常强调父子关系的数据结构,如:
a->b->c的调用,在这“一个”调用栈上,不可能[父b]被调用了3次而[子c]被调用了4次。
但[子c]可以调用3次,[父b]被调用了4次:这代表了a->b->c的调用了出现了3次,a->b出现了1次。
3. 火焰图如何查看
生成火焰图通常需要两步:
收集性能数据:使用性能分析工具(如 perf、BPF、dtrace 等)收集 CPU 采样数据,捕获每个函数的调用堆栈和消耗时间。
生成火焰图:通过工具将采集的数据转换为火焰图。例如,Flamegraph.pl 是一个 Perl 脚本,可以解析 perf 的输出数据并生成 SVG 格式的火焰图。用浏览器打开svg格式的图片,否则无法用鼠标点击查看每个方块内详细信息;
(1)鼠标悬浮
火焰的每一层都会标注函数名,鼠标悬浮时会显示完整的函数名、抽样抽中的次数、占据总抽样次数的百分比。
(2)点击放大
在某一层点击,火焰图会水平放大,该层会占据所有宽度,显示详细信息。
左上角会同时显示"Reset Zoom",点击该链接,图片就会恢复原样。
(3)搜索
Ctrl + F 会显示一个搜索框,可以输入关键词或正则表达式,所有符合条件的函数名会高亮显示。

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

4. J6 火焰图实例
4.1 检查环境
检查系统内核是否开启CONFIG_PERF_EVENTS,在J5上可以运行以下指令进行检测
开启状态下会有以下结果,如果是关闭状态则需要编译内核镜像开启perf
4.2 工具使用
采样:
处理采样结果,生成火焰图所需的perf.unfold文件:
绘图:
运行如下两行命令,最后生成的svg文件即为火焰图

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

