如何在 Linux 上安装和使用性能分析工具 Gprof
在此页
- 什么是 Gprof?
- 下载并安装 Gprof
- Gprof 用法
毫无疑问,测试是软件开发过程中不可或缺的,也是最重要的方面之一。通过测试,我们不仅仅意味着测试代码中的错误——当然,错误检测很重要,因为没有人希望他们的软件有错误——代码的性能如今也同样重要。
如果细分到最后一点,性能测试可以有效地测试一段特定代码(比如一个函数)消耗了多少时间。通常情况下,一个功能或一组功能可能对应于软件的许多功能之一。所以,如果通过性能测试,我们能够在代码中提升这些功能的性能,那么软件的整体性能就会变得更好。
如果您是一位使用 C、Pascal 或 Fortran77 编程语言编写代码并使用 Linux 作为开发平台的程序员,您会很高兴知道有一个强大的工具可以检查您的代码的性能 - 工具有问题的是 Gprof。在本教程中,我们将详细讨论如何下载、安装和使用此工具。
在我们继续之前,请注意本教程中提到的所有示例和说明均已在 Ubuntu 14.04LTS 上进行测试,并且使用的 Gprof 版本为 2.24。
什么是 Gprof?
那么,Gprof 到底是什么?根据该工具的官方文档,它为用户提供了他们的 C、Pascal 或 Fortran77 程序的执行配置文件。 Gprof 基本上做的是计算在每个例程或函数中花费的时间。 \接下来,这些时间沿着调用图的边缘传播。发现循环,并调用循环以共享循环的时间。\
如果此时所有这些听起来有点混乱(尤其是引号中的部分),请不要担心,通过一个例子来说明一切。所以,请继续阅读。
下载并安装 Gprof
首先检查该工具是否已安装在您的系统上。为此,只需在终端中运行以下命令。
$ gprof
如果您收到类似以下的错误:
$ a.out: No such file or directory
那么这将意味着该工具已经安装。否则您可以使用以下命令安装它:
$ apt-get install binutils
Gprof 用法
不用说,了解像 Gprof 这样的工具的最佳方式是通过实际示例。所以,我们从一个 C 语言程序开始,我们将通过 Gprof 进行分析。继承人的程序:
//test_gprof.c
#include<stdio.h>
void func4(void)
{
printf("\n Inside func4() \n");
for(int count=0;count<=0XFFFF;count++);
}
void func3(void)
{
printf("\n Inside func3() \n");
for(int count=0;count<=0XFFFFFFF;count++);
}
void func2(void)
{
printf("\n Inside func2() \n");
for(int count=0;count<=0XFFF;count++);
func3();
}
void func1(void)
{
printf("\n Inside func1() \n");
for(int count=0;count<=0XFFFFFF;count++);
func2();
}
int main(void)
{
printf("\n main() starts...\n");
for(int count=0;count<=0XFFFFF;count++);
func1();
func4();
printf("\n main() ends...\n");
return 0;
}
请注意,上面显示的代码 (test_gprof.c) 是专门为解释 Gprof 而编写的 - 它并非来自任何现实生活中的项目。
现在,继续,下一步是使用 gcc 编译这段代码。请注意,理想情况下,我会使用以下命令编译上述代码:
$ gcc -Wall -std=c99 test_gprof.c -o test_gprof
但是由于我们要使用 Gprof 分析代码,我将不得不使用 gcc 编译器提供的 -pg 命令行选项。所以,命令变成:
$ gcc -Wall -std=c99 -pg test_gprof.c -o test_gprof
如果你看一下 gccs 手册页,这里是关于 -pg 选项的内容:
\“生成额外的代码来编写适合分析程序 gprof 的配置文件信息。您必须在编译您想要数据的源文件时使用此选项,并且在链接时也必须使用它。\”
现在,回到上面的命令,在成功执行后,它会在输出中生成一个名为 test_gprof 的二进制文件。下一步是启动该可执行文件。在我的案例中,这是我启动二进制文件的方式:
$ ./test_gprof
执行该命令后,您会看到在当前工作目录中将生成一个名为 gmon.out 的文件。
$ ls gmon*
gmon.out
正是这个文件包含了 Gprof 工具生成人类可读的分析数据所需的所有信息。因此,现在按以下方式使用 Gprof 工具:
$ gprof test_gprof gmon.out > profile-data.txt
基本上,此命令的通用语法是:
$ gprof [executable-name] gmon.out > [name-of-file-that-will-contain-profiling-data]
现在,在我们看到 profile-data.txt 文件包含的信息之前,值得一提的是,Gprof 生成的人类可读输出分为两部分:平面配置文件和调用图。以下是 Gprof 的手册页对这两个部分下的信息的说明:
\“平面配置文件显示了您的程序在每个函数中花费了多少时间,以及该函数被调用了多少次。如果您只是想知道哪些函数消耗了最多的周期,这里简明扼要地说明了这一点。\”
\对于每个函数,调用图显示了哪些函数调用了它,它调用了哪些其他函数,以及调用了多少次。还有对每个函数的子例程花费了多少时间的估计。这可以建议您可以尝试消除占用大量时间的函数调用的地方。\
有了这些信息,现在您就可以更好地理解分析输出文件(在我的例子中是 profile-data.txt)中存在的数据。这是我的平面配置文件:
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
96.43 0.81 0.81 1 810.00 810.00 func3
3.57 0.84 0.03 1 30.00 840.00 func1
0.00 0.84 0.00 1 0.00 810.00 func2
0.00 0.84 0.00 1 0.00 0.00 func4
这是每个字段的含义:

继续,这里是我的调用图:
Call graph (explanation follows)
granularity: each sample hit covers 4 byte(s) for 1.19% of 0.84 seconds
index % time self children called name
0.03 0.81 1/1 main [2]
[1] 100.0 0.03 0.81 1 func1 [1]
0.00 0.81 1/1 func2 [3]
-----------------------------------------------
<spontaneous>
[2] 100.0 0.00 0.84 main [2]
0.03 0.81 1/1 func1 [1]
0.00 0.00 1/1 func4 [5]
-----------------------------------------------
0.00 0.81 1/1 func1 [1]
[3] 96.4 0.00 0.81 1 func2 [3]
0.81 0.00 1/1 func3 [4]
-----------------------------------------------
0.81 0.00 1/1 func2 [3]
[4] 96.4 0.81 0.00 1 func3 [4]
-----------------------------------------------
0.00 0.00 1/1 main [2]
[5] 0.0 0.00 0.00 1 func4 [5]
以下截图解释了调用图包含的信息:



如果您想知道以上屏幕截图的来源,让我告诉您所有这些信息都在包含分析信息的输出文件中,包括平面分析和调用图。如果您希望从输出中省略此信息,您可以使用 Gprof 提供的 -b 选项。
结论
不用说,我们只是触及了这里的表面,因为 Gprof 提供了很多功能(只要看看它的手册页)。但是,无论我们在此处介绍什么,都应该足以让您入门。如果您已经在使用 Gprof,并且想在这里与大家分享与该工具相关的内容,请在下方发表评论。