火焰图(Flame Graph)是由 Linux
性能优化大师 Brendan Gregg
发明的。它是定位疑难杂症的神器,比如 CPU
占用高、内存泄漏等问题。
和所有其他的 trace
和 profiling
方法不同的是,Flame Graph
以一个全局的视野来看待时间分布,它从底部往顶部,列出所有可能的调用栈。
1、Flame Graph
火焰图看起来就像一团跳动的火焰,这也正是其名字的由来。燃烧在火苗尖部的就是 CPU
正在执行的操作,不过需要说明的是颜色是随机的,本身并没有特殊的含义。
纵向表示调用栈的深度(或 code-path
),横向表示消耗的时间。因为调用栈在横向会按照字母排序,并且同样的调用栈会做合并,所以,一个格子的宽度越大越说明其可能是瓶颈。
综上所述,主要就是看那些比较宽大的火苗,特别留意那些类似平顶山的火苗。
详细介绍可以看阮一峰的如何读懂火焰图。
1 | # 安装 |
生成和创建火焰图需要如下几个步骤:
流程 | 描述 | 脚本 |
---|---|---|
捕获堆栈 | 使用 perf/systemtap/dtrace 等工具抓取程序的运行堆栈 | perf/systemtap/dtrace |
折叠堆栈 | trace 工具抓取的系统和程序运行每一时刻的堆栈信息, 需要对他们进行分析组合, 将重复的堆栈累计在一起, 从而体现出负载和关键路径 | FlameGraph 中的 stackcollapse 程序 |
生成火焰图 | 分析 stackcollapse 输出的堆栈信息生成火焰图 | flamegraph.pl |
这里的 捕获堆栈
,用的是 systemtap
。如果是 Java
或 Lua
等应用程序,还需要借助第三方 perf
工具来完成堆栈数据的采集。这里,Java
用的是lightweight-java-profiler, Nginx
用的是openresty-systemtap-toolkit。
Flame Graph
提供了一系列的 stackcollapse
。
stackcollapse | 描述 |
---|---|
stackcollapse.pl | 用于 DTrace 堆栈 |
stackcollapse-perf.pl | 用于 Linux perf_events perf 脚本输出 |
stackcollapse-pmc.pl | 用于 FreeBSD pmcstat -G 堆栈 |
stackcollapse-stap.pl | 用于 SystemTap 堆栈 |
stackcollapse-instruments.pl | 用于 XCode 工具 |
stackcollapse-vtune.pl | 用于英特尔 VTune 捕获 |
stackcollapse-ljp.awk | 用于Lightweight Java 捕获 |
stackcollapse-jstack.pl | 用于Java jstack(1) 输出 |
stackcollapse-gdb.pl | 用于 gdb(1) 堆栈 |
stackcollapse-go.pl | 用于 Golang pprof 堆栈 |
stackcollapse-vsprof.pl | 用于 Microsoft Visual Studio 捕获 |
2、SystemTap
SystemTap
是对 Linux
内核监控和跟踪的工具。由于 SystemTap
运行需要内核的调试信息支撑,默认发行版的内核在配置时这些调试开关没有打开,所以,安装完 SystemTap
也是无法去探测内核信息的。需要先去 debuginfo
网站(CentOS debuginfo,Ubuntu 内核资源)下载对应的内核包。
因为 SystemTap
对内核的检查机制非常严格,如果只是使用 uname -r
查看系统内核,会省略内核的小版本号。而小版本号不对应的话,则会导致无法使用。
以 Ubuntu
为例:
1 | > uname -r #此方式无法查看小版本号 |
Ubuntu安装SystemTap
再次强调,一定要选对系统版本号,否则后续无法正常执行。内核资源地址。下载可能会比较耗时,需要耐心等待。
如果服务器可以重启的话,最好先更新一下内核,然后再重启一次。
1 | apt-get update |
下载并安装 debuginfo
1 | #以下为我的系统内核版本地址,并不一定适用,请查找对应的版本下载 |
下载并安装 elfutils
elfutils
是 Systemtap
依赖的软件,需要安装。有的系统可能以及默认安装了,建议卸载掉,安装较新的版本。最好与安装的 Systemtap
发行版本日期较近。
1 | # 查看 elfutils 版本 |
下载并安装 Systemtap
Ubuntu
默认是安装了 Systemtap
,一般默认安装的版本是2.9,此版本较旧,可能会遇到各种问题,建议安装最新版本。不建议使用 apt-get install
方式安装。
1 | # 查看版本 |
安装完成后,需要配置环境变量。
1 | export SYSTEMTAP_HOME=/opt/apps/systemtap |
验证安装结果。
1 | # 查看 systemtap 是否可用 |
如果能正常打印出内容,则说明安装成功。
为了使 debug symbols
更友好的支持 gdb
,需要执行以下操作。创建 debug_ko.sh
脚本。内容如下:
1 | for file in `find /usr/lib/debug -name '*.ko' -print`do buildid=`eu-readelf -n $file| grep Build.ID: | awk '{print $3}'` dir=`echo $buildid | cut -c1-2` fn=`echo $buildid | cut -c3-` mkdir -p /usr/lib/debug/.build-id/$dir ln -s $file /usr/lib/debug/.build-id/$dir/$fn ln -s $file /usr/lib/debug/.build-id/$dir/${fn}.debugdone |
然后执行该脚本。
1 | stap -L 'module("thinkpad_acpi").function("brightness*")' | sort |
这样,在 Ubuntu
下安装 SystemTap
就完成。
3、Profiler
火焰图是将在 CPU
上运行的栈信息可视化。所以,要想生成火焰图,必须先收集目标程序在 CPU
上的栈信息。
3.1、Java
针对 Java
,可以使用轻量级的 lightweight-java-profiler,目前只支持 hotspot JVM
。只有在程序退出时(例如: System.exit(0)),才会将收集到数据写到目标文件(kill -9 pid方式结束进程是无效的)。
1 | git clone https://github.com/dcapwell/lightweight-java-profilercd lightweight-java-profilermake all |
- 注意: 可以在
src/globasl.h
中设定参数
1 | // 每秒采样点数static const int kNumInterrupts = 100; // 最大采样的stack数量static const int kMaxStackTraces = 3000; // 一个采样stack的最大深度static const int kMaxFramesToCapture = 128; |
3.2、Nginx / Lua
针对 Nginx
,推荐使用 openresty-systemtap-toolkit
, 是基于 SystemTap
的 OpenResty
(包括 Nginx
,LuaJIT
,ngx_lua
等)的实时分析和诊断工具。由大神章亦春提供。这里面很多工具适用于任何 C/CPP 语言编写的程序。
它支持选择对 CPU
监控开关的控制。
- sample-bt: 用来生成 On-CPU 火焰图的采样数据(DEMO)
- sample-bt-off-cpu: 用来生成 Off-CPU 火焰图的采样数据(DEMO)
什么时候使用 On-CPU
火焰图,什么时候使用 Off-CPU
火焰图,取决于当前的瓶颈到底是什么。
如果是 CPU
瓶颈,使用 On-CPU
火焰图。
如果是 IO
或锁瓶颈,使用 Off-CPU
火焰图。
如果无法确定,那么可以通过压测工具来确认:通过压测工具看看能否让 CPU
使用率趋于饱和,如果能,那么使用 On-CPU
火焰图.
如果不管怎么压,CPU
使用率始终上不来,那么多半说明程序被 IO
或锁卡住了,此时适合使用 Off-CPU
火焰图。
如果还是确认不了,那么不妨将 On-CPU
和 Off-CPU
两种模式都尝试一下,正常情况下它们的差异会比较大,如果两张火焰图长得差不多,那么通常认为 CPU
被其它进程抢占了。
1 | # 安装git clone https://github.com/openresty/openresty-systemtap-toolkit.git |
4、生成火焰图
4.1、Java
在应用程序或Web容器中,加入以下 JVM
的配置。
1 | export JAVA_OPTS="-Xms1024m -Xmx2048m -Xss256k -Xmn1024m -agentpath:/work/app/lightweight-java-profiler/build-64/liblagent.so" |
-Xms
之类的配置,根据情况调整。
然后,启动应用程序,进行请求测试,最后,正确停止应用服务(非kill -9方式)。
会在启动目录下生成一个 traces.txt
文件。这个就是采集到数据。
4.2、Nginx
1 | # 获取 nginx work 进程idps -ef | grep nginx# 进入openresty-systemtap-toolkit 启动./sample-bt -p 21408 -t 60 -u > /tmp/a.bt |
将数据转成SVG格式的火焰图
1 | # 进入FlameGraph目录#Java./stackcollapse-ljp.awk < ../traces.txt | ./flamegraph.pl > ../traces.svg#Nginx./stackcollapse-stap.pl /tmp/a.bt > /tmp/a.cbt./flamegraph.pl /tmp/a.cbt > /tmp/a.svg |
如果想快速查看某个包路径下的性能,可以直接用浏览器搜索快捷键进行搜索,关键字会以蓝色突出显示,便于快速定位。
参考: