如何生成火焰图

如何使用perf工具和火焰图工具生成火焰图

火焰图仅用一张小图,就可以定量展示所有的性能瓶颈的全景图,而不论目标软件有多么复杂。
传统的性能分析工具通常会给用户展示大量的细节信息和数据, 而用户很难看到全貌,反而容易去优化那些并不重要的地方,经常浪费大量时间和精力却看不到明显效果。传统分析器的另一个缺点是,它们通常会孤立地显示每个函数调用的延时,但很难看出各个函数调用的上下文,而且用户还须刻意区分当前函数本身运行的时间(exclusive time)和包括了其调用其他函数的时间在内的总时间(inclusive time)。

而相比之下,火焰图可以把大量信息压缩到一个大小相对固定的图片当中(通常一屏就可以显示全)。 不怎么重要的代码路径会在图上自然地淡化乃至消失,而真正重要的代码路径则会自然地凸显出来。越重要的,则会显示得越明显。火焰图总是为用户提供最适当的信息量,不多,也不少。

1 火焰图简介

官方博客:https://www.brendangregg.com/flamegraphs.html,火焰图的源资料皆出自该博客。

火焰图能做什么:

  • 可以分析函数执行的频繁程度
  • 可以分析哪些函数经常阻塞
  • 可以分析哪些函数频繁分配内存

以分析程序的性能瓶颈。

火焰图整个图形看起来就像一个跳动的火焰,这就是它名字的由来。
火焰图有以下特征(这里以 on-cpu 火焰图为例):

  • 每一列代表一个调用栈,每一个格子代表一个函数
  • 纵轴展示了栈的深度,按照调用关系从下到上排列。最顶上格子代表采样时,正在占用 cpu 的函数。
  • 横轴的意义是指:火焰图将采集的多个调用栈信息,通过按字母横向排序的方式将众多信息聚合在一起。需要注意的是它并不代表时间。
  • 横轴格子的宽度代表其在采样中出现频率,所以一个格子的宽度越大,说明它是瓶颈原因的可能性就越大。
  • 火焰图格子的颜色是随机的暖色调,方便区分各个调用信息。
  • 其他的采样方式也可以使用火焰图, on-cpu 火焰图横轴是指 cpu 占用时间,off-cpu 火焰图横轴则代表阻塞时间。
  • 采样可以是单线程、多线程、多进程甚至是多 host,进阶用法可以参考附录进阶阅读。

1.1 火焰图类型

常见的火焰图类型有 On-CPU,Off-CPU,还有 Memory,Hot/Cold,Differential 等等。它们有各自适合处理的场景。

火焰图类型 横轴含义 纵轴含义 解决问题 采样方式
on-cpu火焰图 cpu占用时间 调用栈 找出cpu占用搞的问题函数;分析代码热路径 固定频率采样cpu调用栈
off-cpu火焰图 阻塞时间 调用栈 i/o、网络等阻塞场景导致的性能下降;锁竞争、死锁导致的性能下降问题 固定频率采样阻塞事件调用栈
内存火焰图 内存申请/释放函数调用次数 调用栈 内存泄漏问题;内存占用高的对象/申请内存多的函数;虚拟内存或物理内存泄漏问题 有四种方式:跟踪malloc/free;跟踪brk;跟踪mmap;跟踪页错误
Hot/Cold火焰图 on-cpu和off-cpu综合展示 调用栈 需要结合cpu占用以及阻塞分析的场景;off-cpu火焰图无法直观判断的场景 on-cpu火焰图和off-cpu火焰图结合

1.2 什么时候使用 On-CPU 火焰图? 什么时候使用 Off-CPU 火焰图呢?

