Linux下用火焰圖進行效能分析【轉】
轉自:https://blog.csdn.net/gatieme/article/details/78885908
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可, 轉載請註明出處, 謝謝合作
因本人技術水平和知識面有限, 內容如有紕漏或者需要修正的地方, 歡迎大家指正, 也歡迎大家提供一些其他好的除錯工具以供收錄, 鄙人在此謝謝啦
軟體的效能分析, 往往需要檢視 CPU
耗時, 瞭解瓶頸在哪裡.
火焰圖(flame graph
) 是效能分析的利器
1 火焰圖簡介
很多人感冒發燒的時候, 往往會模仿神農氏嘗百草的路子: 先嚐嘗抗病毒的藥, 再試試抗細菌的藥, 甭管家裡有什麼藥挨個試, 什麼中藥西藥, 瞎貓總會碰上死耗子, 如此做法自然是不可取的, 正確的做法應該是去醫院驗個血, 確診後再對症下藥.
讓我們回想一下我們一般是如何除錯程式的 : 通常是在沒有資料的情況下依靠主觀臆斷來瞎蒙, 而不是考慮問題到底是什麼引起的!
毫無疑問, 調優程式效能問題的時候, 同樣需要對症下藥. 好訊息是 [Brendan D. Gregg
]((http://www.brendangregg.com/perf.html#FlameGraphs) 發明了火焰圖
1.1 火焰圖
常見的火焰圖型別有 On-CPU
, Off-CPU
, 還有 Memory
, Hot/Cold
, Differential
等等.
關於火焰圖詳細的介紹可以參考 Blazing Performance with Flame Graphs
, 簡而言之 : 整個圖形看起來就像一團跳動的火焰, 這也正是其名字的由來. 燃燒在火苗尖部的就是 CPU
要生成火焰圖, 必須要有一個順手的 Tracer
工具, 如果作業系統是 Linux
的話, 那麼選擇通常是 perf
, systemtap
中的一種. 其中 perf
相對更常用, 因為它時 Linux Kernel
內建的效能調優工具, 多數 Linux
都包含了它, 有興趣的讀者稍後可以參考 Linux Profiling at Netflix
中的介紹, 尤其是裡面關於如何處理 Broken stacks
問題的描述, 建議多看幾遍, 而 systemtap
相對更強大, 不過缺點是你需要先學會它本身的程式語言.
早期火焰圖在 Nginx
和 社群比較活躍, 如果你是一個 Nginx
開發或者優化人員, 那麼我強烈推薦你使用 春哥 的 nginx-systemtap-toolkit
, 乍一看名字你可能會誤以為這個工具包是 nginx
專用的, 實際上這裡面很多工具適用於任何 C/CPP
語言編寫的程式:
程式 | 功能 |
---|---|
sample-bt |
用來生成 On-CPU 火焰圖的取樣資料(DEMO ) |
sample-bt-off-cpu |
用來生成 Off-CPU 火焰圖的取樣資料 (DEMO ) |
1.2 On/Off-CPU 火焰圖
那麼什麼時候使用 On-CPU
火焰圖? 什麼時候使用 Off-CPU
火焰圖呢?
取決於當前的瓶頸到底是什麼, 如果是 CPU
則使用 On-CPU
火焰圖, 如果是 IO
或鎖則使用 Off-CPU
火焰圖. 如果無法確定, 那麼可以通過壓測工具來確認 : 通過壓測工具看看能否讓 CPU
使用率趨於飽和, 如果能那麼使用 On-CPU
火焰圖, 如果不管怎麼壓, CPU
使用率始終上不來, 那麼多半說明程式被 IO
或鎖卡住了, 此時適合使用 Off-CPU
火焰圖.
如果還是確認不了, 那麼不妨 On-CPU
火焰圖和 Off-CPU
火焰圖都搞搞, 正常情況下它們的差異會比較大, 如果兩張火焰圖長得差不多, 那麼通常認為 CPU
被其它程序搶佔了.
在取樣資料的時候, 最好通過壓測工具對程式持續施壓, 以便採集到足夠的樣本. 關於壓測工具的選擇, 如果選擇 ab
的話, 那麼務必記得開啟 -k
選項, 以避免耗盡系統的可用埠. 此外, 我建議嘗試使用諸如 wrk
之類更現代的壓測工具.
1.3 火焰圖視覺化生成器
Brendan D. Gregg
的 Flame Graph
工程實現了一套生成火焰圖的指令碼.
Flame Graph
專案位於 GitHub
上
用 git
將其 clone
下來
git clone https://github.com/brendangregg/FlameGraph.git
- 1
生成和建立火焰圖需要如下幾個步驟
流程 | 描述 | 指令碼 |
---|---|---|
捕獲堆疊 | 使用 perf/systemtap/dtrace 等工具抓取程式的執行堆疊 |
perf/systemtap/dtrace |
摺疊堆疊 | trace 工具抓取的系統和程式執行每一時刻的堆疊資訊, 需要對他們進行分析組合, 將重複的堆疊累計在一起, 從而體現出負載和關鍵路徑 |
FlameGraph 中的 stackcollapse 程式 |
生成火焰圖 | 分析 stackcollapse 輸出的堆疊資訊生成火焰圖 | flamegraph.pl |
不同的 trace 工具抓取到的資訊不同, 因此 Flame Graph
提供了一系列的 stackcollapse
工具.
stackcollapse | 描述 |
---|---|
stackcollapse.pl | for DTrace stacks |
stackcollapse-perf.pl | for Linux perf_events “perf script” output |
stackcollapse-pmc.pl | for FreeBSD pmcstat -G stacks |
stackcollapse-stap.pl | for SystemTap stacks |
stackcollapse-instruments.pl | for XCode Instruments |
stackcollapse-vtune.pl | for Intel VTune profiles |
stackcollapse-ljp.awk | for Lightweight Java Profiler |
stackcollapse-jstack.pl | for Java jstack(1) output |
stackcollapse-gdb.pl | for gdb(1) stacks |
stackcollapse-go.pl | for Golang pprof stacks |
stackcollapse-vsprof.pl | for Microsoft Visual Studio profiles |
2 用 perf 生成火焰圖
2.1 perf 採集資料
讓我們從 perf
命令(performance
的縮寫)講起, 它是 Linux
系統原生提供的效能分析工具, 會返回 CPU
正在執行的函式名以及呼叫棧(stack
)
sudo perf record -F 99 -p 3887 -g -- sleep 30
- 1
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
- 1
2.2 生成火焰圖
首先用 perf script
工具對 perf.data
進行解析
# 生成摺疊後的呼叫棧
perf script -i perf.data &> perf.unfold
- 1
- 2
將解析出來的資訊存下來, 供生成火焰圖
首先用 stackcollapse-perf.pl
將 perf 解析出的內容 perf.unfold
中的符號進行摺疊 :
# 生成火焰圖
./stackcollapse-perf.pl perf.unfold &> perf.folded
- 1
- 2
最後生成 svg
圖
./flamegraph.pl perf.folded > perf.svg
- 1
我們可以使用管道將上面的流程簡化為一條命令
perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > process.svg
- 1
3 解析火焰圖
最後就可以用瀏覽器開啟火焰圖進行分析啦.
3.1 火焰圖的含義
火焰圖是基於 stack
資訊生成的 SVG
圖片, 用來展示 CPU
的呼叫棧。
y
軸表示呼叫棧, 每一層都是一個函式. 呼叫棧越深, 火焰就越高, 頂部就是正在執行的函式, 下方都是它的父函式.
x
軸表示抽樣數, 如果一個函式在 x
軸佔據的寬度越寬, 就表示它被抽到的次數多, 即執行的時間長. 注意, x
軸不代表時間, 而是所有的呼叫棧合併後, 按字母順序排列的.
火焰圖就是看頂層的哪個函式佔據的寬度最大. 只要有 “平頂”(plateaus
), 就表示該函式可能存在效能問題。
顏色沒有特殊含義, 因為火焰圖表示的是 CPU
的繁忙程度, 所以一般選擇暖色調.
3.2 互動性
火焰圖是 SVG
圖片, 可以與使用者互動.
- 滑鼠懸浮
火焰的每一層都會標註函式名, 滑鼠懸浮時會顯示完整的函式名、抽樣抽中的次數、佔據總抽樣次數的百分比
- 點選放大
在某一層點選,火焰圖會水平放大,該層會佔據所有寬度,顯示詳細資訊。
左上角會同時顯示 “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
格式)
通常, 在標準的火焰圖中棧幀和棧塔的顏色是隨機選擇的. 而在紅/藍差分火焰圖中, 使用不同的顏色來表示兩個 profile
檔案中的差異部分.
在第二個 profile
中 deflate_slow( )
函式以及它後續呼叫的函式執行的次數要比前一次更多, 所以在上圖中這個棧幀被標為了紅色. 可以看出問題的原因是ZFS的壓縮功能被啟用了, 而在系統升級前這項功能是關閉的.
這個例子過於簡單, 我甚至可以不用差分火焰圖也能分析出來. 但想象一下, 如果是在分析一個微小的效能下降, 比如說小於5%, 而且程式碼也更加複雜的時候, 問題就為那麼好處理了.
4.2 紅藍差分火焰圖簡介
這個事情我已經討論了好幾年了, 最終我自己編寫了一個我個人認為有價值的實現。它的工作原理是這樣的 :
-
抓取修改前的堆疊
profile1
檔案 -
抓取修改後的堆疊
profile2
檔案 -
使用
profile2
來生成火焰圖. (這樣棧幀的寬度就是以profile2
檔案為基準的) -
使用 “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
- 1
- 2
- 3
- 4
- 5
- 6
- 一段時間後 (或者程式程式碼修改後), 抓取 profile 2` 檔案
# 抓取資料
perf record -F 99 -a -g -- sleep 30
# 解析資料生成堆疊資訊
perf script > out.stacks2
# 摺疊堆疊
./stackcollapse-perf.pl ../out.stacks2 > out.folded2
- 1
- 2
- 3
- 4
- 5
- 6
生成紅藍差分火焰圖
./difffolded.pl out.folded1 out.folded2 | ./flamegraph.pl > diff2.svg
- 1
difffolded.pl
只能對 “摺疊” 過的堆疊 profile
檔案進行操作, 摺疊操作 是由前面的 stackcollapse
系列指令碼完成的. 指令碼共輸出 3
列資料, 其中一列代表摺疊的呼叫棧, 另兩列為修改前後 profile
檔案的統計資料.
func_a;func_b;func_c 31 33
[...]
- 1
- 2
在上面的例子中 “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
- 1
這樣, 把前面生成 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
5 參考
-
本作品/博文 ( AderStep-紫夜闌珊-青伶巷草 Copyright ©2013-2017 ), 由 成堅(gatieme) 創作.
-
採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可. 歡迎轉載、使用、重新發布, 但務必保留文章署名成堅gatieme ( 包含連結: http://blog.csdn.net/gatieme ), 不得用於商業目的.
-
基於本文修改後的作品務必以相同的許可釋出. 如有任何疑問,請與我聯絡.
CSDN | GitHub |
---|---|
Linux下用火焰圖進行效能分析 | LDD-LinuxDeviceDrivers/study/debug/tools/perf/flame_graph |