用 perf 生成火焰图

用 perf 生成火焰图

##2.1 perf 采集数据

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

sudo perf record -F 99 -p 3887 -g -- sleep 30
perf record -F 99 -a -g -- sleep 60 所有信息

perf 采集 chrome 运行的信息

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

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

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

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

sudo perf report -n --stdio

perf report 信息

##2.2 生成火焰图

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

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

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

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

# 生成火焰图
./stackcollapse-perf.pl perf.unfold &> perf.folded

最后生成 svg 图

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

我们可以使用管道将上面的流程简化为一条命令

perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > process.svg

生成火焰图

#3 解析火焰图

最后就可以用浏览器打开火焰图进行分析啦.

##3.1 火焰图的含义

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

y 轴表示调用栈, 每一层都是一个函数. 调用栈越深, 火焰就越高, 顶部就是正在执行的函数, 下方都是它的父函数.

x 轴表示抽样数, 如果一个函数在 x 轴占据的宽度越宽, 就表示它被抽到的次数多, 即执行的时间长. 注意, x 轴不代表时间, 而是所有的调用栈合并后, 按字母顺序排列的.

火焰图就是看顶层的哪个函数占据的宽度最大. 只要有 "平顶"(plateaus), 就表示该函数可能存在性能问题。

颜色没有特殊含义, 因为火焰图表示的是 CPU 的繁忙程度, 所以一般选择暖色调.

##3.2 互动性

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

  • 鼠标悬浮

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

mysqld'JOIN::exec (272,959 samples, 78.34 percent)

  • 点击放大

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

左上角会同时显示 "Reset Zoom", 点击该链接, 图片就会恢复原样.

  • 搜索

按下 Ctrl + F 会显示一个搜索框,用户可以输入关键词或正则表达式,所有符合条件的函数名会高亮显示.

##3.3 局限

两种情况下, 无法画出火焰图, 需要修正系统行为.

  • 调用栈不完整

当调用栈过深时,某些系统只返回前面的一部分(比如前10层)。

  • 函数名缺失

有些函数没有名字,编译器只用内存地址来表示(比如匿名函数)。

##3.4 浏览器的火焰图

Chrome 浏览器可以生成页面脚本的火焰图, 用来进行 CPU 分析.

打开开发者工具, 切换到 Performance 面板. 然后, 点击"录制" 按钮, 开始记录数据. 这时, 可以在页面进行各种操作, 然后停止"录制".

这时, 开发者工具会显示一个时间轴. 它的下方就是火焰图.

浏览器的火焰图与标准火焰图有两点差异 : 它是倒置的(即调用栈最顶端的函数在最下方); x 轴是时间轴, 而不是抽样次数.

#4 红蓝分叉火焰图

参考 http://www.brendangregg.com/blog/2014-11-09/differential-flame-graphs.html

幸亏有了 CPU 火焰图(flame graphs), CPU 使用率的问题一般都比较好定位. 但要处理性能回退问题, 就要在修改前后或者不同时期和场景下的火焰图之间, 不断切换对比, 来找出问题所在, 这感觉就是像在太阳系中搜寻冥王星. 虽然, 这种方法可以解决问题, 但我觉得应该会有更好的办法.

所以, 下面就隆重介绍 红/蓝差分火焰图(red/blue differential flame graphs)

##4.1 红蓝差分火焰图示例

红/蓝差分火焰图

上面是一副交互式 SVG 格式图片. 图中使用了两种颜色来表示状态, 红色表示增长, 蓝色表示衰减.

这张火焰图中各火焰的形状和大小都是和第二次抓取的 profile 文件对应的 CPU 火焰图是相同的. (其中, y 轴表示栈的深度, x 轴表示样本的总数, 栈帧的宽度表示了 profile 文件中该函数出现的比例, 最顶层表示正在运行的函数, 再往下就是调用它的栈).

在下面这个案例展示了, 在系统升级后, 一个工作载荷的 CPU 使用率上升了. 下面是对应的 CPU 火焰图(SVG 格式)

对应的 CPU 火焰图