取决于当前的瓶颈到底是什么:

  • 如果是 CPU 则使用 On-CPU 火焰图,
  • 如果是 IO 或锁则使用 Off-CPU 火焰图.
  • 如果无法确定, 那么可以通过压测工具来确认:
  • 通过压测工具看看能否让 CPU 使用率趋于饱和, 如果能那么使用 On-CPU 火焰图
  • 如果不管怎么压, CPU 使用率始终上不来, 那么多半说明程序被 IO 或锁卡住了, 此时适合使用 Off-CPU 火焰图.
  • 如果还是确认不了, 那么不妨 On-CPU 火焰图和 Off-CPU 火焰图都搞搞, 正常情况下它们的差异会比较大, 如果两张火焰图长得差不多, 那么通常认为 CPU 被其它进程抢占了

1.3 火焰图分析技巧

  1. 纵轴代表调用栈的深度(栈桢数),用于表示函数间调用关系:下面的函数是上面函数的父函数。
  2. 横轴代表调用频次,一个格子的宽度越大,越说明其可能是瓶颈原因。
  3. 不同类型火焰图适合优化的场景不同,比如 on-cpu 火焰图适合分析 cpu 占用高的问题函数,off-cpu 火焰图适合解决阻塞和锁抢占问题。
  4. 无意义的事情:横向先后顺序是为了聚合,跟函数间依赖或调用关系无关;火焰图各种颜色是为方便区分,本身不具有特殊含义
  5. 多练习:进行性能优化有意识的使用火焰图的方式进行性能调优(如果时间充裕)

2 如何绘制火焰图?

2.1 生成火焰图的流程

Brendan D. Gregg 的 Flame Graph 工程实现了一套生成火焰图的脚本。Flame Graph 项目位于 GitHub上
https://github.com/brendangregg/FlameGraph

当GitHub网络不通畅的时候可以使用码云的链接:
git clone https://gitee.com/mirrors/FlameGraph.git

用 git 将其 clone下来

生成和创建火焰图需要如下几个步骤

流程 描述 脚本
捕获堆栈 使用 perf/systemtap/dtrace 等工具抓取程序的运行堆栈 perf/systemtap/dtrace
折叠堆栈 trace 工具抓取的系统和程序运行每一时刻的堆栈信息, 需要对他们进行分析组合, 将重复的堆栈累计在一起, 从而体现出负载和关键路径 FlameGraph 中的 stackcollapse 程序
生成火焰图 分析 stackcollapse 输出的堆栈信息生成火焰图 flamegraph.pl

不同的 trace 工具抓取到的信息不同, 因此 Flame Graph 提供了一系列的 stackcollapse 工具.

stackcollapse 描述
stackcollapse.pl for DTrace stacks
stackcollapse-perf.pl for Linux perf_events “perf script” output
stackcollapse-pmc.pl for FreeBSD pmcstat -G stacks
stackcollapse-stap.pl for SystemTap stacks
stackcollapse-instruments.pl for XCode Instruments
stackcollapse-vtune.pl for Intel VTune profiles
stackcollapse-ljp.awk for Lightweight Java Profiler
stackcollapse-jstack.pl for Java jstack(1) output
stackcollapse-gdb.pl for gdb(1) stacks
stackcollapse-go.pl for Golang pprof stacks
stackcollapse-vsprof.pl for Microsoft Visual Studio profiles

查看帮助
./FlameGraph/flamegraph.pl -h

2.2 安装 perf工具

perf 命令(performance 的缩写)讲起, 它是 Linux 系统原生提供的性能分析工具, 会返回 CPU 正在执行的函数名以及调用栈(stack)

具体用法:

2.2.1 安装perf

ubuntu下

1
# apt install linux-tools-common

centos下

1
yum install perf

2.2.2 测试perf是否可用

1
# perf record -F 99 -a -g -- sleep 10    

如果报错

WARNING: perf not found for kernel 4.15.0-48

You may need to install the following packages for this specific kernel:
linux-tools-4.15.0-48-generic
linux-cloud-tools-4.15.0-48-generic

You may also want to install one of the following packages to keep up to date:
linux-tools-generic
linux-cloud-tools-generic

