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

  • 2019 年 10 月 10 日
  • 筆記

轉自:https://blog.csdn.net/gatieme/article/details/78885908

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

本作品採用知識共享署名-非商業性使用-相同方式共享 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上

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