通常, 在标准的火焰图中栈帧和栈塔的颜色是随机选择的. 而在红/蓝差分火焰图中, 使用不同的颜色来表示两个 profile 文件中的差异部分.

在第二个 profile 中 deflate_slow( ) 函数以及它后续调用的函数运行的次数要比前一次更多, 所以在上图中这个栈帧被标为了红色. 可以看出问题的原因是ZFS的压缩功能被启用了, 而在系统升级前这项功能是关闭的.

这个例子过于简单, 我甚至可以不用差分火焰图也能分析出来. 但想象一下, 如果是在分析一个微小的性能下降, 比如说小于5%, 而且代码也更加复杂的时候, 问题就为那么好处理了.

##4.2 红蓝差分火焰图简介

这个事情我已经讨论了好几年了, 最终我自己编写了一个我个人认为有价值的实现。它的工作原理是这样的 :

  1. 抓取修改前的堆栈 profile1 文件

  2. 抓取修改后的堆栈 profile2 文件

  3. 使用 profile2 来生成火焰图. (这样栈帧的宽度就是以profile2 文件为基准的)

  4. 使用 "2-1" 的差异来对火焰图重新上色. 上色的原则是, 如果栈帧在 profile2 中出现出现的次数更多, 则标为红色, 否则标为蓝色. 色彩是根据修改前后的差异来填充的.

这样做的目的是, 同时使用了修改前后的 profile 文件进行对比, 在进行功能验证测试或者评估代码修改对性能的影响时,会非常有用. 新的火焰图是基于修改后的 profile 文件生成(所以栈帧的宽度仍然显示了当前的CPU消耗). 通过颜色的对比,就可以了解到系统性能差异的原因。

只有对性能产生直接影响的函数才会标注颜色(比如说,正在运行的函数),它所调用的子函数不会重复标注。

##4.3 生成红/蓝差分火焰图

作者的 GitHub 仓库 FlameGrdph 中实现了一个程序脚本,difffolded.pl 用来生成红蓝差分火焰图. 为了展示工具是如何工作的, 用 Linux perf_events 来演示一下操作步骤. 你也可以使用其他 profiler/tracer.

  • 抓取修改前的profile 1文件:
#	抓取数据
perf record -F 99 -a -g -- sleep 30
#	解析数据生成堆栈信息
perf script > out.stacks1
#	折叠堆栈
./stackcollapse-perf.pl ../out.stacks1 > out.folded1
  • 一段时间后 (或者程序代码修改后), 抓取 profile 2` 文件
#	抓取数据
perf record -F 99 -a -g -- sleep 30
#	解析数据生成堆栈信息
perf script > out.stacks2
#	折叠堆栈
./stackcollapse-perf.pl ../out.stacks2 > out.folded2

生成红蓝差分火焰图

./difffolded.pl out.folded1 out.folded2 | ./flamegraph.pl > diff2.svg

difffolded.pl 只能对 "折叠" 过的堆栈 profile 文件进行操作, 折叠操作 是由前面的 stackcollapse 系列脚本完成的. 脚本共输出 3 列数据, 其中一列代表折叠的调用栈, 另两列为修改前后 profile 文件的统计数据.

func_a;func_b;func_c 31 33
[...]

在上面的例子中 "funca()->funcb()->func_c()" 代表调用栈,这个调用栈在 profile1文件中共出现了31次, 在profile2文件中共出现了33次. 然后, 使用flamegraph.pl脚本处理这3` 列数据, 会自动生成一张红/蓝差分火焰图.

再介绍一些有用的选项:

其他选项描述
difffolded.pl -n 这个选项会把两个profile文件中的数据规范化,使其能相互匹配上。如果你不这样做,抓取到所有栈的统计值肯定会不相同,因为抓取的时间和CPU负载都不同。这样的话,看上去要么就是一片红(负载增加),要么就是一片蓝(负载下降)。-n选项对第一个profile文件进行了平衡,这样你就可以得到完整红/蓝图谱
difffolded.pl -x 这个选项会把16进制的地址删掉。 profiler时常会无法将地址转换为符号,这样的话栈里就会有16进制地址。如果这个地址在两个profile文件中不同,这两个栈就会认为是不同的栈,而实际上它们是相同的。遇到这样的问题就用-x选项搞定
flamegraph.pl --negate 用于颠倒红/蓝配色。 在下面的章节中,会用到这个功能