apt install linux-tools-generic
apt install linux-cloud-tools-generic

则需要安装linux-tools-generic和linux-cloud-tools-generic,但需选择对应的版本,比如提示的是4.15.0-48,则我们安装4.15.0-48版本

# apt-get install linux-tools-4.15.0-48-generic linux-cloud-tools-4.15.0-48-generic linux-tools-generic linux-cloud-tools-generic

再次测试

1
# perf record -F 99 -a -g -- sleep 10    

如果没有报错则在执行的目录产生perf.data

2.2.3 perf常用命令

查看帮助文档,perf功能非常强大,我们这里只关注record和report功能,record和report也可以继续通过二级命令查询帮助文档。

perf -h

常用的五个命令:

  • perf list:查看当前软硬件环境支持的性能事件
  • perf stat:分析指定程序的性能概况
  • perf top:实时显示系统/进程的性能统计信息
  • perf record:记录一段时间内系统/进程的性能事件perf report:读取perf record生成的perf.data文件,并显示分析数据(生成火焰图用的采集命令)
  • perf report:交互式命令查看资源使用情况

2.3 perf 采集数据

1
perf record -F 99 -p 3887 -g -- sleep 30

perf record 表示采集系统事件, 没有使用 -e 指定采集事件, 则默认采集 cycles(即 CPU clock 周期), -F 99 表示每秒 99 次, -p 13204 是进程号, 即对哪个进程进行分析, -g 表示记录调用栈, sleep 30 则是持续 30 秒.

一定要加上 - g,否则无法记录数据!

-F 指定采样频率为 99Hz(每秒99次), 如果 99次 都返回同一个函数名, 那就说明 CPU 这一秒钟都在执行同一个函数, 可能存在性能问题.

运行后会产生一个庞大的文本文件. 如果一台服务器有 16 个 CPU, 每秒抽样 99 次, 持续 30 秒, 就得到 47,520 个调用栈, 长达几十万甚至上百万行.

为了便于阅读, perf record 命令可以统计每个调用栈出现的百分比, 然后从高到低排列.

1
perf report -n --stdio

2.4 生成火焰图

  1. 首先用 perf script 工具对 perf.data 进行解析

# 生成折叠后的调用栈
perf script -i perf.data &> perf.unfold

这里在任意文件夹都可使用

  1. 然后将解析出来的信息存下来, 供生成火焰图

stackcollapse-perf.pl 将 perf 解析出的内容 perf.unfold 中的符号进行折叠 :

# 在FlameGraph同路径下的文件夹生成火焰图
# ./FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded

或者使用全路径

/opt/rh/FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded

  1. 最后生成 svg 图

# ./FlameGraph/flamegraph.pl perf.folded > perf.svg

/opt/rh/FlameGraph/flamegraph.pl perf.folded > perf.svg

3.1 火焰图的含义

火焰图是基于 stack 信息生成的 SVG 图片, 用来展示 CPU 的调用栈。

  • y 轴表示调用栈, 每一层都是一个函数. 调用栈越深, 火焰就越高, 顶部就是正在执行的函数, 下方都是它的父函数.
  • x 轴表示抽样数, 如果一个函数在 x 轴占据的宽度越宽, 就表示它被抽到的次数多, 即执行的时间长. 注意, x 轴不代表时间, 而是所有的调用栈合并后, 按字母顺序排列的.
  • 火焰图就是看顶层的哪个函数占据的宽度最大. 只要有 “平顶”(plateaus), 就表示该函数可能存在性能问题。
  • 颜色没有特殊含义, 因为火焰图表示的是 CPU 的繁忙程度, 所以一般选择暖色调.

4 互动

火焰图是 SVG 图片,可以与用户互动

火焰的每一层都会标注函数名,鼠标悬浮时会显示完整的函数名、抽样抽中的次数、占据总抽样次数的百分比。下面是一个例子

在某一层点击,火焰图会水平放大,该层会占据所有宽度,显示详细信息。