性能文章>火焰图:全局视野的Linux性能剖析>

火焰图:全局视野的Linux性能剖析原创

1年前
699618

什么是火焰图

火焰图(Flame Graph)是由Linux性能优化大师Brendan Gregg发明的,和所有其他的trace和profiling方法不同的是,Flame Graph以一个全局的视野来看待时间分布,它从底部往顶部,列出所有可能的调用栈。其他的呈现方法,一般只能列出单一的调用栈或者非层次化的时间分布。

我最快乐的童年时代,每逢冬天,尤其是春节的时候,和一家人围坐在火堆旁边烤火。这已经成为最美好的回忆,其实人生追求的快乐非常简单。火焰图的火焰首先来自于根,然后以火苗的形式往上面窜。可以把从靠近地面的根到顶上的每个火苗,想想成一个调用栈。由于火苗有很多根,这正好也和现实生活中程序的执行逻辑相似。

image.png

以典型的分析CPU时间花费到哪个函数的on-cpu火焰图为例来展开。

CPU火焰图中的每一个方框是一个函数,方框的长度,代表了它的执行时间,所以越宽的函数,执行越久。火焰图的楼层每高一层,就是更深一级的函数被调用,最顶层的函数,是叶子函数。

image.png

火焰图的生成过程是:

先trace系统,获取系统的profiling数据

用脚本来绘制

系统的profiling数据获取,可以选择最流行的perf record,而后把采集的数据进行加工处理,绘制为火焰图。其中第二步的绘制火焰图的脚本程序,通过如下方式获取:

git clone https://github.com/brendangregg/FlameGraph

火焰图案例

废话不多说,直接从最简单的例子开始说起。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();

}

则这三个函数,在火焰图中呈现的样子为:

image.png

a()的2/3的时间花在b()上面,而b()的1/3的时间花在c()上面。很多个这样的a->b->c的火苗堆在一起,就构成了火焰图。

image.png

进一步理解火焰图的最好方法仍然是通过一个实际的案例,下面的程序创建2个线程,两个线程的handler都是thread_fun(),之后thread_fun()调用fun_a()、fun_b()、fun_c(),而fun_a()又会调用fun_d():

/*

 * One example to demo flamegraph

 *

 * Copyright (c) Barry Song

 *

 * Licensed under GPLv2

 */



#include <pthread.h>



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;

}

先看看不用火焰图的缺点在哪里。

如果不用火焰图,我们也可以用类似perf top这样的工具分析出来CPU时间主要花费在哪里了:

$gcc exam.c -pthread

$./a.out&

$sudo perf top

perf top的显示结果如下:

image.png

perf top提示出来了fun_a()、fun_b()、fun_c(), fun_d(),thread_func()这些函数内部的代码是CPU消耗大户,但是它缺乏一个全局的视野,我们无法看出全局的调用栈,也弄不清楚这些函数之间的关系。火焰图则不然,我们用下面的命令可以生成火焰图(以root权限运行):

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

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

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

上述程序捕获系统的行为60秒钟,最后调用flamegraph.pl生成一个火焰图perf-kernel.svg,用看图片的工具就可以打开这个svg。

image.png

上述火焰图显示出了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()等待信号量。

image.png

本文来自公众号:Linux阅码场,作者:宋宝华,10几年的Linux开发经验。“十佳原创精品”图书《Linux设备驱动开发详解》的作者和《Essential Linux Device Driver》的译者。

请先登录,再评论

很棒

1年前

为你推荐

在调试器里看LINUX内核态栈溢出
图灵最先发明了栈,但没有给它取名字。德国人鲍尔也“发明”了栈,取名叫酒窖。澳大利亚人汉布林也“发明”了栈,取名叫弹夹。1959年,戴克斯特拉在度假时想到了Stack这个名字,后来被广泛使用。
LONG究竟有多长,从皇帝的新衣到海康SDK
转眼之间初中毕业30年了,但我仍清楚的记得初中英语的一篇课文,题目叫《皇帝的新装》(“The king’s new clothes”)。这篇课文的前两句话是:”Long long ago, there
雕刻在LINUX内核中的LINUS故事
因为LINUX操作系统的流行,Linus 已经成为地球人都知道的名人。虽然大家可能都听过钱钟书先生的名言:“假如你吃个鸡蛋觉得味道不错,又何必认识那个下蛋的母鸡呢?” 但是如果真是遇到一个“特别显赫”
如何使用Linux内核中没有被导出的变量或函数?
本文详细介绍了使用EXPORT_SYMBOL宏导出函数或变量、使用kallsyms_lookup_name()查找函数或变量的虚拟地址以及内核模块中直接使用内核函数的虚拟地址等3种方案解决没有被EXPORT_SYMBOL 相关的宏导出的变量或函数不能直接使用的问题
LINUX网络子系统中DMA机制的实现
我们先从计算机组成原理的层面介绍DMA,再简单介绍Linux网络子系统的DMA机制是如何的实现的。 计算机组成原理中的DMA 以往的I/O设备和主存交换信息都要经过CPU的操作。不论是最早的轮询方式,
内存泄漏(增长)火焰图
本文总结了在分析内存增长和内存泄漏问题用到的4种追踪方法得到有关内存使用情况的代码路径,使用栈追踪技术对代码路径进行检查,并且会以火焰图的形式把它们可视化输出,在Linux上演示分析过程,随后概述其它系统的情况。
为什么容器内存占用居高不下,频频 OOM(续)
在之前的文章《[为什么容器内存占用居高不下,频频 OOM](https://heapdump.cn/article/1589003)》 中,我根据现状进行了分析和说明,收到了很多读者的建议和疑
通过生产者与消费者模型感受死锁
一. 实验目的及实验环境 1.实验目的通过观察、分析实验现象,深入理解产生死锁的原因,学会分析死锁的方法, 并利用 pstack、 gdb 或 core 文件分析( valgrind (DRD+Hel