##4.4 不足之处

虽然红/蓝差分火焰图很有用, 但实际上还是有一个问题 : 如果一个代码执行路径完全消失了, 那么在火焰图中就找不到地方来标注蓝色. 你只能看到当前的 CPU 使用情况, 而不知道为什么会变成这样.

一个办法是, 将对比顺序颠倒, 画一个相反的差分火焰图. 例如 :

红蓝差分火焰图的不足之处

上面的火焰图是以修改前的 profile 文件为基准, 颜色表达了将要发生的情况. 右边使用蓝色高亮显示的部分, 从中可以看出修改后 CPU Idle 消耗的 CPU 时间会变少. (其实, 通常会把 cpuidle 给过滤掉, 使用命令行 grep -v cpuidle)

图中把消失的代码也突显了出来(或者应该是说, 没有突显), 因为修改前并没有使能压缩功能, 所以它没有出现在修改前的 profile 文件了, 也就没有了被表为红色的部分.

下面是对应的命令行:

./difffolded.pl out.folded2 out.folded1 | ./flamegraph.pl --negate > diff1.svg

这样, 把前面生成 diff2.svg 一并使用,我们就能得到:

火焰图信息描述
diff1.svg 宽度是以修改前profile文件为基准,颜色表明将要发生的情况
diff2.svg 宽度是以修改后profile文件为基准,颜色表明已经发生的情况

如果是在做功能验证测试,我会同时生成这两张图。

##4.5 CPI 火焰图

这些脚本开始是被使用在CPI火焰图 的分析上. 与比较修改前后的 profile 文件不同, 在分析 CPI 火焰图时, 可以分析 CPU 工作周期与停顿周期的差异变化, 这样可以凸显出CPU的工作状态来.

##4.6 其他的差分火焰图

其他差分图

也有其他人做过类似的工作. Robert Mustacchi 在不久前也做了一些尝试,他使用的方法类似于代码检视时的标色风格:只显示了差异的部分,红色表示新增(上升)的代码路径,蓝色表示删除(下降)的代码路径。一个关键的差别是栈帧的宽度只体现了差异的样本数。右边是一个例子。这个是个很好的主意,但在实际使用中会感觉有点奇怪,因为缺失了完整profile文件的上下文作为背景,这张图显得有些难以理解。

火焰图

Cor-Paul Bezemer也制作了一种差分显示方法flamegraphdiff, 他同时将3张火焰图放在同一张图中,修改前后的标准火焰图各一张,下面再补充了一张差分火焰图,但栈帧宽度也是差异的样本数。 上图是一个例子. 在差分图中将鼠标移到栈帧上,3张图中同一栈帧都会被高亮显示。这种方法中补充了两张标准的火焰图,因此解决了上下文的问题。

我们3人的差分火焰图,都各有所长。三者可以结合起来使用:Cor-Paul方法中上方的两张图,可以用我的diff1.svg 和 diff2.svg。下方的火焰图可以用Robert的方式。为保持一致性,下方的火焰图可以用我的着色方式:蓝->白->红。

火焰图正在广泛传播中,现在很多公司都在使用它。如果大家知道有其他的实现差分火焰图的方式,我也不会感到惊讶。(请在评论中告诉我)

##4.7 总结

如果你遇到了性能回退问题,红/蓝差分火焰图是找到根因的最快方式。这种方式抓取了两张普通的火焰图,然后进行对比,并对差异部分进行标色:红色表示上升,蓝色表示下降。 差分火焰图是以当前(“修改后”)的profile文件作为基准,形状和大小都保持不变。因此你通过色彩的差异就能够很直观的找到差异部分,且可以看出为什么会有这样的差异。

差分火焰图可以应用到项目的每日构建中,这样性能回退的问题就可以及时地被发现和修正。

via: http://www.brendangregg.com/blog/2014-11-09/differential-flame-graphs.html

posted on 2019-07-05 17:44  紫枫术河  阅读(1260)  评论(0编辑  收藏  举报

导航