函数调用关系分析软件

今天忽然有个网友加我QQ,说他正在用gprof分析一个项目的源代码,想打印出该项目的函数调用关系图,不料它参考的资料[1]中用于打印函数调用关系 图的Mkgraph脚本已经没法下载了,因此想问我要一份。可我当初看到这篇资料时也由于也没下到,因此给它推荐了另一个一样可以产生函数调用关系图的 工具——calltree。不过刚才又忽然想起,其实还有另一个工具能够代替Mkgraph的,那就是kprof。为了方便你们往后分析源代码,这里一 并再把几个相关工具介绍一下。html

先列出我当前用的这几个工具的版本和它们的下载地址:

calltree  2.3 
下载地点 http://linux.softpedia.com/progDownload/calltree-Download-971.html
或者
http://mirror.lzu.edu.cn/software/calltree/calltree-2.3.tar.bz2

gprof 2.18.0.20080103 在ubuntu/debian下直接安装便可 http://citeseer.ist.psu.edu/graham82gprof.htmllinux

kprof 1.4.3 (在ubuntu/debian下直接用apt-get安装) http://kprof.sourceforge.net/程序员

graphviz (在ubuntu/debian下直接用apt-get安装便可,须要它的一个dot工具) http://www.graphviz.org/ubuntu

  1. introductionvim

    对于一个C语言编写的项目,它的框架能够反应为一棵函数调用树。若是在分析项目以前,可以获得这样一颗调用树,那么就能够了解项目的总体框架;若是在项目 运行以后,可以跟踪到该次运行过程当中的函数调用,那么将有利于分析某些测试条件下项目的执行流程;而若是在项目运行过程当中(好比调试项目时)可以跟踪出某 个位置以前的函数调用,那么将有利于肯定潜在bug可能存在的位置。框架

    对于这三种状况,虽然没有任何一个工具可以彻底知足,不过"聪明"和"乐于奉献"的程序员们仍是分别贡献了不一样的工具:编辑器

无须运行项目自己,calltree就可以根据整个项目的源代码产生一棵函数调用树,并可把该调用树导出为dot格式的图形。所以能够说calltree可以在不运行项目的条件下对项目进行函数级别的分析。svg

gprof则可以在项目运行以后,把该次运行过程当中的函数调用以文本的形式反应出来,不过善于思考的人们老是喜欢更美好的生活,因而kprof产生了,它 不只能够辅助gprof更好的分析程序代码级别的运行状况,并且可以导出当前执行过程当中的函数调用树,并一样能够把调用树导出为dot格式的图形。函数

gdb(Gnu DeBugger),这个应该很熟悉吧,它是一个调试工具。它提供专门的backtrace命令来跟踪程序执行到某个位置(好比指定的断点处)以前的函数调用。不过这个目前仍是文本输出的,感兴趣的能够hack一下gdb,给它加上漂亮的输出。工具

上面提到了DOT格式的图形。这个DOT[2]是什么呢?是graphviz[3]定义的一种图形描述语言,它能够经过graphviz提供的dot工具 (安装graphviz以后就有了)把用DOT描述的图形转化为各类其余格式的图形。虽然有一些专门的DOT图形浏览工具,如dotty,不过这个东西不 怎么好用,因此仍是建议经过dot工具转换为比较常见的图形格式,如svg,jpg,gif,png,ps,它还能够转换成dia格式,进而能够经过“超 级牛力”的dia绘图工具来进行进一步的编辑。
  1. demo

    下面来介绍这几个工具的具体用法,更多细节请参考它们本身的文档。 为了方便演示,这里写一个很是“糟糕的”可是却对此次演示颇有用的代码。

Code: /**

  • test.c -- * a demo program for using calltree, gprof&kprof, gdb&backtrace command
  • */

void a(void), b(void), c(void), d(void), e(void);

void a(void) { b (); } void b(void) { c (); } void c(void) { d (); e (); } void d(void) { ; } void e(void) { ; }

int main (int argc, char **argv) { if (argc < 2) { a (); } else { b (); } }

[Ctrl+A Select All]

对这个代码而言,很是容易看出其调用关系,即:

当main的参数个数少于2个时,调用关系为 a -> b -> c -> d,e

