gprof是一个GNU profiler工具,可以采集程序中每个函数的调用次数、每个函数消耗的CPU时间、以及显示调用关系图包括每个函数调用花费了多少时间。要查看gprof的官方详细信息请点击这里,也可以参看这里。
一、原理
在编译和链接程序的时候,使用-pg选项,这样gcc/g++就会在应用程序的每个函数中都加入一个名为mcount/_mcount/__mcount的函数,即用-pg编译的应用程序里的每个函数都会调用mcount函数,而mcount函数会在内存中保存一张函数调用图,并通过函数调用堆栈的形式查找子函数和父函数的地址,这张调用图也保存了所有与函数相关的调用时间、调用次数等所有信息。
二、用法
运行用-pg编译和链接后生成的应用程序,在程序退出时会在当前路径生成一个gmon.out文件,这个文件会记录下采集的监控数据,可以通过命令行方式的gprof或图形化方式的Kprof来解析监控数据以分析应用程序的性能。
gprof常用命令选项如下:
-b:不再输出统计图表中每个字段的详细描述
-p:只输出函数的调用图(Call graph的那部分信息)
-q:只输出函数的时间消耗列表
-e Name:不再输出函数Name及其子函数的调用图(除非它们有未被限制的其它父函数)。一个-e标志只能指定一个函数,但是可以给定多个-e标志。
-E Name:不再输出函数Name及其子函数的调用图,此标志类似于-e标志,但它在总时间和百分比时间的计算中排除了由函数Name及其子函数所用的时间。
-f Name:输出函数Name及其子函数的调用图。一个-f标志只能指定一个函数,但是可以指定多个-f标志。
-F Name:输出函数Name及其子函数的调用图,它类似于-f标志,但它在总时间和百分比时间计算中仅使用所打印的例程的时间。一个-F标志只能指定一个函数,但是可以指定多个-F标志,-F标志覆盖-E标志。
-z:显示使用次数为零的例程(按照调用计数和累积时间计算)。
需要注意如下两点:
(1)如果要查看库函数的profiling,需要在编译时再加入-lc_p编译参数,这样程序会链接libc_p库,从而产生库函数的profiling信息。
(2)gprof只能在应用程序正常结束退出后才能生产gmon.out文件,这是因为gprof通过在atexit()里注册了一个函数来产生结果信息,任何非正常退出都不会执行atexit(),也就不会产生gmon.out文件。通常为了达到这个效果会为应用程序注册一个信号处理函数以优雅解决此问题,比如:
1
2
3
4
5
6
7
8
9
|
static void sighandler( int sig_no) { exit (0); } int main( int argc, char **argv) { signal (SIGUSR1,
sighandler); ... } |
当使用kill -USER1 pid后,应用程序会退出并生成gmon.out文件。
三、实例
下面以简单实例来说明如何使用gprof,具体代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
#include
<stdio.h> #include
<stdlib.h> #include
<unistd.h> #include
<signal.h> static void sighandler( int sig_no
) { printf ( "hello
exit\n" ); exit (0); } void a() { printf ( "\t\t+---call
a() function\n" ); } void c() { printf ( "\t\t+---call
c() function\n" ); } int b() { printf ( "\t+---
call b() function\n" ); a(); c(); return 0; } int main() { signal (SIGUSR1,
sighandler); printf ( "
main() function()\n" ); b(); while ( true ) { sleep(5); } return 0; } |
(1)使用-pg选项编译和链接应用程序。
gcc -pg -o test test.c
(2)执行应用程序,使之运行完成后生成供gprof分析的数据文件(默认是gmon.out)。
由于本程序是一个永不退出的后台程序,所以就不仅仅是./test了,而需要如下来退出:
#kill -USR1 pid
这样在应用程序所在目录就会产生gmon.out文件。
(3)使用gprof分析应用程序生成的数据文件。
gprof test gmon.out
使用上面命令就能分析test的性能了,找出耗时最多的函数或者运算不是梦。
通常profiling结果都比较多,这样可以使用管道或者重定向将结果分页显示或者输出到文件中,再具体分析,比如:
gprof test gmon.out | less
gprof test gmon.out > profiling.txt
gprof分析结果会产生如下一些信息:
%time:函数使用时间占所有时间的百分比
cumulative seconds:函数和上列函数累计执行的时间
self seconds:函数本身所执行的时间
calls:函数被调用的次数
self ms/call:每一次调用花费在函数的时间(单位微秒)
total ms/call:每一次调用花费在函数及其衍生函数的平均时间(单位微秒)
name:函数名
简单地用-b参数分析下性能,其结果输出如下:
从上面的输出能看出main调用了b,而b又分别调用了a和c,由于函数很简单,所以每个函数的时间消耗都是0秒。
四、附注
一般来说,gprof用于查找用户态层次性能瓶颈,如果你需要查找内核态的性能瓶颈,请使用另一个款开源的profiling工具——oprofile,其使用硬件调试寄存器来统计信息,可以对内核进行profiling,采集cache缺失率、memory访存信息、分支预测错误率等,具体可以参考oprofile官网,也可以参考这里。