陈硕的Blog,很好的入门综述:
from:http://blog.csdn.net/solstice/article/details/488865
绘制函数调用关系图对理解大型程序大有帮助。我想大家都有过一边读源码(并在头脑中维护一个调用栈),一边在纸上画函数调用关系,然后整理成图的经历。如果运气好一点,借助调试器的单步跟踪功能和call stack窗口,能节约一些脑力。不过如果要分析的是脚本语言的代码,那多半只好老老实实用第一种方法了。如果在读代码之前,手边就有一份调用图,岂不妙哉?下面举出我知道的几种免费的分析C/C++函数调用关系的工具。
函数调用关系图(call graph)是图(graph),而且是有向图,多半还是无环图(无圈图)——如果代码中没有直接或间接的递归的话。Graphviz是专门绘制有向图和无向图的工具,所以很多call graph分析工具都以它为后端(back end)。那么前端呢?就看各家各显神通了。
调用图的分析分析大致可分为“静态”和“动态”两种,所谓静态分析是指在不运行待分析的程序的前提下进行分析,那么动态分析自然就是记录程序实际运行时的函数调用情况了。
静态分析又有两种方法,一是分析源码,二是分析编译后的目标文件。
分析源码获得的调用图的质量取决于分析工具对编程语言的理解程度,比如能不能找出正确的C++重载函数。Doxygen是源码文档化工具,也能绘制调用图,它似乎是自己分析源码获得函数调用关系的。GNU cflow也是类似的工具,不过它似乎偏重分析流程图(flowchart)。
对编程语言的理解程度最好的当然是编译器了,所以有人想出给编译器打补丁,让它在编译时顺便记录函数调用关系。CodeViz(其灵感来自Martin Devera (Devik) 的工具)就属于此类,它(1.0.9版)给GCC 3.4.1打了个补丁。另外一个工具egypt的思路更巧妙,不用大动干戈地给编译器打补丁,而是让编译器自己dump出调用关系,然后分析分析,交给Graphviz去绘图。不过也有人另起炉灶,自己写个C语言编译器(ncc),专门分析调用图,勇气可嘉。不如要是对C++语言也这么干,成本不免太高了。分析C++的调用图,还是借助编译器比较实在。
分析目标文件听起来挺高深,其实不然,反汇编的工作交给binutils的objdump去做,只要分析一下反汇编出来的文本文件就行了。下面是Cygwin下objdump -d a.exe的部分结果:
00401050 <_main>:
401050: 55 push %ebp
401051: 89 e5 mov %esp,%ebp
401053: 83 ec 18 sub $0x18,%esp
......
40107a: c7 44 24 04 00 20 40 movl $0x402000,0x4(%esp)
401081: 00
401082: c7 04 24 02 20 40 00 movl $0x402002,(%esp)
401089: e8 f2 00 00 00 call 401180 <_fopen>
从中可以看出,main()调用了fopen()。CodeViz带有分析目标文件的功能。
动态分析是在程序运行时记录函数的调用,然后整理成调用图。与静态分析相比,它能获得更多的信息,比如函数调用的先后顺序和次数;不过也有一定的缺点,比如程序中语句的某些分支可能没有执行到,这些分支中调用的函数自然就没有记录下来。
动态分析也有两种方法,一是借助gprof的call graph功能(参数-q),二是利用GCC的 -finstrument-functions 参数。
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]
-----------------------------------------------
从中可以看出,bar()被foo()调用了4次,foo()被init()和main()各调用了一次,init()被main()调用了一次。用Perl脚本分析gprof的输出,生成Graphviz的dot输入,就能绘制call graph了。这样的脚本不止一个人写过:http://www.graphviz.org/Resources.php,http://www.ioplex.com/~miallen/。
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。具体方法在《用Graphviz 可视化函数调用》中有详细介绍,这里不再赘述。
从适应能力上看,源码分析法是最强的,即便源码中有语法错,头文件不全也没关系,它照样能分析个八九不离十。而基于编译器的分析法对源码的要求要高一些,至少能编译通过(gcc 参数 -c)——能产生object file,不一定要链接得到可执行文件。这至少要求源码没有语法错,其中调用的函数不一定有定义(definition),但要有声明(declaration),也就是说头文件要齐全。当然,真的不全也没关系,自己放几个函数声明在前面就能糊弄编译器:) 至于动态分析,要求最高——程序需得运行起来。如果你要分析的是操作系统中某一部分,比如内存管理或网络协议栈,那么这里提到的两种动态分析法恐怕都不适用了。
我发现前面列举的所有免费工具几乎都和GCC、GNU Binutils脱不了干系。这里在把它们整理一下,用Graphviz绘成图(也可见附件2):
在jamvm项目上的试用结果:
cflow效果还是可以,不用--breief参数结果过大(几百M)
sourceforge上的perl工具:cflow2dot:
链接了cflow和Graphviz,直接生成svg
实验结果表明太复杂的图结构要想绘制成可视图比较困难,画出来了也是一片蜘蛛网
cflow2dot project:http://code.google.com/p/cflow2dot/
附件1:cflow2dot.pl1.0 Oct 2010
附件2:陈硕图
- 大小: 8.7 KB
分享到:
相关推荐
"Go-Gocyto-Go的调用图(Callgraph)分析和可视化"是针对Go程序的一种高级调试和分析工具,它可以帮助开发者更直观地看到函数之间的调用关系,从而提高代码质量和维护性。 调用图(Callgraph)是一种图形表示形式,...
jvm-callgraph项目为我们提供了一个强大的工具,能够生成JVM字节码的调用图,帮助我们直观地洞察代码的执行路径和调用结构。 一、JVM字节码与调用图 1. JVM字节码:Java源代码经过编译后生成的中间表示形式,它是...
前端开源库"Callgraph"是一款专为JavaScript开发者设计的工具,它能够将JavaScript代码解析成调用图,帮助开发者更好地理解和分析代码结构。调用图是一种图形表示法,用于显示程序中的函数或方法之间的调用关系,这...
CallGraph的最新动态展示了京东正在构建全局调用拓扑图,这是一张全面反映应用之间关系的地图,用于评价应用的重要性和资源使用的合理性。此外,京东还计划针对微服务治理的未来发展,解决多语言支持、升级难题,...
生成Java项目类间静态调用图的开源代码_java-callgraph
这份文档主要展示了通过`perf`工具收集的程序性能数据,具体来说,是关于某个Oracle应用程序的调用图(Call Graph)分析报告。下面我们将从多个角度对这些数据进行解读。 ### perf callgraph文档简介 `perf call...
一个基于clang的工具,可以从给定的C ++代码库生成调用图。 用法 ./clang-callgraph.py file.cpp|compile_commands.json [options] [extra clang args...] 可以理解的options有: -x name1,name2 :用逗号分隔的...
调用图生成器从 elf 二进制文件生成调用图二进制发布Windows 应用程序/ macOS Catalina 应用程序入门它需要以下库来构建代码。 uthash:哈希库 PCRE:Perl 兼容的正则表达式 libxml2: : 安装 PCRE 库和 libxml2 ...
JavaScript的基于字段的调用图构造 该项目为JavaScript实现了基于字段的调用图构造算法,如所述。 A. Feldthaus,M。Schäfer,M。Sridharan,J。Dolby,F。Tip。 有效构建JavaScript IDE服务的近似调用图。 在...
通过收集和分析服务间的调用关系,CallGraph能够提供详尽的调用图谱,帮助开发者直观地理解服务之间的依赖关系,从而更好地进行性能优化和故障排查。这种调用链跟踪技术,使得问题定位从以往的“黑盒”状态转变为...
6. **全局调用拓扑图**:CallGraph提供全局调用拓扑视图,显示各应用之间的调用关系,便于理解系统的交互模式和识别潜在问题。 7. **应用评价与资源使用合理性评估**:平台能够对应用的重要性进行评价,并分析应用...
而这一切分析的核心就是调用图(call graph),它是程序结构的一种抽象表示,能帮助我们理解和操作代码。 调用图在软件工程领域扮演着关键角色,特别是在静态分析和动态分析中。静态分析工具在不执行代码的情况下分析...
matlab点点图代码用于解析函数定义和调用的源代码的多语言工具 调用图显示了函数在程序中如何相互调用。 每个椭圆代表一个函数,每个箭头表示一个函数调用。 在下图中,主程序由节点 MAIN 表示。 它调用 6 个函数,...
call-graph-为c / c ++函数生成调用图的库 生成c / c ++函数的调用图。 这个库来自哪里? 您有多少次这样的感觉,当您在“现代” IDE中看到奇特的函数调用层次结构时,为什么我们不能在emacs中使用它? 我希望有一...
5. **应用领域**:Callgraph可能广泛应用于多个领域,例如网络拓扑可视化、程序调用图分析、生物信息学中的分子结构展示,甚至城市规划或交通网络的演示。任何需要将复杂关系以3D形式呈现的场景,Callgraph都能提供...
本文档由IBM华生实验室提供,深入探讨了程序调用图(call graph)构造的算法与实现原理。程序调用图是一种用于表示程序中函数或方法之间调用关系的数据结构,在编译器优化、静态分析等领域具有重要意义。 #### 关键...
3. **函数调用图(Call Graph)**:函数调用图是一种图形表示,显示了程序中函数间的调用关系。节点代表函数,边表示调用关系。Cflow能够生成这样的图,帮助开发者理解程序的控制流程。 4. **源码分析**:Cflow通过...
在这种背景下,"Java Fast Method Call Graph Generator" 是一个非常有用的工具,它专注于生成方法调用图,以帮助开发者更好地理解程序的结构和执行流程。 方法调用图是一种可视化表示,显示了程序中各方法之间的...
5. **构建调用图**: 最后,调用`CallGraph`类的构造函数,根据当前的场景和控制流图生成调用图。这个图展示了程序中所有方法间的调用关系。 过程间控制流程图(ICFG)则进一步扩展了控制流图的概念,它不仅包含单个...