如何在Linux上使用Gprof分析工具
随着代码变得越来越大,函数和嵌套函数调用的数量也随之增加。
因此,要分析此类程序的性能,有必要了解哪些功能需要花费时间才能执行。
同样,单个函数的分析还应该清楚地区分父函数所花费的时间与从父函数中调用的其他函数之间的时间。
用于这种性能分析的软件工具通常称为探查器。
在本教程中,我们将讨论名为gprof的基于Linux的探查器。
本文介绍的所有示例均已在Ubuntu,bash 4.2.45和gprof 2.23.2上进行了测试。
Gprof设置和用法
以下是下载和设置gprof环境所需的一些步骤:
- 如果尚未安装,请通过执行“ apt-get install binutils”下载并安装gprof
- 要检查gprof是否已正确安装,请执行gprof命令,它应显示诸如“ a.out:No such file or directory”之类的错误。
- 假设编译器使用的是gcc或者cc,请使用选项'-pg'编译代码,以便可执行文件包括用于分析目的的另外代码。
- 以正常方式运行程序。当程序终止时,将在运行该程序的同一目录中生成一个名为“ gmon.out”的文件(探查器数据)。
- 使用gprof分析器处理该分析器数据(gmon.out),并生成易于理解的性能分析和程序统计信息。
这是我们要分析的示例代码:
#include<stdio.h> void some_other_test(void) { printf("\n Inside the function some_other_test() \n"); int i = 0; for(;i<=0XFFFF;i++); } void yet_another_test(void) { printf("\n Inside the function yet_other_test() \n"); int i = 0; for(;i<=0XFFFFFFF;i++); } void another_test(void) { printf("\n Inside the function another_test() \n"); int i = 0; for(;i<=0XFFF;i++); yet_another_test(); } void test(void) { printf("\n Inside the function test() \n"); int i = 0; for(;i<=0XFFFFFF;i++); another_test(); } int main(void) { printf("\n Inside the function main() \n"); int i = 0; for(;i<=0XFFFFF;i++); test(); some_other_test(); return 0; }
请注意,上面的代码只是一个虚拟示例,其中包含for循环以模拟函数处理中的时间消耗。
一旦代码到位,第一步就是编译代码。
这是我们可以执行的操作:
$gcc -Wall -pg profile.c -o profile
因此,我们可以看到代码已保存在名为“ profile.c”的文件中,输出文件名为“ profile”。
一旦生成了名为“ profile”的可执行文件,它就可以像其他程序一样被执行:
$./profile Inside the function main() Inside the function test() Inside the function another_test() Inside the function yet_other_test() Inside the function some_other_test()
这样我们可以看到程序成功执行。
同样,它在同一目录中产生了一个名为“ gmon.out”的文件。
$ls gmon.out gmon.out
现在,通过以下方式使用gprof实用程序:
$gprof profile gmon.out > output
因此,我们可以看到可执行文件名称和gmon.out作为gprof命令的参数提供,并且输出已重定向到名为“输出”的文件。
该文件以人类可读的形式包含对程序“配置文件”性能的所有统计分析。
输出的内容分为信息类型-平面配置文件和调用图。
平面轮廓
这是平面轮廓形式的统计分析:
上面显示的统计信息称为平面轮廓。
这是每列含义的解释(从文件输出中获取):
- 时间百分比-此功能使用的程序在总运行时间中所占的百分比。
- 累积秒数-此功能及其上方列出的秒数的总和。
- 自我秒数-仅此功能所占的秒数。这是此列表的主要种类。
- 调用-调用此函数的次数,如果已对此函数进行了概要分析,则为空。
- 自身ms /调用-如果已分析此函数,则每个调用在此函数中花费的平均毫秒数,否则为空。
- 总计ms/call-此函数及其每个调用在其后代中所花费的平均毫秒数(如果已对此函数进行了分析,则为空白)。
- 名称-函数的名称。这是此列表的次要排序。索引显示函数在gprof列表中的位置。如果索引在括号中,则会显示该索引在要打印的gprof列表中的显示位置。
通话图
这是调用图形式的统计分析:
该表描述了程序的调用树,并按每个函数及其子函数所花费的总时间进行了排序。
该表中的每个条目都包含几行。
索引号位于左边距的行列出了当前功能。
它上面的行列出了调用此函数的函数,下面的行列出了此函数所调用的函数。
这是每列含义的解释(从文件输出中获取):
- 索引-赋予表中每个元素的唯一编号。索引号按数字排序。索引号显示在每个函数名称的旁边,因此可以更轻松地查找表中函数的位置。
- 时间百分比-这是此功能及其子级花费的“总”时间的百分比。请注意,由于不同的观点,选项所排除的功能等,这些数字的总和不会达到100%。
- 自我-这是此功能花费的总时间。对于函数的父级,这是直接从函数传播到该父级的时间。而对于函数的子代而言,这是直接从子代传播到函数中的时间量。
- 子级-这是其子级传播到此功能的总时间。对于函数的父级,这是从函数的子级传播到该父级的时间。而对于函数的子代来说,这是从子代的子代传播到函数的时间。
- 被调用-这是函数被调用的次数。如果该函数以递归方式调用自身,则该数字仅包括非递归调用,后跟一个“ +”和递归调用的数目。对于函数的父级,这是此父级调用函数“ /”的次数,即该函数被调用的总次数。对函数的递归调用不包含在“ /”后的数字中。而对于函数的子代而言,这是函数调用此子代的次数“ /”,即该子代被调用的总次数。子代的递归调用未在“ /”后的数字中列出。
- 名称-当前函数的名称。索引号将在其后打印。如果函数是循环的成员,则在函数名称和索引号之间打印循环号。对于函数的父代,这是父代的名称。父母的索引号被打印在其后。如果父级是循环的成员,则循环号将显示在名称和索引号之间。对于函数的子代,这是子代的名称。在孩子的索引号之后打印。如果孩子是一个周期的成员,则在名称和索引号之间打印周期号。
gprof生成的输出文件中也包含上述所有说明。
为了方便读者,我在这里提到了它。
注意:每次运行gprof时,所有这些说明都会在输出文件中产生。
因此,一旦我们了解了细节,就可以使用gprof命令行选项-b。
要阅读更多命令行选项,请阅读gprof手册页。