Linux C/C++服务器
火焰图的生成原理与构建方式
1 火焰图简介
火焰图能做什么:
- 可以分析函数执行的频繁程度
- 可以分析哪些函数经常阻塞
- 可以分析哪些函数频繁分配内存
Flame Graph 原图路径:https://queue.acm.org/downloads/2016/Gregg4.svg
每一列代表一个调用栈,每一个格子代表一个函数
- 纵轴展示了栈的深度,按照调用关系从下到上排列。最顶上格子代表采样时,正在占用 cpu 的函数。
- 横轴的意义是指:火焰图将采集的多个调用栈信息,通过按字母横向排序的方式将众多信息聚合在一起。需要注意的是它并不代表时间。
- 横轴格子的宽度代表其在采样中出现频率,所以一个格子的宽度越大,说明它是瓶颈原因的可能性就越大。
- 火焰图格子的颜色是随机的暖色调,方便区分各个调用信息。
- 其他的采样方式也可以使用火焰图, on-cpu 火焰图横轴是指 cpu 占用时间,off-cpu 火焰图横轴则代表阻塞时间。
- 采样可以是单线程、多线程、多进程甚至是多 host,进阶用法可以参考附录进阶阅读。
官方博客:https://www.brendangregg.com/flamegraphs.html,火焰图的源资料皆出自该博客。
该博主名称:Brendan Gregg,著作有:
性能之巅:洞悉系统、企业与云计算
BPF之巅:洞悉Linux系统和应用性能
1.1 火焰图的类型
常见的火焰图类型有 On-CPU,Off-CPU,还有 Memory,Hot/Cold,Differential 等等。它们有各自适合处理的场景。
什么时候使用 On-CPU 火焰图? 什么时候使用 Off-CPU 火焰图呢?
取决于当前的瓶颈到底是什么:
- 如果是 CPU 则使用 On-CPU 火焰图,(先看cpu是不是快到百分百),
- 如果是 IO 或锁则使用 Off-CPU 火焰图(如果cpu占用率不高,就需要用off-cpu).
- 如果无法确定, 那么可以通过压测工具来确认:
- 通过压测工具看看能否让 CPU 使用率趋于饱和, 如果能那么使用 On-CPU 火焰图
- 如果不管怎么压, CPU 使用率始终上不来, 那么多半说明程序被 IO 或锁卡住了, 此时适合使用 Off-CPU 火焰图.
- 如果还是确认不了, 那么不妨 On-CPU 火焰图和 Off-CPU 火焰图都搞搞, 正常情况下它们的差异会比较大, 如果两张火焰图长得差不多, 那么通常认为 CPU 被其它进程抢占了.
1.2 火焰图分析技巧
- 纵轴代表调用栈的深度(栈桢数),用于表示函数间调用关系:下面的函数是上面函数的父函数。
- 横轴代表调用频次,一个格子的宽度越大,越说明其可能是瓶颈原因。
- 不同类型火焰图适合优化的场景不同,比如 on-cpu 火焰图适合分析 cpu 占用高的问题函数,off-cpu 火焰图适合解决阻塞和锁抢占问题。
- 无意义的事情:横向先后顺序是为了聚合,跟函数间依赖或调用关系无关;火焰图各种颜色是为方便区分,本身不具有特殊含义
- 多练习:进行性能优化有意识的使用火焰图的方式进行性能调优(如果时间充裕)
2.火焰图环境搭建
2.1 生成火焰图的三个步骤
2.2安装火焰图必备工具
Brendan D. Gregg 的 Flame Graph 工程实现了一套生成火焰图的脚本。Flame Graph 项目位于 GitHub上
不同的 trace 工具抓取到的信息不同, 因此 Flame Graph 提供了一系列的 stackcollapse 工具.
2.3 安装火焰图数据采集工具perf
系统级性能优化通常包括两个阶段:性能剖析(performance profiling)和代码优化。性能剖析的目标是寻找性能瓶颈,查找引发性能问题的原因及热点代码。代码优化的目标是针对具体性能问题而优化代码或编译选项,以改善软件性能。一般在工作中比较关心的是性能瓶颈,特别是算法。
当在系统全功能启动的时候,算法一般需要将设备的性能用到极限,而在这个过程中不免出现各类性能上的瓶颈,此时需要分析自身的一些性能瓶颈在什么地方就可以用到专门的性能分析工具perf。
perf 命令(performance profiling的缩写), 它是 Linux 系统原生提供的性能分析工具, 会返回 CPU 正在执行的函数名以及调用栈(stack)
perf的原理是这样的:每隔一个固定的时间,就在CPU上(每个核上都有)产生一个中断,在中断上看看,当前是哪个pid,哪个函数,然后给对应的pid和函数加一个统计值,这样,我们就知道CPU有百分几的时间在某个pid,或者某个函数上了。这个原理图示如下:
1秒采集99次,1秒采集1000次
安装perf
安装需要root权限,perf 采集的时候也需要root的权限
# apt install linux-tools-common
测试perf是否可用
# perf record -F 99 -a -g -- sleep 10
perf常用命令
- perf list:查看当前软硬件环境支持的性能事件
- perf stat:分析指定程序的性能概况
- perf top:实时显示系统/进程的性能统计信息
- perf record:记录一段时间内系统/进程的性能事件perf report:读取perf record生成的perf.data文件,并显示分析数据(生成火焰图用的采集命令)
perf record -h 常用命令
-e :指定性能事件(可以是多个,用,分隔列表)
-p :指定待分析进程的 pid(可以是多个,用,分隔列表)
-t :指定待分析线程的 tid(可以是多个,用,分隔列表)
-u :指定收集的用户数据,uid为名称或数字
-a:从所有 CPU 收集系统数据
-g:开启 call-graph (stack chain/backtrace) 记录
-C :只统计指定 CPU 列表的数据,如:0,1,3或1-2
-r :perf 程序以SCHED_FIFO实时优先级RT priority运行这里填入的数值越大,进程优先级越高(即 nice 值越小)
-c : 事件每发生 count 次采一次样
-F :每秒采样 n 次
-o <output.data>:指定输出文件output.data,默认输出到perf.data
3 生成火焰图实践
3.1 编译执行测试程序
src-flamegraph\test\test.c
#include <stdio.h>
void func_d()
{
for (int i = 5 * 10000; i--;);
}
void func_a()
{
for (int i = 10 * 10000; i--;);
func_d();
}
void func_b()
{
for (int i = 20 * 10000; i--;);
}
void func_c()
{
for (int i = 35 * 10000; i--;);
}
int main(void)
{
printf("main into\n");
while (1)
{
for (int i = 30 * 10000; i--;);
func_a();
func_b();
func_c();
}
printf("main end\n");
return 0;
}
该程序用于生成测试程序。
编译:gcc -o test test.c
执行:./test
3.2 perf 采集数据
通过top指令查看test的pid。
# perf record -F 99 -p 5000 -g -- sleep 30
perf record 表示采集系统事件, 没有使用 -e 指定采集事件, 则默认采集 cycles(即 CPU clock 周期), -F 99 表示每秒99 次, -p 5000是进程号, 即对哪个进程进行分析, -g 表示记录调用栈, sleep 30 则是持续 30 秒.
-F 指定采样频率为 99Hz(每秒99次), 如果 99次 都返回同一个函数名, 那就说明 CPU 这一秒钟都在执行同一个函数,可能存在性能问题
运行后会产生一个庞大的文本文件. 如果一台服务器有 16 个 CPU, 每秒抽样 99 次, 持续 30 秒, 就得到 47,520 个调用栈, 长达几十万甚至上百万行.为了便于阅读, perf record 命令可以统计每个调用栈出现的百分比, 然后从高到低排列.
# perf report -n --stdio
3.3 折叠堆栈
首先用perf script 工具对 perf.data 进行解析
# 生成折叠后的调用栈
perf script -i perf.data &> perf.unfold
然后将解析出来的信息存下来, 供生成火焰图
用 stackcollapse-perf.pl 将 perf 解析出的内容 perf.unfold 中的符号进行折叠 :
# 生成火焰图需要的统计信息
# ./FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded
3.4 生成火焰图
最后生成 svg 图-绘制火焰图
# ./FlameGraph/flamegraph.pl perf.folded > test_oncpu.svg
我们也可以使用管道将上面的流程简化为一条命令
# perf script | ./FlameGraph/stackcollapse-perf.pl | ./FlameGraph/flamegraph.pl > test_oncpu.svg
3.5 解析火焰图含义
最后就可以用浏览器打开火焰图进行分析啦.
火焰图是基于 stack 信息生成的 SVG 图片, 用来展示 CPU 的调用栈。
- y 轴表示调用栈, 每一层都是一个函数. 调用栈越深, 火焰就越高, 顶部就是正在执行的函数, 下方都是它的父函数.
- x 轴表示抽样数, 如果一个函数在 x 轴占据的宽度越宽, 就表示它被抽到的次数多, 即执行的时间长. 注意, x 轴不代表时间, 而是所有的调用栈合并后, 按字母顺序排列的.
- 火焰图就是看顶层的哪个函数占据的宽度最大. 只要有 “平顶”(plateaus), 就表示该函数可能存在性能问题。
- 颜色没有特殊含义, 因为火焰图表示的是 CPU 的繁忙程度, 所以一般选择暖色调.