牛逼的Linux性能剖析—perf

原文鏈接:http://m.sohu.com/a/292825608_467784

系統級性能優化通常包括兩個階段:性能剖析(performance profiling)和代碼優化。性能剖析的目標是尋找性能瓶頸,查找引發性能問題的原因及熱點代碼。代碼優化的目標是針對具體性能問題而優化代碼或編譯選項,以改善軟件性能。本篇主要講性能分析中常用的工具——perf。

perf是一款Linux性能分析工具。Linux性能計數器是一個新的基於內核的子系統,它提供一個性能分析框架,比如硬件(CPU、PMU(Performance Monitoring Unit))功能和軟件(軟件計數器、tracepoint)功能。通過perf,應用程序可以利用PMU、tracepoint和內核中的計數器來進行性能統計。它不但可以分析制定應用程序的性能問題(per thread),也可以用來分析內核的性能問題。

總之perf是一款很牛逼的綜合性分析工具,大到系統全局性性能,再小到進程線程級別,甚至到函數及彙編級別。

調優方向

可以從以下三種事件爲調優方向:

  • Hardware Event由PMU部件產生,在特定的條件下探測性能事件是否發生以及發生的次數。比如cache命中。
  • Software Event是內核產生的事件,分佈在各個功能模塊中,統計和操作系統相關性能事件。比如進程切換,tick數等。
  • Tracepoint Event是內核中靜態tracepoint所觸發的事件,這些tracepoint用來判斷程序運行期間內核的行爲細節(這些tracepint的對應的sysfs節點在/sys/kernel/debug/tracing/events目錄下)。比如slab分配器的分配次數等。

perf 的使用

序號 命令 作用

1	annotate	解析perf record生成的perf.data文件,顯示被註釋的代碼。
2	archive	根據數據文件記錄的build-id,將所有被採樣到的elf文件打包。利用此壓縮包,可以再任何機器上分析數據文件中記錄的採樣數據。
3	bench	perf中內置的benchmark,目前包括兩套針對調度器和內存管理子系統的benchmark。
4	buildid-cache	管理perf的buildid緩存,每個elf文件都有一個獨一無二的buildid。buildid被perf用來關聯性能數據與elf文件。
5	buildid-list	列出數據文件中記錄的所有buildid。
6	diff	對比兩個數據文件的差異。能夠給出每個符號(函數)在熱點分析上的具體差異。
7	evlist	列出數據文件perf.data中所有性能事件。
8	inject	該工具讀取perf record工具記錄的事件流,並將其定向到標準輸出。在被分析代碼中的任何一點,都可以向事件流中注入其它事件。
9	kmem	針對內核內存(slab)子系統進行追蹤測量的工具
10	kvm	用來追蹤測試運行在KVM虛擬機上的Guest OS。
11	list	列出當前系統支持的所有性能事件。包括硬件性能事件、軟件性能事件以及檢查點。
12	lock	分析內核中的鎖信息,包括鎖的爭用情況,等待延遲等。
13	mem	內存存取情況
14	record	收集採樣信息,並將其記錄在數據文件中。隨後可通過其它工具對數據文件進行分析。
15	report	讀取perf record創建的數據文件,並給出熱點分析結果。
16	sched	針對調度器子系統的分析工具。
17		執行perl或python寫的功能擴展腳本、生成腳本框架、讀取數據文件中的數據信息等。
18	stat	執行某個命令,收集特定進程的性能概況,包括CPI、Cache丟失率等。
19	test	perf對當前軟硬件平臺進行健全性測試,可用此工具測試當前的軟硬件平臺是否能支持perf的所有功能。
20	timechart	針對測試期間系統行爲進行可視化的工具
21	top	類似於linux的top命令,對系統性能進行實時分析。
22	trace	關於syscall的工具。
23	probe	用於定義動態檢查點。

全局性概況:

perf list查看當前系統支持的性能事件;

perf bench對系統性能進行摸底;

perf test對系統進行健全性測試;

perf stat對全局性能進行統計;

perf list查看當前系統支持的性能事件;

perf bench對系統性能進行摸底;

perf test對系統進行健全性測試;

perf stat對全局性能進行統計;

全局細節:

perf top可以實時查看當前系統進程函數佔用率情況;

perf probe可以自定義動態事件;

perf top可以實時查看當前系統進程函數佔用率情況;

perf probe可以自定義動態事件;

特定功能分析:

perf kmem針對slab子系統性能分析;

perf kvm針對kvm虛擬化分析;

perf lock分析鎖性能;

perf mem分析內存slab性能;

perf sched分析內核調度器性能;

perf trace記錄系統調用軌跡;

perf kmem針對slab子系統性能分析;

perf kvm針對kvm虛擬化分析;

perf lock分析鎖性能;

perf mem分析內存slab性能;

perf sched分析內核調度器性能;

perf trace記錄系統調用軌跡;

