函数按函数调用关系图怎么画可分为哪俩种

12193人阅读
C/C++(75)
源程序如下
#include &stdio.h&
#include &string.h&
#include &stdint.h&
typedef uint32_t UINT32;
void fun3(void)
& void* array[10] = {0};
& UINT32 size = 0;
& char **strframe = NULL;
& UINT32 i = 0, j = 0;
& size = backtrace(array, 10);
& strframe = (char **)backtrace_symbols(array, size);
& printf(&print call frame now:/n&);
& for(i = 0; i & i++){
&&& printf(&frame %d -- %s/n&, i, strframe[i]);
& if(strframe)
&&& free(strframe);
&&& strframe = NULL;
void fun2(void)
void fun1(void)
int main(void)
& return 0;
linux-xms:/data/test # gcc& test.c
linux-xms:/data/test # ./a.out
print call frame now:
frame 0 -- ./a.out [0x80484fe]
frame 1 -- ./a.out [0x8048582]
frame 2 -- ./a.out [0x804858f]
frame 3 -- ./a.out [0x80485a7]
frame 4 -- /lib/libc.so.6(__libc_start_main+0xdc) [0xb7e188ac]
frame 5 -- ./a.out [0x8048431]
只能看到地址
修改编译参数
linux-xms:/data/test # gcc -rdynamic& test.c
linux-xms:/data/test # ./a.out
print call frame now:
frame 0 -- ./a.out(fun3+0x4a) [0x80486de]
frame 1 -- ./a.out(fun2+0xb) [0x8048762]
frame 2 -- ./a.out(fun1+0xb) [0x804876f]
frame 3 -- ./a.out(main+0x16) [0x8048787]
frame 4 -- /lib/libc.so.6(__libc_start_main+0xdc) [0xb7e588ac]
frame 5 -- ./a.out [0x8048611]
现在可以看到函数名了,但没有行号,不过没关系addr2line提供了这个功能
然后我们试图用addr2line来看地址对应的函数和行号
linux-xms:/data/test # addr2line 0x80486de -e ./a.out -f
失败了,别急,我们再次修改编译参数
linux-xms:/data/test # gcc -g -rdynamic test.c
linux-xms:/data/test # ./a.out
print call frame now:
frame 0 -- ./a.out(fun3+0x4a) [0x80486de]
frame 1 -- ./a.out(fun2+0xb) [0x8048762]
frame 2 -- ./a.out(fun1+0xb) [0x804876f]
frame 3 -- ./a.out(main+0x16) [0x8048787]
frame 4 -- /lib/libc.so.6(__libc_start_main+0xdc) [0xb7dcb8ac]
frame 5 -- ./a.out [0x8048611]
linux-xms:/data/test # addr2line 0x80486de -e ./a.out -f
/data/test/test.c:14
这次我们成功了,多加了-g参数。
backtrace和backtrace_symbols的具体说明可以参考
http://www.kernel.org/doc/man-pages/online/pages/man3/backtrace.3.html
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1056430次
积分:10684
积分:10684
排名:第904名
原创:175篇
转载:217篇
评论:76条
(1)(3)(1)(2)(1)(1)(1)(1)(4)(1)(1)(2)(1)(3)(3)(5)(3)(3)(4)(4)(2)(2)(1)(2)(5)(5)(1)(2)(4)(6)(5)(7)(11)(15)(2)(10)(4)(34)(16)(21)(2)(1)(7)(13)(2)(5)(10)(8)(5)(3)(5)(3)(10)(24)(2)(7)(4)(6)(8)(3)(2)(1)(2)(4)(9)(7)(2)(2)(9)(4)(6)(2)(1)(5)(5)(2)(1)泰晓科技 - 源码分析:动态分析 C 程序函数调用关系源码分析:动态分析 C 程序函数调用关系 创作于
By Falcon of
缘由源码分析是程序员离不开的话题。无论是研究开源项目,还是平时做各类移植、开发,都避免不了对源码的深入解读。工欲善其事,必先利其器。之前已经介绍了,这里介绍如何分析程序运行时的实际函数执行情况,考虑到应用部分和内核部分有比较大的差异,该篇先介绍应用部分。主要介绍三款工具,一款是 gprof,另外一款是 valgrind,再一款则是能够把前两款的结果导出为 dot 图形的工具,叫 gprof2dot,它的功能有点类似于我们介绍的 tree2dotx。准备需要事先准备好几个相关的工具。: converts the output from many profilers into a dot graph$ sudo apt-get install python python-pip
$ sudo pip install gprof2dot
graphviz: dot 格式处理$ sudo apt-get install graphviz
gprof: display call graph profile data$ sudo apt-get install gprof
valgrind: a suite of tools for debugging and profiling programs$ sudo apt-get install valgrind
工具好了,再来一个典型的 C 程序,保存为:fib.c#include &stdio.h&
int fibonacci(int n);
int main(int argc, char **argv)
for (n = 0; n &= 42; n++) {
fib = fibonacci(n);
printf("fibonnaci(%d) = %dn", n, fib);
int fibonacci(int n)
if (n &= 0) {
} else if (n == 1) {
fib = fibonacci(n -1) + fibonacci(n - 2);
gprofGprof 用于对某次应用的运行时代码执行情况进行分析。它需要对源代码采用 -pg 编译,然后运行:$ gcc -pg -o fib fib.c
运行完以后,会生成一份日志文件:$ ls gmon.out
可以分析之:$ gprof -b ./fib | gprof2dot | dot -Tsvg -o fib-gprof.svg
查看 fib-gprof.svg 如下:可以观察到,这个图表除了调用关系,还有每个函数的执行次数以及百分比。Valgrind s callgrindValgrind 是开源的性能分析利器。它不仅可以用来检查内存泄漏等问题,还可以用来生成函数的调用图。Valgrind 不依赖 -pg 编译选项,可以直接编译运行:$ gcc -o fib fib.c
$ valgrind --tool=callgrind ./fib
然后会看到一份日志文件:$ ls callgrind*
callgrind.out.22737
然后用 gprof2dot 分析:$ gprof2dot -f callgrind ./callgrind.out.22737 | dot -Tsvg -o fib-callgrind.svg
查看 fib-callgrind.svg 如下:需要提到的是 Valgrind 提取出了比 gprof 更多的信息,包括 main 函数的父函数。不过 Valgrind 实际提供了更多的信息,用 -n0 -e0 把执行百分比限制去掉,所有执行过的全部展示出来:$ gprof2dot -f callgrind -n0 -e0 ./callgrind.out.22737 | dot -Tsvg -o fib-callgrind-all.svg
结果如下:所有的调用情况都展示出来了。热点调用分支用红色标记了出来。因为实际上一个程序运行时背后做了很多其他的事情,比如动态符号链接,还有比如 main 实际代码里头也调用到 printf,虽然占比很低。考虑到上述结果太多,不便于分析,如果只想关心某个函数的调用情况,以 main 为例,则可以:$ gprof2dot -f callgrind -n0 -e0 ./callgrind.out.22737 --root=main | dot -Tsvg -o fib-callgrind-main.svg
需要提到的是,实际上除了 gprof2dot,kcachegrind 也可以用来展示 Valgrind's callgrind 的数据:$ sudo apt-get install kcachegrind
$ kcachegrind ./callgrind.out.22737
通过 File --& Export Graph 可以导出调用图。只不过一个是图形工具,一个是命令行,而且 kcachegrind 不能一次展示所有分支,不过它可以灵活逐个节点查看。What’s more?上文我们展示了从运行时角度来分析源码的实际执行路径,目前只是深入到了函数层次。结果上跟上次的静态分析稍微有些差异。实际运行时,不同分支的调用次数有差异,甚至有些分支可能根本就执行不到。这些数据为我们进行性能优化提供了可以切入的热点。实际运行时,我们观察到除了代码中有的函数外,还有关于 main 的父函数,甚至还有库函数如 printf的内部调用细节,给我们提供了一种途径去理解程序背后运行的细节。本文只是介绍到了应用程序部分(实际上是程序运行时的用户空间),下回我们将分析,当某个应用程序执行时,哪些内核接口(系统调用)被调用到,那些接口的执行情况以及深入到内核空间的函数调用情况。 Read More:本作品由
创作,采用
进行许可。未经授权,谢绝商业使用!3.69¥9.68¥
微信扫码打赏一下& 2010 ~ 2016 泰晓科技 |35702人阅读
绘制函数调用关系图对理解大型程序大有帮助。我想大家都有过一边读源码(并在头脑中维护一个调用栈),一边在纸上画函数调用关系,然后整理成图的经历。如果运气好一点,借助调试器的单步跟踪功能和call stack窗口,能节约一些脑力。不过如果要分析的是脚本语言的代码,那多半只好老老实实用第一种方法了。如果在读代码之前,手边就有一份调用图,岂不妙哉?下面举出我知道的几种免费的分析C/C++函数调用关系的工具。函数调用关系图(call graph)是图(graph),而且是有向图,多半还是无环图(无圈图)&&如果代码中没有直接或间接的递归的话。是专门绘制有向图和无向图的工具,所以很多call graph分析工具都以它为后端(back end)。那么前端呢?就看各家各显神通了。调用图的分析分析大致可分为&静态&和&动态&两种,所谓静态分析是指在不运行待分析的程序的前提下进行分析,那么动态分析自然就是记录程序实际运行时的函数调用情况了。静态分析又有两种方法,一是分析源码,二是分析编译后的目标文件。分析源码获得的调用图的质量取决于分析工具对编程语言的理解程度,比如能不能找出正确的C++重载函数。是源码文档化工具,也能绘制调用图,它似乎是自己分析源码获得函数调用关系的。也是类似的工具,不过它似乎偏重分析流程图(flowchart)。对编程语言的理解程度最好的当然是编译器了,所以有人想出给编译器打补丁,让它在编译时顺便记录函数调用关系。(其灵感来自)就属于此类,它(1.0.9版)给GCC 3.4.1打了个补丁。另外一个工具的思路更巧妙,不用大动干戈地给编译器打补丁,而是让编译器自己dump出调用关系,然后分析分析,交给Graphviz去绘图。不过也有人另起炉灶,自己写个C语言编译器(),专门分析调用图,勇气可嘉。不如要是对C++语言也这么干,成本不免太高了。分析C++的调用图,还是借助编译器比较实在。分析目标文件听起来挺高深,其实不然,反汇编的工作交给binutils的objdump去做,只要分析一下反汇编出来的文本文件就行了。下面是Cygwin下objdump -d a.exe的部分结果: &_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&& $0xx4(%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了。这样的脚本不止一个人写过:,。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。具体方法在《》中有详细介绍,这里不再赘述。从适应能力上看,源码分析法是最强的,即便源码中有语法错,头文件不全也没关系,它照样能分析个八九不离十。而基于编译器的分析法对源码的要求要高一些,至少能编译通过(gcc 参数 -c)&&能产生object file,不一定要链接得到可执行文件。这至少要求源码没有语法错,其中调用的函数不一定有定义(definition),但要有声明(declaration),也就是说头文件要齐全。当然,真的不全也没关系,自己放几个函数声明在前面就能糊弄编译器:) 至于动态分析,要求最高&&程序需得运行起来。如果你要分析的是操作系统中某一部分,比如内存管理或网络协议栈,那么这里提到的两种动态分析法恐怕都不适用了。我发现前面列举的所有免费工具几乎都和GCC、GNU Binutils脱不了干系。这里在把它们整理一下,用Graphviz绘成图:
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:2844046次
积分:22543
积分:22543
排名:第197名
原创:144篇
评论:3264条
本人博客的文章均为原创作品,除非另有声明。个人转载或引用时请保留本人的署名及博客网址,商业转载请事先联系。我不使用即时聊天工具。也请不要用 CSDN 站内信、私信、短消息或者留言功能跟我联系。我的 gmail 用户名是 giantchen,微博 /giantchen。
(1)(1)(1)(1)(1)(1)(2)(1)(3)(1)(1)(4)(1)(1)(1)(2)(2)(1)(2)(2)(3)(6)(8)(5)(10)(1)(4)(3)(1)(3)(2)(3)(4)(1)(1)(1)(1)(1)(1)(1)(1)(1)(1)(1)(1)(2)(1)(1)(4)(5)(3)(7)(1)(2)(3)(5)(2)(2)(4)(6)(1)(3)(1)(1)当前位置:
>>>函数是两个变量x和y之间的一种对应关系,数学家欧拉1734年提出一..
函数是两个变量x和y之间的一种对应关系,数学家欧拉1734年提出一种简便的记法,使用“y=f(x)”来表示y和x的某种对应关系.如对于函数y=4-2x可用f(x)=4-2x来表示,那么当x=3时,y=4-2×3=-2,可表示成f(3)=-2.若f(x)=2x+4,你能求出f(-1)和f(f(-1))的值吗?
题型:解答题难度:中档来源:不详
f(-1)=2×(-1)+4=-2+4=2;f(f(-1))=f(2)=2×2+4=8.
马上分享给同学
据魔方格专家权威分析,试题“函数是两个变量x和y之间的一种对应关系,数学家欧拉1734年提出一..”主要考查你对&&函数值&&等考点的理解。关于这些考点的“档案”如下:
现在没空?点击收藏,以后再看。
因为篇幅有限,只列出部分考点,详细请访问。
定义:函数的值是指自变量在其取值范围内取某个值时,函数与之对应的唯一确定的值。如当x=a时,函数有唯一确定的对应值,这个值就是当x=a时的函数值。函数值的性质:①当函数式是由一个解析式表示时,欲求函数值,实质就是求代数式的值;②当一只函数解析式,又给出函数值,欲求相应的自变量的值时,实质就是解方程;③当给定函数值的一个取值范围,欲求相应的自变量的取值范围时,实质就是解不等式;④当自变量确定时,函数值时唯一确定的,但当函数值唯一确定时,对应的自变量可以是多个,如y=x2-1,当x=3时,x=±2。
发现相似题
与“函数是两个变量x和y之间的一种对应关系,数学家欧拉1734年提出一..”考查相似的试题有:
503570512616431573423760552118480930 上传我的文档
 下载
 收藏
互联网+重在分享
 下载此文档
正在努力加载中...
一种基于libclang的函数调用关系生成方法
下载积分:1000
内容提示:一种基于libclang的函数调用关系生成方法
文档格式:PDF|
浏览次数:15|
上传日期: 09:44:08|
文档星级:
该用户还上传了这些文档
一种基于libclang的函数调用关系生成方法
官方公共微信}

我要回帖

更多关于 vs 函数调用关系图 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信