使用火焰图做性能分析
火焰图(Flame Graph)是由 `Linux` 性能优化大师 `Brendan Gregg` 发明的。它是定位疑难杂症的神器,比如 `CPU` 占用高、内存泄漏等问题。
和所有其他的 `trace` 和 `profiling` 方法不同的是,`Flame Graph` 以一个全局的视野来看待时间分布,它从底部往顶部,列出所有可能的调用栈。
Flame Graph
火焰图看起来就像一团跳动的火焰,这也正是其名字的由来。燃烧在火苗尖部的就是 CPU
正在执行的操作,不过需要说明的是颜色是随机的,本身并没有特殊的含义。
纵向表示调用栈的深度(或 code-path
),横向表示消耗的时间。因为调用栈在横向会按照字母排序,并且同样的调用栈会做合并,所以,一个格子的宽度越大越说明其可能是瓶颈。
综上所述,主要就是看那些比较宽大的火苗,特别留意那些类似平顶山的火苗。
详细介绍可以看阮一峰的 如何读懂火焰图。
# 安装 git clone https://github.com/brendangregg/FlameGraph
生成和创建火焰图需要如下几个步骤:
流程 | 描述 | 脚本 |
---|---|---|
捕获堆栈 | 使用 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 捕获 |
SystemTap
SystemTap
是对 Linux
内核监控和跟踪的工具。由于 SystemTap
运行需要内核的调试信息支撑,默认发行版的内核在配置时这些调试开关没有打开,所以,安装完 SystemTap
也是无法去探测内核信息的。需要先去 debuginfo
网站(CentOS debuginfo,Ubuntu 内核资源)下载对应的内核包。
因为 SystemTap
对内核的检查机制非常严格,如果只是使用 uname -r
查看系统内核,会省略内核的小版本号。而小版本号不对应的话,则会导致无法使用。
以 Ubuntu
为例:
> uname -r #此方式无法查看小版本号 4.4.0-85-generic > cat /proc/version_signature #此方式可以看到小版本号, 108 就是小版本号 Ubuntu 4.4.0-85.108-generic 4.4.73
Ubuntu安装SystemTap
再次强调,一定要选对系统版本号,否则后续无法正常执行。内核资源地址。下载可能会比较耗时,需要耐心等待。
如果服务器可以重启的话,最好先更新一下内核,然后再重启一次。
apt-get update apt-get upgrade
下载并安装 debuginfo
#以下为我的系统内核版本地址,并不一定适用,请查找对应的版本下载 wget http://ddebs.ubuntu.com/pool/main/l/linux/linux-image-4.4.0-85-generic-dbgsym_4.4.0-85.108_amd64.ddeb #安装 dpkg -i linux-image-4.4.0-85-generic-dbgsym_4.4.0-85.108_amd64.ddeb
下载并安装 elfutils
elfutils
是 Systemtap
依赖的软件,需要安装。有的系统可能以及默认安装了,建议卸载掉,安装较新的版本。最好与安装的 Systemtap
发行版本日期较近。
# 查看 elfutils 版本 apt-show-versions -p elfutils # 删除elfutils apt-get remove elfutils #下载对应的最新版本 wget https://sourceware.org/elfutils/ftp/0.168/elfutils-0.168.tar.bz2 tar -xjf elfutils-0.168.tar.bz2 #安装 ./configure make && make install
下载并安装 Systemtap
Ubuntu
默认是安装了 Systemtap
,一般默认安装的版本是2.9,此版本较旧,可能会遇到各种问题,建议安装最新版本。不建议使用 apt-get install
方式安装。
# 查看版本 stap -V # 卸载 systemtap apt-get remove systemtap # 下载最新版本 wget https://sourceware.org/systemtap/ftp/releases/systemtap-3.1.tar.gz tar -zxvf systemtap-3.1.tar.gz # 安装 ./configure --prefix=/opt/apps/systemtap make && make install
安装完成后,需要配置环境变量。
export SYSTEMTAP_HOME=/opt/apps/systemtap export PATH=$SYSTEMTAP_HOME/bin:$PATH
验证安装结果。
# 查看 systemtap 是否可用 stap -ve 'probe begin { log("hello systemtap!") exit() }' # 查看 systemtap 与 debuginfo 结合是否可用 stap -e 'probe kernel.function("sys_open") {log("hello world") exit()}'
如果能正常打印出内容,则说明安装成功。
为了使 debug symbols
更友好的支持 gdb
,需要执行以下操作。创建 debug_ko.sh
脚本。内容如下:
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}.debug done
然后执行该脚本。
stap -L 'module("thinkpad_acpi").function("brightness*")' | sort
这样,在 Ubuntu
下安装 SystemTap
就完成。
第三方 Profiler
火焰图是将在 CPU
上运行的栈信息可视化。所以,要想生成火焰图,必须先收集目标程序在 CPU
上的栈信息。
Java
针对 Java
,可以使用轻量级的 lightweight-java-profiler,目前只支持 hotspot JVM
。只有在程序退出时(例如: System.exit(0)),才会将收集到数据写到目标文件(kill -9 pid方式结束进程是无效的)。
git clone https://github.com/dcapwell/lightweight-java-profiler cd lightweight-java-profiler make all
- 注意: 可以在
src/globasl.h
中设定参数
// 每秒采样点数 static const int kNumInterrupts = 100; // 最大采样的stack数量 static const int kMaxStackTraces = 3000; // 一个采样stack的最大深度 static const int kMaxFramesToCapture = 128;
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
被其它进程抢占了。
# 安装 git clone https://github.com/openresty/openresty-systemtap-toolkit.git
生成火焰图
Java
在应用程序或Web容器中,加入以下 JVM
的配置。
export JAVA_OPTS="-Xms1024m -Xmx2048m -Xss256k -Xmn1024m -agentpath:/work/app/lightweight-java-profiler/build-64/liblagent.so"
-Xms
之类的配置,根据情况调整。
然后,启动应用程序,进行请求测试,最后,正确停止应用服务(非kill -9方式)。
会在启动目录下生成一个 traces.txt
文件。这个就是采集到数据。
Nginx
# 获取 nginx work 进程id ps -ef | grep nginx # 进入openresty-systemtap-toolkit 启动 ./sample-bt -p 21408 -t 60 -u > /tmp/a.bt
将数据转成SVG格式的火焰图
# 进入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

如果想快速查看某个包路径下的性能,可以直接用浏览器搜索快捷键进行搜索,关键字会以蓝色突出显示,便于快速定位。
参考:
I wanted to check up and let you know how really I liked discovering your blog today.
Today, with the fast way of life that everyone leads, credit cards have a huge demand in the economy. Persons from every field are using the credit card and people who are not using the card have lined up to apply for one. Thanks for discussing your ideas on credit cards. https://varicoseveinsmedi.com best prescription drugs for varicose veins
Ja naprawdę nagrodę twoją pracę, Świetny post szybki test na koronowirusa szybki test na koronowirusa.