显示调用图概要分析数据
/usr/ucb/gprof [ -b ] [ -e Name ] [ -E Name ] [ -f Name ] [ -F Name ] [ -L PathName ] [ -s ] [ -z] [ a.out [ gmon.out ... ] ]
gprof 命令可生成 C、Pascal、FORTRAN 或 COBOL 程序的执行概要文件。被调用的例程的结果合并到各个调用程序的概要文件中。gprof 命令有助于标识程序如何消耗 CPU 资源量。要了解程序的哪些功能(例程)正在使用 CPU,您可以用 gprof 命令对该程序进行概要分析。
概要数据可从调用图概要文件(缺省状态下为 gmon.out)中获取,这些程序是由使用 -pg 选项的 cc 命令编译的程序创建的。-pg 选项链接在为概要分析而编译的各种版本的库例程中,且它读取指定的对象文件(缺省情况下为 a.out)中的符号表,并将该符号表与调用图概要文件相关联。如果指定了多个概要文件,gprof 命令的输出显示给定概要文件中的概要信息的总结。
-pg 选项可使编译器在为程序中每个重新编译的函数生成的对象代码中插入对 mcount 子例程的调用。在程序执行期间,每当父函数调用子函数时,子函数都会调用 mcount 子例程来使用于该父-子函数对的独特计数器加 1。在不是使用 -pg 选项重新编译的程序中不会插入 mcount 子例程,因此也就不会保持是谁调用它们的记录。
注意:来自 C++ 对象文件的符号在使用之前将其名称解码(demangle)。
gprof 命令将得到三项结果:
如果程序在多个并发的进程中运行了 fork 或 exec 子例程时,使用 gprof 命令进行概要分析会存在一些问题。概要是每个进程的环境属性,因此,如果对其进行概要分析的进程又派生出新进程,则此子进程也会被列入分析的范畴。然而,这样一来两个进程都向运行父进程的目录中的 gmon.out 文件写入,从而导致它们其中的写入内容被覆盖。因此,对于多进程概要分析,推荐您使用 tprof 命令。
如果必须使用 gprof 命令,绕过上述问题的一个方法是调用 chdir 子例程更改子进程的当前目录。这样一来,当子进程退出时,其 gmon.out 文件就会被写入到新的目录。下列示例演示了这种方法:
cd /u/test # current directory containing forker.c program pg forker.c main() { int i, pid; static char path[]="/u/test2"; pid=fork(); /* fork a child process */ if(pid==0) { /* Ok, this is the child process */ chdir (path); /* create new home directory so gmon.out isn't clobbered! */ for (i=0; i<30000; i++) sub2(); /* 30000 calls to sub2 in child profile */ } else /* Parent process... leave gmon.out in current directory */ for (i=0;i<1000; i++) sub1(pid); /* 1000 calls to sub1 in parent profile */ } int sub1(pid) /* silly little function #1, called by parent 1000 times */ int pid; { int i; printf("I'm the parent, child pid is %i.\n",pid); } int sub2() /* silly little function #2, called by child 30,000 times */ { printf("I'm the child.\n"); } cc -pg forker.c -o forker # compile the program mkdir /u/test2 # create a directory for childi to write gmon.out in forker >/dev/null # Throw away forker's many, useless output lines gprof forker >parent.out # Parent process's gmon.out is in current directory gprof forker ../test2/gmon.out >child.out # Child's gmon.out is in test2 directory
此时,如果将 test 目录中的 parent.out 和 child.out 这两个 gprof 命令的输出列表进行比较,您可以看到 sub1 子例程在父进程中调用了 1,000 次,在子进程中调用了 0 次,而 sub2 子例程在子进程中调用了 30,000 次,在父进程中调用了 0 次。
运行 exec 子例程的进程不继承概要分析。然而,如果由 exec 子例程执行的程序是使用 -pg 选项编译的,则此程序应进行概要分析。就拿前面的 forker.c 示例来说,如果同时对父进程和由 exec 子例程程序运行的程序进行概要分析,则其中一个会覆盖另外一个的 gmon.out 文件,除非对其中之一中使用 chdir 子例程。
如果没有程序的源代码,您可以在不重新编译的情况下使用 gprof 命令进行概要分析。然而,您必须能通过相应的编译程序命令(例如,用于 C 语言的 cc 命令)重新链接程序模块。如果不作重新编译,您将无法获得调用频率计数,尽管平面概要文件在没有它们的情况下仍然有用。作为额外的补偿,您的程序会以与平常大致相同的速度运行。下面说明了如何进行概要分析:
cc -c dhry.c # Create dhry.o without call counting code. cc -pg dhry.o -L/lib -L/usr/lib -o dhryfast # Re-link (and avoid -pg libraries). dhryfast # Create gmon.out without call counts. gprof >dhryfast.out # You get an error message about no call counts # -- ignore it.
在不调用计数的情况下运行时,一些快速执行的功能根本不会显示在列表中(但您知道必须要调用它们)。尽管不可见,但这个结果对 gprof 命令而言是正常的。gprof 命令仅列出了那些至少调用一次或每个时钟周期至少注册一次的功能。虽然快速执行函数也运行,但快速执行功能通常不会接收任何时钟周期。由于暂挂了调用计数功能,因此这些小函数不会被列出。(如果在 cc -pg 命令行中省略了 -L 选项,您可以获得运行时例程的调用计数。)
由于 -pg 选项专用相当于程序文本大小一半的固定实时内存缓冲区,因此使用 gprof 命令进行概要分析时可能导致程序过度分页。过度分页不会影响概要分析所生成的数据,原因是被分析的程序在等待 I/O 时不生成时钟周期(它仅在使用 CPU 时才这样做)。如果因为过度分页而导致的时间延迟是不可接受的,建议您使用 tprof 命令。
gprof
gprof -L/home/score/lib runfile runfile.gmon
本示例使用给定的 runfile.gmon 文件作为样本数据和 runfile 文件作为本地符号,检查 /u/score/lib 文件中的可加载对象。
在 gprof 命令的通篇描述中,大部分示例都使用了 C 程序 dhry.c。但是,只要将 C 编译器和 cc 替换成相应的编译器名称,并将 function 一词替换成 subroutine,则关于这些示例的讨论也同样适用于 FORTRAN、Pascal 或 COBOL 模块。例如,下列命令即显示了如何对名为 matrix.f 的 FORTRAN 程序进行概要分析:
xlf -pg matrix.f -o matrix # 对 matrix.f 程序进行 FORTRAN 样式的编译 matrix # 执行 gprof 概要分析, # 并生成 gmon.out 文件 gprof > matrix.out # 在 matrix.out 中生成来自 gmon.out # 的概要分析报告 vi matrix.out # 首先读取平面概要文件。
a.out | 名称列表和文本空间 |
gmon.out | 动态调用图和概要文件 |
gmon.sum | 动态调用图和概要文件总结 |
/usr/ucb/gprof | 包含 gprof 命令。 |
cc 命令、prof 命令。
exit 子例程、monitor 子例程、profil 子例程。
《AIX 5L V5.2 性能管理指南》中的『性能监视与调整命令和子例程』。
《AIX 5L V5.2 系统用户指南:操作系统与设备》中的『命令概述』。
AIX 5L Version 5.2 General Programming Concepts: Writing and Debugging Programs中的『子例程概述』。