参考:https://blog.youkuaiyun.com/aneutron/article/details/49251921
Gprof
简介
Gprof 是GNU gnu binutils工具之一,默认情况下linux系统当中都带有这个工具。Gprof给出了函数调用的次数、调用耗时以及函数的调用关系,通过分析产生的数据结果可以确定程序的执行流程,进而有针对性的对程序进行优化。
功能
可以获得的几种格式的数据:1
- flat profile :给出了每个函数的耗时以及函数被调用的次数。
- call graph:给出了函数调用关系,以及对函数耗时的一个估计。
- 注释的源代码--是程序源代码的一个复本,标记有程序中每行代码的执行次数。
原理
实现原理2
gcc -pg 在应用程序的每个函数中添加了名为 mcount/mcount/_mcount的函数。 应用程序每个函数执行时都会执mcount,而mcount则会在内存中保存一张函数调用图,通过函数调用堆栈的形式,查找子函数、父函数的地址,也保存了与函数相关的调用时间、次数等信息。
程序运行结束后,会在程序退出的路径下生成一个 gmon.out文件。这个文件就是记录并保存下来的监控数据。可以通过命令行方式的gprof或图形化的Kprof来解读这些数据并对程序的性能进行分析。
另外,如果想查看库函数的profiling,需要在编译是再加入“-lc_p”编译参数代替“-lc”编译参数,这样程序会链接libc_p.a 库,才可以产生库函数的profiling信息。如果想执行一行一行的profiling,还需要加入“-g”编译参数。
结果产生与分析
图1 Gprof结果信息 3
用法
- 在编译和链接时 加上-pg选项。一般我们可以加在 makefile 中。
- 执行编译的二进制程序。执行参数和方式同以前。
- 在程序运行目录下 生成 gmon.out 文件。如果原来有gmon.out 文件,将会被重写。
- 结束进程。这时 gmon.out 会再次被刷新。
- 用 gprof 工具分析 gmon.out 文件。
优缺点4
优点
- Gprof为GNU binutils工具之一,默认情况下linux系统当中都带有这个工具,使用方便。
- 生成结果包括函数调用时间以及函数调用关系,可以方便用户利用该数据做进一步分析 。
缺点
- 使用插桩技术,消耗系统资源。函数的每次执行都会调用插桩函数mcount,并且mcount函数会在内存中维护一个函数调用图,使得对CPU和内存资源都带来消耗。
- 程序必须是正常退出才能生成gmon.out文件,也就是说程序必须执行到main函数的return或者exit()。
- 如果程序运行的时间非常短,则Gprof可能无效,因为受到启动、初始化、退出等函数运行时间的影响。
- 不支持多进程,如果分析多进程程序则可能一个进程的gmont.out文件会覆盖另一个进程的gmont.out文件。 解决方法是在执行程序之前执行:export GMON_OUT_PREFIX=x.out 则之后生成的文件名就如x.out.pid,多进程的gmon.out就不会相互覆盖。
- 不支持多线程。缘故是gprof使用ITIMER_PROF定时器, 当超时时由内核向应用程序发送信号。但多线程程序只有主线程接收ITIMER_PROF。 这里有一个简单的实现方法: 对pthread_create进行包装,并以动态库的形式在程序运行前加载。
- 只能分析应用程序在运行过程中所消耗掉的用户时间,无法得到程序内核空间的运行时间。
例子
http://blog.sina.com.cn/s/blog_4a471ff601013vud.html
https://blog.youkuaiyun.com/u010784938/article/details/27698961
下面给出一个小例子,说明如何利用Gnu/Gprof工具追踪程序运行过程中的函数调用,源程序文件名为test.c,其内容如下。
【步骤1】使用gcc编译器的-pg选项对源程序进行编译和链接,输入如下命令:
其中test.c文件存放在路径为/home/test,运行命令后会在该路径下生成一个默认名为“a.out”的可执行文件,当然也可以利用-o选项指定可执行文件的名字。本例的调试编译如上所示很简单,但对包含成千上万个源文件的大型开源项目进行编译时会相对复杂一些。首先需要进入项目主文件输入“./configure”命令进行编译配置检查,然后输入“make CFLAGS=-pg LDFLAGS=-pg”进行编译,最后还要输入“make install”安装项目。其中CFLAGS和LDFLAGLS分别是编译和链接标志,它们都需要加入-pg选项,否则有可能无法追踪成功。
【步骤2】执行程序,使之生成一个名为“gmon.out”的二进制数据文件,输入如下命令:
除上述运行结果外,还会在当前目录中生成一个名为“gmon.out”的文件。
【步骤3】使用gprof工具对步骤2中生成的数据文件进行分析,输入如下命令:
执行后会在控制台输出分析结果,下面是从中摘抄的一些详细信息。
从输出能明显看出,main函数调用了b函数,而b函数分别调用了a和c函数。由于例子中的函数只是简单地输出一个字符串,所以每个函数的消耗时间都是0。
帮助理解输出:
若:gprof生成的输出如下:
index % time self children called name
0.00 0.00 4/4 foo [4]
[3] 0.0 0.00 0.00 4 bar [3]
-----------------------------------------------
0.00 0.00 1/2 init [5]
0.00 0.00 1/2 main [45]
[4] 0.0 0.00 0.00 2 foo [4]
0.00 0.00 4/4 bar [3]
-----------------------------------------------
0.00 0.00 1/1 main [45]
[5] 0.0 0.00 0.00 1 init [5]
0.00 0.00 1/2 foo [4]
-----------------------------------------------
---------------------
作者:陈硕
来源:优快云
原文:https://blog.youkuaiyun.com/Solstice/article/details/488865
版权声明:本文为博主原创文章,转载请附上博文链接!
从中可以看出,bar()被foo()调用了4次,foo()被init()和main()各调用了一次,init()被main()调用了一次。
《GNU Gprof》:https://sourceware.org/binutils/docs-2.17/gprof/index.html ↩
《gprof使用和介绍》: http://blog.sina.com.cn/s/blog_4a471ff601013vud.html ↩
《Linux性能评测工具之一:gprof篇》: http://blog.youkuaiyun.com/stanjiang2010/article/details/5655143 ↩
《用gprof分析程序性能》: http://www.berlinix.com/dev/gprof.php ↩
另一种
GCC的-finstrument-functions 参数的作用是在程序中加入hook,让它在每次进入和退出函数的时候分别调用下面这两个函数:
void __cyg_profile_func_enter( void *func_address, void *call_site )
attribute ((no_instrument_function));
void __cyg_profile_func_exit ( void *func_address, void *call_site )
attribute ((no_instrument_function));
当然,这两个函数本身不能被钩住(使用no_instrument_function这个__attribute__),不然就反反复复万世不竭了:) 这里获得的是函数地址,需要用binutils中的addr2line这个小工具转换为函数名,如果是C++函数,还要用c++filt进行name demangle。具体方法在https://www.cnblogs.com/wangkangluo1/archive/2012/07/09/2583228.html
原文:https://blog.youkuaiyun.com/Solstice/article/details/488865