Linux下性能检测工具:gprof

1、简介

嵌入式C/C++软件开发&测试过程中,经常遇到某个模块软件运行耗时长,导致影响用户使用,或使业务性能指标劣化情况等。这个时候,我们就需要特别关注程序的性能。如何才能更好地优化程序性能呢?首先我们必须找到性能瓶颈点。在linux系统平台上,为了找到关键路径,我们可以利用profilng技术,使用gprofoprofile工具。

gprofGNU binutils工具之一,默认情况下linux系统当中都带有这个工具。在编译的时候,在每个用户态函数的出入口加入profiling代码,即在每个函数中都加入了一个名为mcountor "_mcount" , or "__mcount" , 依赖于编译器或操作系统)的函数。当应用程序运行时,每一个函数都会调用mcount,mcount会在内存中保存一张函数调用图,并通过函数调用堆栈的形式查找子函数和父函数的地址。这张调用图也保存了所有与函数相关的执行时间,调用次数,调用关系等信息,简单易懂。

gprof适合于查找用户态程序的性能瓶颈,对于很长时间都运行在内核态的程序,gprof不适合。

2gprof使用方法

假设C文件test.c内容如下:

#include <stdio.h>

 

void func_print(void)

{

   printf("123\b\b\b");

   return;

}

 

int func_loop(int x, int y)

{

   int i = 0;

   int j = 0;

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

   {

       for (j = 0; j < y; j++)

       {

           func_print();

           printf("abc\b\b\b");

       }

   }

   return x * y;

}

 

void my_print(int x, int y)

{

   printf("[gprof]: x=%d, y=%d\n",x, y);

   return;

}

 

void main(int argc, char*argv[])

{

   int i = 0;

   const int x = 10;

   const int y = 20000;

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

   {

       func_loop(x, y);

   }

   my_print(x, y);

   return;

}

2.1.编译

编译test.c时,需要增加-pg编译与链接选项,若要得到带注释的源码清单,则同时增加-g选项。执行编译,生成可执行程序test

[root@HLZ gprof]# gcc -pg-g -o test test.c

[root@HLZ gprof]# ls

test test.c

2.2.运行

运行test,程序退出之后,会在该进程执行目录下产生gmon.out文件:

[root@HLZ gprof]# ./test

[gprof]: x=10, y=20000

[root@HLZ gprof]# ls

gmon.out test test.c

注:当前目录下若已有gmon.out文件,则其内容将被本次运行生成的统计信息覆盖。多次运行同一程序时,需将前一次的gmon.out改名。

2.3.分析

使用gprof来分析gmon.out文件时,需要把它和产生它的应用程序关联起来。通过gprof -b <进程文件名>gmon.out获取调用关系与执行时间信息:

[root@HLZ gprof]# gprof -b test gmon.out

Flat profile:

 

Each sample counts as 0.01seconds.

 %  cumulative  self             self    total          

 time  seconds  seconds   calls ms/call ms/call name   

100.00     0.01    0.01       2    5.00    5.00 func_loop

 0.00     0.01    0.00  400000    0.00    0.00 func_print

  0.00     0.01    0.00       1    0.00    0.00 my_print

 

 

                       Call graph

 

 

granularity: each sample hitcovers 4 byte(s) for 100.00% of 0.01 seconds

 

index % time   self children   called    name

               0.01   0.00      2/2          main [2]

[1]   100.0   0.01   0.00      2        func_loop [1]

               0.00   0.00 400000/400000     func_print [3]

-----------------------------------------------

                                                <spontaneous>

[2]   100.0   0.00   0.01                main [2]

               0.01   0.00      2/2          func_loop [1]

               0.00   0.00      1/1          my_print [4]

-----------------------------------------------

               0.00   0.00 400000/400000     func_loop [1]

[3]     0.0   0.00   0.00 400000        func_print [3]

-----------------------------------------------

               0.00   0.00      1/1          main [2]

