1. 程式人生 > >火焰圖安裝與使用

火焰圖安裝與使用

簡介

火焰圖是定位疑難雜症的神器,比如 CPU 佔用高、記憶體洩漏等問題。特別是 Lua 級別的火焰圖,可以定位到函式和程式碼級別。

下圖來自 OpenResty 的官網,顯示的是一個正常執行的 OpenResty 應用的火焰圖,先不用瞭解細節,有一個直觀的瞭解。

裡面的顏色是隨機選取的,並沒有特殊含義。火焰圖的資料來源,是通過systemtap定期收集。

什麼時候使用

一般來說,當發現 CPU 的佔用率和實際業務應該出現的佔用率不相符,或者對 Nginx worker 的資源使用率(CPU,記憶體,磁碟 IO )出現懷疑的情況下,都可以使用火焰圖進行抓取。另外,對 CPU 佔用率低、吐吞量低的情況也可以使用火焰圖的方式排查程式中是否有阻塞呼叫導致整個架構的吞吐量低下。

如何安裝火焰圖生成工具

安裝 SystemTap

SystemTap 是一個診斷 Linux 系統性能或功能問題的開源軟體,為了診斷系統問題或效能,開發者或除錯人員只需要寫一些指令碼,然後通過 SystemTap 提供的命令列介面就可以對正在執行的核心進行診斷除錯。

在 CentOS 上的安裝方法
首先需要安裝當前核心版本對應的開發包和除錯包(這一步非常重要並且最為繁瑣):

檢視核心版本

# uname -r
# 2.6.32-754.6.3.el6.x86_64

下載rpm 包,可以在該網址中下載: http://debuginfo.centos.org

wget http://debuginfo.centos.org/6/x86_64/kernel-debuginfo-2.6.32-754.6.3.el6.x86_64.rpm
wget http://debuginfo.centos.org/6/x86_64/kernel-debuginfo-common-x86_64-2.6.32-754.6.3.el6.x86_64.rpm
# wget http://debuginfo.centos.org/6/x86_64/kernel-devel-2.6.32-754.6.3.el6.x86_64.rpm (這個不存在使用yum安裝)
# #Installaion:
# rpm -Uhv kernel-debuginfo-*rpm
# rpm -ivh kernel-debuginfo-$(uname -r).rpm
# rpm -ivh kernel-debuginfo-common-$(uname -r).rpm
# rpm -ivh kernel-devel-$(uname -r).rpm

yum安裝

安裝核心除錯所需要的包

yum -y install kernel-devel-2.6.32-754.6.3.el6.x86_64

yum -y install kernel-debuginfo-2.6.32-754.6.3.el6.x86_64

yum -y install kernel-debuginfo-common-x86_64-2.6.32-754.6.3.el6.x86_64

安裝 systemtap:

# yum install systemtap
# ...
# 測試systemtap安裝成功否:
# stap -v -e 'probe vfs.read {printf("read performed\n"); exit()}'

Pass 1: parsed user script and 103 library script(s) using 201628virt/29508res/3144shr/26860data kb, in 10usr/190sys/219real ms.
Pass 2: analyzed script: 1 probe(s), 1 function(s), 3 embed(s), 0 global(s) using 296120virt/124876res/4120shr/121352data kb, in 660usr/1020sys/1889real ms.
Pass 3: translated to C into "/tmp/stapffFP7E/stap_82c0f95e47d351a956e1587c4dd4cee1_1459_src.c" using 296120virt/125204res/4448shr/121352data kb, in 10usr/50sys/56real ms.
Pass 4: compiled C into "stap_82c0f95e47d351a956e1587c4dd4cee1_1459.ko" in 620usr/620sys/1379real ms.
Pass 5: starting run.
read performed
Pass 5: run completed in 20usr/30sys/354real ms.

如果出現如上輸出表示安裝成功。

在 Ubuntu 上的安裝方法

對於 Ubuntu 上的安裝,參考 Ubuntu 官方維護的一個 wiki: https://wiki.ubuntu.com/Kernel/Systemtap
一般來說,僅需引入 ddeb 源,然後 apt-get 就能解決了。
由於 systemtap 需要依賴某些核心特性,對於 Ubuntu Gutsy (或更老的版本),必須重新編譯核心。 編譯的步驟參見 systemtap 的這篇 wiki: https://sourceware.org/systemtap/wiki/SystemtapOnUbuntu
另外,由於 Ubuntu 16.04 官方庫裡的 systemtap 版本過舊(version 2.9),從 apt-get 安裝的 systemtap 有些情況下並不能正確地執行。 這時候需要從 systemtap 原始碼中編譯出可用的 systemtap。 編譯的過程參考 systemtap 的這篇文件: https://sourceware.org/git/?p=systemtap.git;a=blob_plain;f=README;hb=HEAD
大體上就這幾步:

