什么是火焰圖
火焰圖(Flame Graph)是由Linux性能優(yōu)化大師Brendan Gregg發(fā)明的,和所有其他的trace和profiling方法不同的是,F(xiàn)lame Graph以一個(gè)全局的視野來看待時(shí)間分布,它從底部往頂部,列出所有可能的調(diào)用棧。其他的呈現(xiàn)方法,一般只能列出單一的調(diào)用棧或者非層次化的時(shí)間分布。
我最快樂的童年時(shí)代,每逢冬天,尤其是春節(jié)的時(shí)候,和一家人圍坐在火堆旁邊烤火。這已經(jīng)成為最美好的回憶,其實(shí)人生追求的快樂非常簡(jiǎn)單?;鹧鎴D的火焰首先來自于根,然后以火苗的形式往上面竄??梢园褟目拷孛娴母巾斏系拿總€(gè)火苗,想想成一個(gè)調(diào)用棧。由于火苗有很多根,這正好也和現(xiàn)實(shí)生活中程序的執(zhí)行邏輯相似。
以典型的分析CPU時(shí)間花費(fèi)到哪個(gè)函數(shù)的on-cpu火焰圖為例來展開。
CPU火焰圖中的每一個(gè)方框是一個(gè)函數(shù),方框的長(zhǎng)度,代表了它的執(zhí)行時(shí)間,所以越寬的函數(shù),執(zhí)行越久?;鹧鎴D的樓層每高一層,就是更深一級(jí)的函數(shù)被調(diào)用,最頂層的函數(shù),是葉子函數(shù)。
火焰圖的生成過程是:
先trace系統(tǒng),獲取系統(tǒng)的profiling數(shù)據(jù)
用腳本來繪制
系統(tǒng)的profiling數(shù)據(jù)獲取,可以選擇最流行的perf record,而后把采集的數(shù)據(jù)進(jìn)行加工處理,繪制為火焰圖。其中第二步的繪制火焰圖的腳本程序,通過如下方式獲取:
gitclone https://github.com/brendangregg/FlameGraph
火焰圖案例
廢話不多說,直接從最簡(jiǎn)單的例子開始說起。talk is cheap, show you the cde,代碼如下:
c()
{
for(int i=0;i<1000;i++);
}
b()
{
for(int i=0;i<1000;i++);
c();
}
a()
{
for(int i=0;i<1000;i++);
b();
}
則這三個(gè)函數(shù),在火焰圖中呈現(xiàn)的樣子為:

a()的2/3的時(shí)間花在b()上面,而b()的1/3的時(shí)間花在c()上面。很多個(gè)這樣的a->b->c的火苗堆在一起,就構(gòu)成了火焰圖。

進(jìn)一步理解火焰圖的最好方法仍然是通過一個(gè)實(shí)際的案例,下面的程序創(chuàng)建2個(gè)線程,兩個(gè)線程的handler都是thread_fun(),之后thread_fun()調(diào)用fun_a()、fun_b()、fun_c(),而fun_a()又會(huì)調(diào)用fun_d():
/*
* One example to demo flamegraph
*
* Copyright (c) Barry Song
*
* Licensed under GPLv2
*/
#include
func_d()
{
int i;
for(i=0;i<50000;i++);
}
func_a()
{
int i;
for(i=0;i<100000;i++);
func_d();
}
func_b()
{
int i;
for(i=0;i<200000;i++);
}
func_c()
{
int i;
for(i=0;i<300000;i++);
}
void* thread_fun(void* param)
{
while(1) {
int i;
for(i=0;i<100000;i++);
func_a();
func_b();
func_c();
}
}
int main(void)
{
pthread_t tid1,tid2;
int ret;
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){
...
}
return 0;
}
先看看不用火焰圖的缺點(diǎn)在哪里。
如果不用火焰圖,我們也可以用類似perf top這樣的工具分析出來CPU時(shí)間主要花費(fèi)在哪里了:
$gcc exam.c -pthread
$./a.out&
$sudo perf top
perf top的顯示結(jié)果如下:

perf top提示出來了fun_a()、fun_b()、fun_c(), fun_d(),thread_func()這些函數(shù)內(nèi)部的代碼是CPU消耗大戶,但是它缺乏一個(gè)全局的視野,我們無法看出全局的調(diào)用棧,也弄不清楚這些函數(shù)之間的關(guān)系。火焰圖則不然,我們用下面的命令可以生成火焰圖(以root權(quán)限運(yùn)行):
perf record -F 99 -a -g -- sleep 60
perf script | ./stackcollapse-perf.pl > out.perf-folded
./flamegraph.pl out.perf-folded > perf-kernel.svg
上述程序捕獲系統(tǒng)的行為60秒鐘,最后調(diào)用flamegraph.pl生成一個(gè)火焰圖perf-kernel.svg,用看圖片的工具就可以打開這個(gè)svg。

上述火焰圖顯示出了a.out中,thread_func()、func_a()、func_b()、fun_c()和func_d()的時(shí)間分布。
從上述火焰圖可以看出,雖然thread_func()被兩個(gè)線程調(diào)用,但是由于thread_func()之前的調(diào)用棧是一樣的,所以2個(gè)線程的thread_func()調(diào)用是合并為同一個(gè)方框的。
更深閱讀
除了on-cpu的火焰圖以外,off-cpu的火焰圖,對(duì)于分析系統(tǒng)堵在IO、SWAP、取得鎖方面的幫助很大,有利于分析系統(tǒng)在運(yùn)行的時(shí)候究竟在等待什么,系統(tǒng)資源之間的彼此伊伴。
比如,下面的火焰圖顯示,nginx的吞吐能力上不來的很多程度原因在于sem_wait()等待信號(hào)量。

上圖摘自Yichun Zhang (agentzh)的《Introduction to offCPU Time Flame Graphs》。
關(guān)于火焰圖的更多細(xì)節(jié)和更多種火焰圖各自的功能,可以訪問:
http://www.brendangregg.com/flamegraphs.html
-
Linux
+關(guān)注
關(guān)注
88文章
11822瀏覽量
219602 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4422瀏覽量
67873
原文標(biāo)題:火焰圖:全局視野的Linux性能剖析
文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
AD7679:高性能18位SAR ADC的深度剖析
12 位高速 ADC AD9432:性能剖析與應(yīng)用指南
AD7327:高性能12位ADC的深度剖析與應(yīng)用指南
AD9625:高性能12位ADC的深度剖析與應(yīng)用指南
AD9081:高性能混合信號(hào)前端的技術(shù)剖析
昉·星光開發(fā)板火焰傳感器探測(cè)火光
內(nèi)核配置項(xiàng)引發(fā)網(wǎng)絡(luò)性能下降的深度剖析
瑞芯微(EASY EAI)RV1126B 火焰檢測(cè)
火焰圖:全局視野的Linux性能剖析
評(píng)論