火焰图性能监测

准备工作——安装perf

我是在github的codespace上搞的,可以按下面的方式安装perf:

sudo apt install linux-tools-generic
sudo apt install linux-cloud-tools-azure

参考在WSL2中使用perf性能剖析工具

测试程序——简单的C程序

弄一个无限循环的C程序进行测试:

// main.c
#include

int add(int a, int b){ return a + b; }

int sub(int a, int b){ return a - b; }

int main(){
    while(1){
        sub(add(3, 4), 5);
    }
    return 0;
}

编译成可执行文件gcc main.c -o main并运行。
获取这个进程的pid,用htop命令查看一下./main对应的进程:
火焰图性能监测_第1张图片
这里对应的pid是19647

监测并生成火焰图

使用perf程序进行监测:

sudo perf record -F 99 -p 19647 -a -g -- sleep 10 

这里将采样频率设置为99次/秒,最高好像是100次,为保证准确,一般设置为99次。

该命令会生成perf.data文件,记录了进程的函数调用情况,随后从github将FlameGraph克隆到本地,执行如下命令:

sudo perf script > out.perf
../FlameGraph/stackcollapse-perf.pl out.perf > out.folded
./FlameGraph/flamegraph.pl out.folded > "./flamegraph.svg"

生成的./flamegraph.svg可以在vscode中使用扩展SVG进行预览,右击./flamegraph.svg进行预览就出来了,还是很方便的。

生成火焰图如下:
火焰图性能监测_第2张图片
我们的主要函数依然是main,add,sub,在编译的过程中,他们的名称可能改变,比如C++中可能会添加前缀或下划线什么的。其中add和sub函数占用最多的计算资源,两者的比例分别为24.69%和30.04%,采样次数分别为2,293,939,370次和3,494,949,460次,在以效率著称的C语言中这很明显。

后面我测了一下python,相同的代码,add和sub分别被改名为long_add和long_sub,而两者占用的时间比例仅为1.12%和1.22%!!!采样次数分别为111,111,110次和131,313,130次。这里采样的时间是相同的,均为10s。python做了挺多额外工作的,毕竟是解释型语言,python脚本并不直接作为程序,而是作为解释器的一部分(不过也可以用编译的方式来运行)。

python程序用perf生成的火焰图里面的名字会变得很奇怪,甚至不知道对应的是什么函数,用py-spy工具会好一些。这个工具很方便,通过pip install py-spy即可安装,然后通过py-spy record --pid 130526 -o out.svg 就可以直接生成火焰图,很便捷。

这里要注意以下采样次数的意思,类似于频率的意思,采样是每隔一小段时间观测一下,如果这次观测中监测到了sub函数,则sub函数的次数加一。sub(3,494,949,460 samples, 30.04%),表示在10s内遇到过这个函数3,494,949,460次。由于是隔一段时间采样一下,所以难免有遗漏,但在大量的采用中还是具有统计意义的。

这在add和sub函数之后还有一些冒尖的函数,放大左右两边冒尖的函数情况如下:
火焰图性能监测_第3张图片
虽然我们的C测试程序主要是main,add,sub三个函数,但编译链接的过程中,会将其他相关的包链接到程序中,使程序中的符号增加。所以程序执行的过程中,还会涉及到其他的一些函数,比如上面的asm_sysvec_reschedule_ipi 用于处理系统调度的中断处理函数。它负责在特定的时间间隔内触发调度器,使得其他进程有机会运行。
exit_to_user_mode_prepare用于准备切换到用户模式的操作。在操作系统中,内核和用户模式之间的切换需要进行一些准备工作,这个函数就负责执行这些准备操作。

因为这个进程是开始一段时间后再监测的,所以有一些函数没有抓取到,通过readelf命令,我们可以看到main可执行文件中涉及到的所有符号:

$ readelf main -s

Symbol table '.dynsym' contains 6 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     5: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (2)

Symbol table '.symtab' contains 64 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000318     0 SECTION LOCAL  DEFAULT    1 
     2: 0000000000000338     0 SECTION LOCAL  DEFAULT    2 
	...
    58: 0000000000004010     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start
    59: 0000000000001157    37 FUNC    GLOBAL DEFAULT   14 main
    60: 0000000000004010     0 OBJECT  GLOBAL HIDDEN    23 __TMC_END__
    61: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
    62: 0000000000001141    22 FUNC    GLOBAL DEFAULT   14 sub
    63: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@@GLIBC_2.2

如果要了解进程更详细的情况,还可以去/proc文件夹下面看看,每个进程都建立了对应的一个文件夹,文件夹名称对应进程的pid。
比如通过ll /proc/19647/cwd可以查看该进程的工作目录,通过ll /proc/19647/exe可以查看该进程对应的执行文件。

最后,为便于下次使用,我将生成火焰图的过程整理成脚本perf.sh,直接执行bash perf.sh pid就好了,生成的火焰图文件名为flamegraph_pid_$pid.svgperf.sh脚本内容如下:

#!/bin/bash

# Check if the number of arguments is correct
if [ $# -ne 1 ]; then
    echo "Usage: $0 "
    exit 1
fi

pid=$1
mkdir -p temp

# Check if FlameGraph folder exists
if [ ! -d "FlameGraph" ]; then
    # Clone FlameGraph repository
    git clone https://github.com/brendangregg/FlameGraph.git
fi

cd temp || exit
sudo perf record -F 99 -p $pid -a -g -- sleep 10 
sudo perf script > out.perf
current_time=$(date + "%Y%m%d%H%M%S")
mv perf.data "perf_pid_${pid}_$current_time.data"

../FlameGraph/stackcollapse-perf.pl out.perf > out.folded
../FlameGraph/flamegraph.pl out.folded > "../flamegraph_pid_$pid.svg"

你可能感兴趣的:(linux)