[4]     0.0   0.00   0.00      1        my_print [4]

-----------------------------------------------

 

 

Index by function name

 

  [1] func_loop              [3] func_print             [4] my_print

显然地,上半部分是函数调用次数与调用时间统计信息,下半部分是函数调用关系信息。

 

特别说明几个gprof常用的分析选项[读者可根据需要使用,本文不再演示]

1) -m num--min-count=num:不显示被调用次数小于num的函数;

2) -z--display-unused-functions:显示没有被调用的函数(按照调用计数和累积时间计算)。

3-b:不再输出统计图表中每个字段的详细描述。

4-p:只输出函数的调用图(Call graph的那部分信息)。

5-q:只输出函数的时间消耗列表。

6-e <Name>:不再输出函数Name及其子函数的调用图(除非它们有未被限制的其它父函数)。可以给定多个 -e 标志。一个 -e标志只能指定一个函数。

7-E <Name>:不再输出函数Name及其子函数的调用图,此标志类似于-e标志,但它在总时间和百分比时间的计算中排除了由函数Name及其子函数所用的时间。

8-f <Name>:输出函数Name及其子函数的调用图。可以指定多个-f标志。一个-f标志只能指定一个函数。

9-F <Name>:输出函数Name及其子函数的调用图,它类似于-f标志,但它在总时间和百分比时间计算中仅使用所打印的例程的时间。可以指定多个-F标志。一个-F标志只能指定一个函数。-F标志覆盖-E标志。

10-A:输出一个带注释的源代码清单,它会注释源码,指出每个函数的执行次数(需要在编译时加-g选项)。

* gprof更多选项,读者可以在linuxshell下执行gprof--help查看。

3、分析结果图形化

可以通过结合gprof2dot.pydot工具,实现gprof分析结果的图形化输出。

3.1.gprof2dot.py

一个Python语言工具脚本,用于gmon.out生成dot文件。该脚本可以直接下载使用。如果想要显示全部的函数调用,可以 gprof2dot -n0 -e0,默认是n0.5,即影响小于5%的函数就不显示了。当然,这样图片会很乱,因为显示内容太多,可以 gprof2dot -n0 -e0 -s  #-s选项表示不显示诸如模板,函数入口参数等等。

3.2.dot

dot工具通过安装graphviz获得,具体方法:

tar -zxvf graphviz-2.38.0.tar.gz -C .

cd graphviz-2.38.0

./configure

make

make install

3.3.图形化gprof分析结果方法

图形化gprof分析结果的语法:

gprof  <进程文件>  <gmon.out文件> | ./gprof2dot.py -n0 -e0 | dot -Tjpg -o <输出的图形文件名.jpg>

图形格式也可以是png等格式。

[root@HLZ gprof]# gprof ./test ./gmon.out | ./gprof2dot.py -n0 -e0 | dot -Tjpg -o test.jpg

4、注意点

1 程序必须正常退出(调用exit或从main中返回)才能生成统计信息。

如果你的程序是一个不会退出的服务程序,那就只有修改代码来达到目的。如果不想改变程序的运行方式,可以添加一个信号处理函数解决问题。可以采用注册信号,如在SIGUSR1信号处理函数中调用exit方法,来解决程序正常不会退出问题。

当使用kill -USR1 <pid>后,程序退出之后,即可生成gmon.out文件。

2 调试多线程程序只能统计主线程的信息,即gmon.out只会生成进程主线程的函数调用信息;

3 对于多进程,可以通过设置export GMON_OUT_PREFIX=gmon.out,使得进程退出后能够生成gmon.out.pid,避免文件同名而覆盖问题。

4 使用ld链接时,需要用/lib/gcrt0.o代替crt0.o作为第一个input文件。

5 gprof只查看用户函数信息,不能对库函数进行查看。如果要调试libc库,链接时需要使用-lc_p代替-lc

 

 



猜你喜欢

转载自blog.csdn.net/hanlizhong85/article/details/77923643