当main的参数个数大于3个时,则调用关系为b -> c -> d,e 不过,若是一个项目包含几十个文件或者几千行甚至上万行代码,这个调用关系恐怕就没这么容易看出来了,因此还得借助后面的工具。

2.1 不用运行程序就能够打印整个项目的函数调用关系图: calltree

下载calltree后本身先编译安装好,放到/usr/bin下面。而后经过"calltree -help"查看该工具的帮助,这里经过使用-mb参数打印以main为树根的函数调用关系图。

$ calltree -mb test.c main: | a | | b | | | c | | | | d | | | | e | b | | c | | | d | | | e

从这个结果能够很是方便的看出函数调用关系,不过仍是不够美观哦,因此加上-dot参数,产生一个dot图形吧。

$ calltree -mb test.c -dot > test.dot

okay,如今获得了一个关系调用图,即test.dot,由于这个格式不太经常使用,咱们给它转换成jpg,见附图calltree.jpg。

$ dot -Tjpg test.dot -o calltree.jpg

不过貌似函数d和e没有打印出来,因此这个应该说是值得改进一下。还好我以前专门写了一个脚本,能够产生完整的输出,这个脚本见附件 tree2dot.sh.tar.gz,具体原理见资料[5],附图calltree1.jpg是这个脚本产生的。这里简单介绍它的用法:

先经过脚本tree2dot.sh获得一个DOT图形 $ calltree -mb test.c | ./tree2dot.sh > test.dot 而后用dot转换为jpg格式 $ dot -Tjpg test.dot -o calltree1.jpg

须要补充一下的是,calltree自己支持过滤掉某些字符,包括想列出的(经过listfile或者list)以及想忽略的(igorefile),例如若是仅仅想列出c函数的调用关系,则能够:

$ calltree list=c -b -np test.c c: | d | e

若是仅仅想导出这个调用图,经过加上-dot参数则没有做用,它仍是会打印出全部的函数调用关系,因此这个时候tree2dot.sh又起做用:

$ calltree list=c -b -np test.c | ./tree2dot.sh

2.2 打印项目当次运行过程当中的函数调用关系图: gprof & kprof

首先经过gcc加上-pg参数编译程序(若是编译和连接分开,都须要加上该参数)。这个参数就是为了产生一些用于gprof&kprof的信息。 gprof只有字符界面,而kprof提供了图形界面,下面仅介绍kprof,由于它和gprof相比,能够产生图形化的函数调用关系。

$ gcc -pg -o test test.c

编译完之后,运行一下就能够产生一个名为gmon.out的文件,它记录了该项目当次运行过程当中的相关信息,包括函数调用关系。

$ ./test $ls gmon.out gmon.out

这样咱们就能够用kprof来获得这个项目在此次运行过程当中的函数调用关系图了( 实际上指定./test是为了告诉kprof,gmon.out和test在同一个目录下,kprof会去找gmon.out)。

$ kprof -f ./test

启动kprof之后找到Graph View标签,能够看到一个函数调用关系图。若是要把这个图导出来,找到Tools菜单,点击Generate Call Graph就能够导出一个DOT图形,咱们命名为kprof_noargument.dot,而后咱们就能够相似2.1用dot工具把它转换为其余格式, 获得的效果图如kprof_noargument.jpg。

$ dot -Tjpg kprof_noargument.dot -o kprof_noargument.jpg

在上面,咱们直接键入了./test执行它,若是给它传递上两个参数呢,这个时候argc等于二,在main中就不会再调用a函数,而是调用c函数,这样的话,函数调用关系图就不同了,此次获得的结果图如kprof_twoargument.jpg。

带上两个参数运行test $ ./test 1 2 经过kprof来查看调用关系图,并导出一个名为kprof_twoargument.dot的图形 $ kprof -f ./test 把DOT图形转换为jpg格式 $ dot -Tjpg kprof_twoargument.dot -o kprof_twoargument.jpg

这里没有提到gprof,由于它只产生一些不太好看的文本调用关系图,因此没有演示,不过它仍是有很大做用的,具体参考一下资料[4]吧。
结合2.1和2.2,咱们能够发现calltree和kprof二者都可以获得项目的函数调用关系图,不过前者可以获得整个项目的函数调用关系,然后者则 可以获得某次运行过程当中的函数调用关系,各有不一样做用。经过前者咱们能够了解整个项目的框架;而经过后者,咱们能够找出一个项目在某些测试条件下的执行路 径,从而更好地辅助源代码的分析。
有时候,这两种结果仍是没法知足咱们的要求,好比在调试过程当中,咱们设置了一个断点,并想了解一下这以前执行过哪些函数,进而找出潜在的bug可能出现的位置。

