源:http://walkerdu.com/2018/09/13/perf-event/
作為服務器后臺開發,不僅僅要寫業務邏輯,后臺意味著高并發,穩定性,當你寫了很多邏輯,發現性能有問題的時候,也要學會性能分析,進行性能優化, 也許你會接觸很多性能分析工具:valgrind,gperftools,gprof, oprofile, 有時間慢慢一一介紹,在學習perf的過程中,也學習和加深了很多之前的知識,本文拋磚引玉,希望能幫助大家了解一些性能分析更深的一些東西。
1. perf簡介
perf是一款linux內置的性能分析工具,隨著內核發布,也被稱為perf_events, perf tools, PCL(Performance Counters for Linux), 發布于Linux kernel version 2.6.31, perf是怎么工作的呢?perf如何使用?本文是來介紹一下。
perf官方wiki的介紹是:Linux profiling with performance counters
Perf可以解決高級性能和故障排除,它可以分析(當然目前我也只是用來進行cpu性能分析):
- 為什么內核消耗CPU高, 代碼的位置在哪里?
- 什么代碼引起了CPU 2級cache未命中?
- CPU是否消耗在內存I/O上?
- 哪些代碼分配內存,分配了多少?
- 什么觸發了TCP重傳?
- 某個內核函數是否正在被調用,調用頻率多少?
- 線程釋放CPU的原因?
2. perf的event
它可以利用CPU performance counters(性能計數器),tracepoints, kprobes和uprobes來進行應用程序的性能分析。perf使用這些linux內核提供的tracing特性進行事件的采集和分析,perf使用的event主要有以下幾種:
- Hardware Events: CPU中的寄存器含有performance counters(性能計數器),用來統計 Hardware event,例如 cpu-cycles、instructions executed 、cache-misses、branch mispredicted等。這些event構成了應用程序profiling的基礎。
- Software Events: 基于內核計數器的低優先級events, 例如, CPU migrations(處理器遷移次數), minor faults(soft page faults), major faults(hard page faults).
- Tracepoints: 是散落在內核源代碼中的一些 hook,用來調用probe函數,開啟后,它們便可以在特定的代碼被運行到時被觸發,這一特性可以被各種 trace/debug 工具所使用。Perf 就是該特性的用戶之一。假如您想知道在應用程序運行期間,內核內存管理模塊的行為,便可以利用潛伏在 slab 分配器中的 tracepoint。當內核運行到這些 tracepoint 時,便會通知 perf。
- Dynamic Tracing: probe函數(探針or探測函數),kprobe(kernel probe)內核態探針,用來創建和管理內核代碼中的探測點。Uprobes,user-probe,用戶態探針,用來對用戶態應用程序進行探測點的創建和管理,關于kprobe和uprobe可參考對應的內核文檔
- perf使用的event來源,如下圖:來源:brendangregg
3. 安裝
兩種安裝方式
- 通過包管理進行安裝,perf工具在 linux-tools-common工具包里,通過包管理軟件安裝的時候還需要依賴linux-tools-kernelversion包
- 源碼編譯:找到對應內核版本的源碼包,在tools/perf目錄下進行編譯
4. 背景
要想使用perf來分析應用程序,需要了解一些基礎概念,例如符號表, frame pointer等信息
4.1 符號表
應用程序的符號表,用來將邏輯地址翻譯成對應的函數和變量名,這樣才能被程序員閱讀和分析,沒有符號表,profile的結果都是一些16進制的邏輯地址:
如下是perf report分析sshd進程的堆棧調用情況(來自brendangregg):
對于通過軟件包安裝的程序,通常都會有dubug package(-dbgsym)即帶有符號表信息的程序,如果是源碼安裝的就需要編譯時開啟debug選項。
通過使用openssh-server-dbgsym and libc6-dbgsym,sshd的perf report分析結果如下:
4.2 ?;厮?棧展開)
我們經常在編譯的時候會開啟frame pointers優化選項,即優化掉函數棧幀rbp,省略掉frame pointer是編譯器一個很爛的優化,它會破壞debug, 更爛的是省略frame pointer一般是編譯器默認的優化選項。
沒有frame pointer會使perf 無法獲取完整的調用棧,如下示例:
例如上面代碼正常不開啟優化的編譯,進行profile是可以看到call graph的,如下:
如果編譯時開啟-fomit-frame-pointer(這里因為測試代碼簡單,直接開優化的話就優化沒了),就無法獲取到call graph
有三種方法可以修復這個問題,這里不做展開,這些stack walking techniques后面可以寫一篇單獨的文章:
- using dwarf data to unwind the stack, 實際上很多profile工具:gperftools, valgrind都是依賴于libunwind,通過dwarf來進行stack trace的
- using last branch record (LBR) if available (a processor feature)
- returning the frame pointers
5. 使用
perf提供了一系列的命令來分析程序,如下:
sub command功能說明
annotate 讀取perf.data(由perf record生成)顯示注釋信息,如果被分析的進程含義debug符號信息,則會顯示匯編和對應的源碼,否則只顯示匯編代碼
archive 根據perf.data(由perf record生成)文件中的build-id將相關的目標文件打包,方便在其他機器分析
bench perf提供的基準套件的通用框架,可以對當前系統的調度,IPC,內存訪問進行性能評估
buildid-cache 管理build-id,管理對于的bin文件
buildid-list 列出perf.data中的所以buildids
data 把perf.data文件轉換成其他格式diff讀取多個perf.data文件,并給出差異分析
evlist 列出perf.data中采集的事件列表
kmem 分析內核內存的使用kvm分析
kvm 虛擬機上的guest os
list 列出當前系統支持的所有事件名,可分為三類:硬件事件、軟件事件,檢查點
lock 分析內核中的鎖信息,包括鎖的爭用情況,等待延遲等
record 對程序運行過程中的事件進行分析和記錄,并寫入perf.data
report 讀取perf.data(由perf record生成) 并顯示分析結果
sched 針對調度器子系統的分析工具。
script 讀取perf.data(由perf record生成),生成trace記錄,供其他分析工具使用
stat 對程序運行過程中的性能計數器進行統計
test perf對當前軟硬件平臺進行健全性測試,可用此工具測試當前的軟硬件平臺是否能支持perf的所有功能。
timechart 對record結果進行可視化分析輸出,record命令需要加上timechart記錄
top 對系統的性能進行分析,類型top命令,當然可以對單個進程進行分析
probe 用于定義動態檢查點。
trace 類似于strace,跟蹤目標的系統調用,但開銷比strace小
perf的使用大體可以有三種方式:
- Counting:統計的方式,統計事件發生的次數,這種方式不生成perf.data文件,例如perf stat, perf top
- Sampling:采樣的方式,采樣事件,并寫入到內核buffer中,并異步寫入perf.data文件中,perf.data文件可以被perf report或者perf script 命令讀取。
- bpf programs on events(https://www.ibm.com/developerworks/cn/linux/l-lo-eBPF-history/index.html)
5.1. perf list
perf list命令可以列出當前perf可用的事件:
cpu-cycles OR cycles [Hardware event] instructions [Hardware event] cache-references [Hardware event] cache-misses [Hardware event] branch-instructions OR branches [Hardware event] branch-misses [Hardware event] bus-cycles [Hardware event] stalled-cycles-frontend OR idle-cycles-frontend [Hardware event] stalled-cycles-backend OR idle-cycles-backend [Hardware event] ref-cycles [Hardware event] alignment-faults [Software event] bpf-output [Software event] context-switches OR cs [Software event] cpu-clock [Software event] cpu-migrations OR migrations [Software event] dummy [Software event] emulation-faults [Software event] major-faults [Software event] minor-faults [Software event] page-faults OR faults [Software event] task-clock [Software event] msr/tsc/ [Kernel PMU event] rNNN [Raw hardware event descriptor] cpu/t1=v1[,t2=v2,t3 ...]/modifier [Raw hardware event descriptor] (see 'man perf-list' on how to encode it) mem:<addr>[/len][:access] [Hardware breakpoint]
這些事件可以分為三類(在文章開始介紹perf工作原理的時候也說了):Hardware Event, Software event, Tracepoint event.
每個具體事件的含義在perf_event_open的man page中有說明:
- cpu-cycles:統計cpu周期數,cpu周期:指一條指令的操作時間。
- instructions: 機器指令數目
- cache-references: cache命中次數
- cache-misses: cache失效次數
- branch-instructions: 分支預測成功次數
- branch-misses: 分支預測失敗次數
- alignment-faults: 統計內存對齊錯誤發生的次數, 當訪問的非對齊的內存地址時,內核會進行處理,已保存不會發生問題,但會降低性能
- context-switches: 上下文切換次數,
- cpu-clock: cpu clock的統計,每個cpu都有一個高精度定時器
- task-clock :cpu clock中有task運行的統計
- cpu-migrations:進程運行過程中從一個cpu遷移到另一cpu的次數
- page-faults: 頁錯誤的統計
- major-faults:頁錯誤,內存頁已經被swap到硬盤上,需要I/O換回
- minor-faults :頁錯誤,內存頁在物理內存中,只是沒有和邏輯頁進行映射
5.2. perf stat
perf stat可以對程序運行過程中的性能計數器(包括Hardware,software counters)進行統計,分析程序的整體消耗情況:
$perf stat ls Performance counter stats for 'ls': 2.164836 task-clock (msec) # 0.808 CPUs utilized 51 context-switches # 0.024 M/sec 4 cpu-migrations # 0.002 M/sec 333 page-faults # 0.154 M/sec 5506056 # 2.543 GHz 0 stalled-cycles-frontend # 0.00% frontend cycles idle 0 stalled-cycles-backend # 0.00% backend cycles idle 6100570 instructions # 1.11 insns per cycle 1298744 branches # 599.927 M/sec 18509 branch-misses # 1.43% of all branches 0.002679758 seconds time elapsed
具體各個字段的含義在perf list中已經列出了,這里分析一下重要的數據:
- task-clock (msec): cpu處理task所消耗的時間,單位ms,0.808 CPUs utilized的表示cpu使用率為80.8% = 2.164836 / 2.679758. 該值越高代表程序是CPU bound而非IO bound 類型。
- instructions:執行的指令條數, insns per cycle: 即IPC,每個cpu周期執行的指令條數,1.11 = 6100570(instructions) / 5506056(cycles),IPC比上面的CPU使用率更能說明CPU的使用情況,關于IPC有一篇brendangregg的文章,很好的說明CPU使用率不是一個很好的性能分析指標
- stalled-cycles-frontend和stalled-cycles-backend表示CPU停滯統計,具體可參考
perf stat可以加上-e選項來設置自己關心的事件統計,具體參數可以通過上面的perf list來查看
5.3. perf top
perf top類似系統的命令top,可以實時的查看當前系統各個進程的各個函數的性能計數分析:$sudo perf top
perf top的和perf record的功能參數差不多,我們可以加上-e:來統計特定的event,-g: 開啟call-graph, -p:分析特定的進程
平常我們用的更多的是perf record,因為它可以將profiler的結果輸出到文件,然后通過perf report來進行分析,或者通過可視化工具進行分析和輸出。所以下面著重介紹perf record
5.4. perf record
perf reord可以運行一個命令,但更多的是對已運行的進程進行性能分析,并將分析結果輸出到perf.data(默認該文件名)中。不會像perf top一樣實時輸出分析結果。輸出perf.data可以通過perf report進行分析,或者通過perf script輸出結果給第三方輸出可視化的視圖。用前面的示例代碼進行測試如下:
$perf record -g -F 99 ./a.out $perf report
perf record幾個總要參數說明一下:
- -F: 事件采樣的頻率, 單位HZ, 更高的頻率會獲得更細的統計,但會帶來更多的開銷
- -g: 進行堆棧追蹤,生成調用關系圖,等價于–call-graph, 默認情況下,-g等同于–call-graph fp,即通過frame pointer來進行堆棧追蹤。 如果frame pointer被優化掉的話,可以通過dwarf, lbr進行堆棧追蹤
- sleep: 采樣的時間
5.5. perf可視化分析
perf 提供了內置的可視化分析工具 perf timechart,例如:
$perf timechart record ./a.out $perf timechart
perf timechart輸出的是進程運行過程中系統調度的情況,無法對程序的具體代碼段進行性能分析,但可以看出總結運行情況:running,idle,I/O等,
Brendangregg寫了兩款對perf采樣結果進行可視化分析的開源工具:Flame Graphs和HeatMap. FlameGraphs即所謂的火焰圖,是大家使用比較多的工具,能清晰的展示程序各個函數的性能消耗。HeatMap可以從采樣數據中的延遲數據來進行消耗展示。
下面是對之前實例代碼的采樣數據perf.data進行FlameGraphs的繪制:
$perf script |./stackcollapse-perf.pl|./flamegraph.pl > fg_output.svg
perf_flamegraph.png
6. 參考
Perf官方wiki
https://perf.wiki.kernel.org/index.php/Main_Page
Perf 維基百科
https://en.wikipedia.org/wiki/Perf_(Linux)
Perf – Linux下的系統性能調優工具,第 1 部分
https://www.ibm.com/developerworks/cn/linux/l-cn-perf1/index.html
Linux 效能分析工具: Perf
http://wiki.csie.ncku.edu.tw/embedded/perf-tutorial
Linux內核調試技術——kprobe使用與實現
https://blog.csdn.net/luckyApple1028/article/details/52972315
內核uprobes使用介紹
https://blog.csdn.net/badu_123/article/details/8302642
perf Examples
http://www.brendangregg.com/perf.html
火焰圖的使用
http://www.brendangregg.com/flamegraphs.html
https://github.com/brendangregg/FlameGraph
頁錯誤
https://yq.aliyun.com/articles/55820
http://cwndmiao.github.io/programming%20tools/2013/11/26/Dwarf/