# 下載依賴……
sudo apt install elfutils
sudo apt-get build-dep systemtap

# 下載最新的版本
wget/git ...

# 構建,並祈禱能一次成功
./configure
make all
[sudo] make install

火焰圖繪製

首先,需要下載 stapxx 工具包:Github地址。 該工具包中包含用 perl 寫的,會生成 stap 探測程式碼並執行的指令碼。如果是要抓 Lua 級別的情況,請使用其中的 lj-lua-stacks.sxx。 由於 lj-lua-stacks.sxx 輸出的是檔案絕對路徑和行號,要想匹配具體的 Lua 程式碼,需要用 fix-lua-bt 進行轉換。

# ps -ef | grep nginx  (ps:得到類似這樣的輸出,其中15010即使worker程序的pid,後面需要用到)
hippo    14857     1  0 Jul01 ?        00:00:00 nginx: master process /opt/openresty/nginx/sbin/nginx -p /home/hippo/skylar_server_code/nginx/main_server/ -c conf/nginx.conf
hippo    15010 14857  0 Jul01 ?        00:00:12 nginx: worker process
# ./samples/lj-lua-stacks.sxx --arg time=5 --skip-badvars -x 15010 > tmp.bt (-x 是要抓的程序的 pid, 探測結果輸出到 tmp.bt)
# ./fix-lua-bt tmp.bt > flame.bt  (處理 lj-lua-stacks.sxx 的輸出,使其可讀性更佳)

其次,下載 Flame-Graphic 生成包:Github地址,該工具包中包含多個火焰圖生成工具,其中,stackcollapse-stap.pl 才是為 SystemTap 抓取的棧資訊的生成工具

# stackcollapse-stap.pl flame.bt > flame.cbt
# flamegraph.pl flame.cbt > flame.svg

如果一切正常,那麼會生成 flame.svg,這便是火焰圖,用瀏覽器開啟即可。
ps:如果在執行 lj-lua-stacks.sxx 的時間週期內(上面的命令是 5 秒), 抓取的 worker 沒有任何業務在跑,那麼生成的火焰圖便沒有業務內容。為了讓生成的火焰圖更有代表性,我們通常都會在抓取的同時進行壓測。

如何定位問題

一個正常的火焰圖,應該呈現出如官網給出的樣例(官網的火焰圖是抓 C 級別函式):

從上圖可以看出,正常業務下的火焰圖形狀類似的“山脈”,“山脈”的“海拔”表示 worker 中業務函式的呼叫深度,“山脈”的“長度”表示 worker 中業務函式佔用 cpu 的比例。+

下面將用一個實際應用中遇到問題抽象出來的示例(CPU 佔用過高)來說明如何通過火焰圖定位問題。
問題表現,Nginx worker 執行一段時間後出現 CPU 佔用 100% 的情況,reload 後一段時間後復現,當出現 CPU 佔用率高情況的時候是某個 worker 佔用率高。
問題分析,單 worker cpu 高的情況一定是某個 input 中包含的資訊不能被 Lua 函式以正確地方式處理導致的,因此上火焰圖找出具體的函式,抓取的過程需要抓取 C 級別的函式和 Lua 級別的函式,抓取相同的時間,兩張圖一起分析才能得到準確的結果。
抓取步驟:

# making the ./stap++ tool visible in PATH:
$ export PATH=$PWD:$PATH
# assuming the nginx worker process pid is 6949:
$ ./samples/lj-lua-stacks.sxx --arg time=5 --skip-badvars -x 6949 > tmp.bt
Start tracing 6949 (/opt/nginx/sbin/nginx)
Please wait for 5 seconds
$ ./fix-lua-bt tmp.bt > a.bt

使用 stackcollapse-stap.pl 和 flamegraph.pl

./stackcollapse-stap.pl a.bt > a.cbt ./flamegraph.pl a.cbt > a.svg
  • a.svg 即是火焰圖,拖入瀏覽器即可: problem
  • 從上圖可以清楚的看到 get_serial_id 這個函式佔用了絕大部分的 CPU
    比例,問題的排查可以從這裡入手,找到其呼叫棧中異常的函式。

PS:一般來說一個正常的火焰圖看起來像一座座連綿起伏的“山峰”,而一個異常的火焰圖看起來像一座“平頂山”。