2.3 项目调试过程当中打印某个位置(如断点)以前的函数调用关系图:gdb & backtrace command

为了可以用gdb调试程序,编译时请使用-g选项。

$ gcc -g -o test test.c

经过gdb的backtrace命令打印程序执行到某个位置以前的函数调用信息。

$ gdb ./test ... (gdb) set args 1 2 //这里设置为两个参数,因此选择了路径b->c->d,e (gdb) l 6 7 void a(void) { b (); } 8 void b(void) { c (); } 9 void c(void) { d (); e (); } 10 void d(void) { ; } 11 void e(void) { ; } 12 13 int main (int argc, char **argv) 14 { 15 if (argc < 2) { (gdb) break c Breakpoint 1 at 0x804833c: file test.c, line 9. (gdb) r Starting program: /home/falcon/Programming/test 1 2

Breakpoint 1, c () at test.c:9 9 void c(void) { d (); e (); } (gdb) backtrace #0 c () at test.c:9 #1 0x08048334 in b () at test.c:8 #2 0x08048380 in main (argc=3, argv=0xbf924e24) at test.c:18

在上面的调试过程当中,咱们首先经过set命令设置了两个参数,选择了main函数的第二个分支,并在c函数的入口设置了一个断点,而后运行程序直到该断点 处,以后经过backtrace命令打印出以前的函数调用信息。经过最后几行,咱们看到c最后被调用,以前是b,再以前是main。

到这里,开头提到的三种状况都介绍完了。不过呢,除了上面这些跟函数关系紧密的工具外,还有一个叫cscope[6]的工具,结合它和vim编辑器,在我 们阅读源代码的过程当中,能够利用它提供的":cs find d function"命令打印出函数function调用的全部函数,从而帮助咱们了解某个函数内部的函数调用关系。固然该工具还有更丰富的用法,具体参考 资料[6]。
除了这些分析应用程序的工具外,还有一个叫KFT[7]的工具能够用来分析linux内核。做为linux内核的一个补丁,它可以跟踪内核中某个系统调用 的函数调用关系图,经过KFT提供的一个kd工具,能够获得一个文本格式的函数调用关系图,结合我上面用到的tree2dot.sh(建议用资料[5]中 的tree2dot.sh),能够获得一个图形输出。

更多相关资料见后面。有任何建议和疑问,欢迎回帖交流,也能够直接给我发邮件。

补充:

  1. 应网友要求,刚写了个mkgraph.sh脚本,这个脚本没有作足够的测试,这个能够结合gprof使用,具体用法以下:

带-pg编译程序 $ gcc -pg -o test test.c 先运行程序 $ ./test 让mkgraph.sh能够运行 $ chmod +x mkgraph.sh 经过gprof产生函数调用图并导出为dot图形 $ gprof ./test gmon.out -bq | ./mkgraph.sh > mkgraph.dot 把DOT图转换为jpg格式 $ dot -Tjpg mkgraph.dot -o mkgraph.jpg

图的结果见附图mkgraph.jpg,这个尚未进行足够的测试,若是发现问题,欢迎回复。

  1. 考虑到原来的tree2dot脚本有些问题,同一个函数调用绘制了多条路径,不妥,因此须要调整一下。请你们下载当前的tree2dot版本,该版本的结果为calltree2.jpg。

参考资料

[1] 使用Gnu gprof进行Linux平台下的程序分析 [2] The DOT Language http://www.graphviz.org/doc/info/lang.html [3] Graphviz - Graph Visualization Software http://www.graphviz.org/ [4] Coverage Measurement and Profiling http://www.linuxjournal.com/article/6758 [5] 用Graphviz进行可视化操做──绘制函数调用关系图 [6] cscope http://cscope.sourceforge.net/ [7] KFT(Kernel Function Tracing) http://elinux.org/Kernel_Function_Trace ftp://dslab.lzu.edu.cn/pub/kft [8] Call Graph -- Gprof http://sourceware.org/binutils/docs-2.17/gprof/Call-Graph.html#Call-Graph

相关文章
相关标签/搜索