最常用功能perf record,可以系統全局,也可以具體到某個進程,更甚具體到某一進程某一事件;可宏觀,也可以很微觀。

pref record記錄信息到perf.data;

perf report生成報告;

perf diff對兩個記錄進行diff;

perf evlist列出記錄的性能事件;

perf annotate顯示perf.data函數代碼;

perf archive將相關符號打包,方便在其它機器進行分析;

perf 將perf.data輸出可讀性文本;

pref record記錄信息到perf.data;

perf report生成報告;

perf diff對兩個記錄進行diff;

perf evlist列出記錄的性能事件;

perf annotate顯示perf.data函數代碼;

perf archive將相關符號打包,方便在其它機器進行分析;

perf 將perf.data輸出可讀性文本;

可視化工具perf timechart

perf timechart record記錄事件;

perf timechart生成output.svg文檔;

perf timechart record記錄事件;

perf timechart生成output.svg文檔;

火焰圖

火焰圖(Flame Graph)是由Linux性能優化大師Brendan Gregg發明的,和所有其他的trace和profiling方法不同的是,Flame Graph以一個全局的視野來看待時間分佈,它從底部往頂部,列出所有可能的調用棧。其他的呈現方法,一般只能列出單一的調用棧或者非層次化的時間分佈。

以典型的分析CPU時間花費到哪個函數的on-cpu火焰圖爲例來展開。CPU火焰圖中的每一個方框是一個函數,方框的長度,代表了它的執行時間,所以越寬的函數,執行越久。火焰圖的樓層每高一層,就是更深一級的函數被調用,最頂層的函數,是葉子函數。

火焰圖的生成過程是:

先trace系統,獲取系統的profiling數據
用腳本來繪製
腳本獲取:git clone https://github.com/brendangregg/FlameGraph

下面通過實例來體驗以下火焰圖的生成過程:

#include<pthread.h>

func_d()

{

inti;

for(i= 0;i< 50000;i++);

}

func_a()

{

inti;

for(i= 0;i< 100000;i++);

func_d();

}

func_b()

{

inti;

for(i= 0;i< 200000;i++);

}

func_c()

{

inti;

for(i= 0;i< 300000;i++);

}

void* thread_fun(void* param)

{

while( 1) {

inti;

for(i= 0;i< 100000;i++);

func_a();

func_b();

func_c();

}

}

intmain(void)

{

pthread_ttid1,tid2;

intret;

ret=pthread_create(&tid1, NULL,thread_fun, NULL);

if(ret== -1){

...

}

ret=pthread_create(&tid2, NULL,thread_fun, NULL);

...

if(pthread_join(tid1, NULL)!= 0){

...

}

if(pthread_join(tid2, NULL)!= 0){

...

}

return0;

}

先用類似perf top分析出來CPU時間主要花費在哪裏:

$ gcc test.c -pthread

$ ./a.out&

$ sudo perf top

perf top提示出來了fun_a()、fun_b()、fun_c(), fun_d(),thread_func()這些函數內部的代碼是CPU消耗大戶,但是它缺乏一個全局的視野,我們無法看出全局的調用棧,也弄不清楚這些函數之間的關係。火焰圖則不然,我們用下面的命令可以生成火焰圖(以root權限運行):

$ perf record -F 99 -a -g -- sleep 60

$ perf | ./stackcollapse-perf.pl > out.perf-folded

$ ./flamegraph.pl out.perf-folded > perf-kernel.svg

上述程序捕獲系統的行爲60秒鐘,最後調用flamegraph.pl生成一個火焰圖perf-kernel.svg,用看圖片的工具就可以打開這個svg。

我們以perf爲例,看一下flamegraph的使用方法:

1、第一步

$sudo perf record -e cpu-clock -g -p 28591

Ctrl+c結束執行後,在當前目錄下會生成採樣數據perf.data.

2、第二步

用perf script工具對perf.data進行解析

perf script -i perf.data &> perf.unfold

3、第三步

將perf.unfold中的符號進行摺疊:

#./stackcollapse-perf.pl perf.unfold &> perf.folded

4、最後生成svg圖:

./flamegraph.pl perf.folded > perf.svg

在這裏插入圖片描述

上述火焰圖顯示出了a.out中,thread_func()、func_a()、func_b()、fun_c()和func_d()的時間分佈。

從上述火焰圖可以看出,雖然thread_func()被兩個線程調用,但是由於thread_func()之前的調用棧是一樣的,所以2個線程的thread_func()調用是合併爲同一個方框的。

除了on-cpu的火焰圖以外,off-cpu的火焰圖,對於分析系統堵在IO、SWAP、取得鎖方面的幫助很大,有利於分析系統在運行的時候究竟在等待什麼,系統資源之間的彼此伊伴。

比如,下面的火焰圖顯示,nginx的吞吐能力上不來的很多程度原因在於sem_wait()等待信號量。

關於火焰圖的更多細節和更多種火焰圖各自的功能,可以訪問:

http://www.brendangregg.com/flamegraphs.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章