前言

日常开发过程中,有时会出现线上机器CPU飙高,这很可能导致服务宕机,造成线上事故。如何快速找出程序问题所在,是必须掌握的技巧,这里分享使用火焰图来定位问题。

火焰图

首先得明白什么是火焰图。简单的讲:整个图形看起来就像一团跳动的火焰. 燃烧在火苗尖部的就是 CPU 正在执行的操作, 不过需要说明的是颜色是随机的, 本身并没有特殊的含义, 纵向表示调用栈的深度, 横向表示消耗的时间. 因为调用栈在横向会按照字母排序, 并且同样的调用栈会做合并, 所以一个格子的宽度越大越说明其可能是瓶颈. 所以, 主要就是看那些比较宽大的火苗, 特别留意那些类似平顶山的火苗.

要生成火焰图, 必须要有一个顺手的 Tracer 工具, 如果操作系统是 Linux 的话, 那么选择通常是 perf, systemtap 中的一种. 其中 perf 相对更常用, 因为它是 Linux Kernel 内置的性能调优工具, 多数 Linux 都包含了它, 有兴趣的读者稍后可以参考 Linux Profiling at Netflix 中的介绍.

火焰图生成工具

常用的生成工具是Brendan D. Gregg 的 Flame Graph。
将工程clone下来

1
git clone https://github.com/brendangregg/FlameGraph

生成火焰图需要如下几个步骤:

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

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

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

使用pref生成火焰图

这里演示使用perf生成火焰图,服务器使用的是center os 7

  1. 安装perf
    1
    yum install perf
  2. 采集数据
    1
    sudo perf record -F 99 -p 2441 -g -- sleep 30

    上面的代码中,perf record表示记录,-F 99表示每秒99次,-p 2441是进程号,即对哪个进程进行分析,-g表示记录调用栈,sleep 30则是持续30秒。

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

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


  1. 生成火焰图
    首先用 perf script 工具对 perf.data 进行解析
1
2
# 生成折叠后的调用栈
perf script -i perf.data &> perf.unfold

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

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

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

最后生成 svg 图

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

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

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

解析火焰图

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

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

火焰图的局限性

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

  1. 调用栈不完整
    当调用栈过深时,某些系统只返回前面的一部分(比如前10层)。
  2. 函数名缺失
    有些函数没有名字,编译器只用内存地址来表示(比如匿名函数)。

参考

  1. https://blog.csdn.net/gatieme/article/details/78885908