天人合一之毕设——准备阶段—— 2 函数调用图(call graph)

 毕设要把数据对象的访问频率和时序图都提取出来,师兄提供了一个关于函数调用树的思路,可以简化提取过程,构造好之后就是遍历了。上网搜了下,专有名称叫函数调用图,应该是考虑到一个函数可能被多个函数调用,所以不只有一个根节点。

       工具有很多,盗个图过来(转载自http://blog.csdn.net/solstice/article/details/488865),静态分析是指在不运行前进行分析,那么动态分析自然就是记录程序实际运行时的函数调用情况了。静态分析又有两种方法,一是分析源码,二是分析编译后的目标文件。我目前主要是想做静态的源码分析,所以想先试试Doxygen(http://www.stack.nl/~dimitri/doxygen/download.html)

天人合一之毕设——准备阶段—— 2 函数调用图(call graph)

Doxygen是源码文档化工具,也能绘制调用图。 绘图要借助于graphviz工具,首先到官网下载

(https://graphviz.gitlab.io/_pages/Download/Download_windows.html)

对这个画图工具感兴趣,可以参考这篇博客 http://blog.csdn.net/lanchunhui/article/details/49472949

 在网站downloads页面下载binary文件,有Linux版本和Windows版本,比较方便。如果感兴趣,网站也提供了source file,可以自行下载编译,网站帮助文档里面有教程。

 下载好之后打开bin目录下的doxywizard.exe,就会出现配置界面。

1.wizard选项卡中

       先设置doxygen生成文档的工作空间,然后选择Project Name作为工程名称,将来会显示在文档的标题中;选择Source code directory,设置源代码所在目录,Destination directory设置文档的生成目录;选择Scan recursively则递归分析源代码目录中的子目录内的源代码。mode)中选择要处理的语言。在diagram选项里选择用graphviz里的dot工具,在下面产生选项勾住没有默认的几项(不太知道call graphs和called by graphs和他有什么区别,先都勾住吧)

2.Expert选项卡

在build中主要勾选

 EXTRACE_ALL, EXTRACE_PRIVATE, EXTRACE_STATIC, EXTRACE_LOCAL_CALSSES, EXTRACE_LOCAL_METHODS

 Dot中设置DOT_PATH即doc.exe的位置,位于graphviz安装路径下的bin目录下。(这里比较关键,我一开始没设置路径,识别不出graphviz的地址,继承关系解析出来了,但是图没画出来)。

 3.Run选项卡

 点击run doxygen,在dictionary_8c.html里得到最后的call graph。经过我人工检验,调用关系没有什么问题。但是好像顺序有点不对,按理说是init、insert、value再deinit,可能是init里调用了deinit的缘故,所以抬到上面来了。

天人合一之毕设——准备阶段—— 2 函数调用图(call graph)

 还有一个问题就是对于一些分支,静态分析不知道执行不执行,也会直接在调用里面也链接在init后面了。

天人合一之毕设——准备阶段—— 2 函数调用图(call graph)

上面参考的那篇博客也提到了,动态分析是在程序运行时记录函数的调用,然后整理成调用图。与静态分析相比,它能获得更多的信息,比如函数调用的先后顺序和次数;不过也有一定的缺点,比如程序中语句的某些分支可能没有执行到,这些分支中调用的函数自然就没有记录下来。

 我的目标是获得函数调用的顺序,所以需要用动态工具。

 动态分析也有两种方法,一是借助gprof的call graph功能(参数-q),二是利用GCC的-finstrument-functions 参数。

 1.gprof

 GNU profilergprofGNU profiler工具。它可以为Linux平台上的程序精确分析性能瓶颈,它能够记录每个函数的调用次数,每个函数消耗的处理器时间,还能够显示调用图,包括函数的调用关系。能够为我们改进应用程序的性能提供很多有利的帮助。可以参考博客http://blog.csdn.net/yetyongjin/article/details/7717464  官网http://www.cs.utah.edu/dept/old/texinfo/as/gprof_toc.htmlhttp://sourceware.org/binutils/docs/gprof/index.html

例子: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了。这样的脚本不止一个人写过。

 This line lists:
     index  A unique number given to each element of the table.
    Index numbers are sorted numerically.
         The index number is printed next to every function name so
         it is easier to look up where the function in the table.

  % time  This is the percentage of the `total' time that was spent
          in this function and its children.  Note that due to
          different viewpoints, functions excluded by options, etc,
          these numbers will NOT add up to 100%.
   self  This is the total amount of time spent in this function.

     children   This is the total amount of time propagated into this
             function by its children.

     called  This is the number of times the function was called.
          If the function called itself recursively, the number
         only includes non-recursive calls, and is followed by
        a `+' and the number of recursive calls.

     name    The name of the current function. The index number is
         printed after it. If the function is a member of a
         cycle, the cycle number is printed between the
         function's name and the index number.


 2.GCC的-finstrument-functions 参数


编译的时候需要增加增加-finstrument-functions选项,同时增加 –g选项,生成符号信息

 原理:在程序中加入hook,让它在每次进入和退出函数的时候分别调用下面这两个函数:

void __cyg_profile_func_enter( void *func_address, void *call_site )

 void __cyg_profile_func_exit ( void *func_address, void *call_site )

 当然,这两个函数本身不能被钩住,不然就万世不竭了,在声明的时候增加no_instrument_function属性的说明。

 例如,void f1( )  __attribute__ ((no_instrument_function));

 这里获得的是函数地址,用addr2line -f 可以找到地址对应的函数名称。

 例子:

 首先要在函数中加入定义

#include <stdio.h>

#define DUMP(func, call) printf("%s: func = %p, called by = %p/n", __FUNCTION__, func, call)

void __attribute__((__no_instrument_function__))
__cyg_profile_func_enter(void *this_func, void *call_site)
{
        DUMP(this_func, call_site);
}
void __attribute__((__no_instrument_function__))
__cyg_profile_func_exit(void *this_func, void *call_site)
{
        DUMP(this_func, call_site);
}

 得到结果:

 ./hello
__cyg_profile_func_enter: func = 0x8048438, called by = 0x658dec/nHello World!
__cyg_profile_func_exit: func = 0x8048438, called by = 0x658dec/n

 在Windows下的codeblocks里没有进入和退出结果。

 后来在*上看到,不止两个得出动态调用图的工具

 

Run-time call-graph (most of tools listed are profilers with callgraph functionality)
  • gprof : included in BSD or part of the GNU Binary Utilities
  • callgrind : part of Valgrind
  • KCachegrind : powerful tool to generate and analyze call graphs based on data generated by callgrind
  • Mac OS X Activity Monitor : Apple GUI process monitor Activity Monitor has a built-in call graph generator that can sample processes and return a call graph. This function is only available in Mac OS X Leopard
  • OpenPAT : includes the control_flow tool which automatically creates a Graphviz call-graph picture from runtime measurements.
  • pprof, open source tool for visualization and analysis of profile data, to be used in conjunction with gperftools.
  • CodeAnalyst from AMD (released under GPL)
  • makeppgraph is a dependency graph generator (at module level) for builds performed with makepp.
  • Intel(R) Single Event API (free, open-source)