Linux下用火焰圖進行性能分析【轉】

轉自:http://www.javashuo.com/article/p-obhbbsfv-m.htmlhtml

版權聲明:本文爲博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接和本聲明。
本文連接:https://blog.csdn.net/gatieme/article/details/78885908
CSDN GitHub
Linux下用火焰圖進行性能分析 LDD-LinuxDeviceDrivers/study/debug/tools/perf/flame_graphjava

 


本做品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可, 轉載請註明出處, 謝謝合做linux

因本人技術水平和知識面有限, 內容若有紕漏或者須要修正的地方, 歡迎你們指正, 也歡迎你們提供一些其餘好的調試工具以供收錄, 鄙人在此謝謝啦nginx

 

軟件的性能分析, 每每須要查看 CPU 耗時, 瞭解瓶頸在哪裏.git

火焰圖(flame graph) 是性能分析的利器github

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上

https://github.com/brendangregg/FlameGraph

用 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 參考
使用linux perf工具生成java程序火焰圖

使用perf生成Flame Graph(火焰圖)

大神brendangregg的站點

 

本做品/博文 ( AderStep-紫夜闌珊-青伶巷草 Copyright ©2013-2017 ), 由 成堅(gatieme) 創做.

採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可. 歡迎轉載、使用、從新發布, 但務必保留文章署名成堅gatieme ( 包含連接: http://blog.csdn.net/gatieme ), 不得用於商業目的.

基於本文修改後的做品務必以相同的許可發佈. 若有任何疑問,請與我聯繫.————————————————版權聲明:本文爲CSDN博主「JeanCheng」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。原文連接:https://blog.csdn.net/gatieme/article/details/78885908

相關文章
相關標籤/搜索