Wu Zhangjin 创作于 2015/04/05

By Falcon of TinyLab.org 2015/04/03

1 故事缘由

源码分析是程序员离不开的话题。无论是研究开源项目,还是平时做各类移植、开发,都避免不了对源码的深入解读。

工欲善其事,必先利其器。今天我们来玩转一个小工具,叫 Callgraph,它可以把 C 语言的函数调用树(或者说流程图)画出来。

传统的命令行工具 Cscope, Ctags 可以结合 vim 等工具提供高效快捷的跳转,但是无法清晰的展示函数内部的逻辑关系。

至于图形化的IDE,如 QtCreator, Source Insight, Eclipse, Android Studio 等,却显得笨重,而且不一定支持导出调用关系图。

开源软件在线代码交叉检索 一文中我们也介绍到了诸如 LXR, OpenGrok 之类的工具,它们避免了本地代码库而且提供了方便的 Web 展示,不过也无法提供函数关系的清晰展示。

下面开始 Callgraph 之旅。

2 安装 Callgraph

Callgraph 实际由三个工具组合而成。

  • 一个是用于生成 C 函数调用树的 cflow 或者 calltree,下文主要介绍 cflow。
  • 一个处理 dot 文本图形语言的工具,由 graphviz 提升。建议初步了解下:DOT 语言
  • 一个用于把 C 函数调用树转换为 dot 格式的脚本:tree2dotx

以 Ubuntu 为例,分别安装它们:

  1. $ sudo apt-get install cflow graphviz

如果确实要用 calltree,请通过如下方式下载。不过 calltree 已经年久失修了,建议不用。

  1. $ wget -c https://github.com/tinyclub/linux-0.11-lab/raw/master/tools/calltree

接下来安装 tree2dotx 和 Callgraph,这里都默认安装到 /usr/local/bin

  1. $ wget -c https://github.com/tinyclub/linux-0.11-lab/raw/master/tools/tree2dotx
  2. $ wget -c https://github.com/tinyclub/linux-0.11-lab/raw/master/tools/callgraph
  3. $ sudo cp tree2dotx callgraph /usr/local/bin
  4. $ sudo chmod +x /usr/local/bin/{tree2dotx,callgraph}

注:部分同学反馈,tree2dotx输出结果有异常,经过分析,发现用了 mawk,所以请提交安装下gawk

  1. $ sudo apt-get install gawk

3 分析 Linux 0.11

3.1 准备

先下载泰晓科技提供的五分钟 Linux 0.11 实验环境:Linux-0.11-Lab

  1. $ git clone https://github.com/tinyclub/linux-0.11-lab.git && cd linux-0.11-lab

3.2 初玩

回到之前在 Linux-0.11-Lab 展示的一副图:

它展示了 Linux 0.11 的主函数 main 的调用层次关系,清晰的展示了内核的基本架构。那这样一副图是如何生成的呢?非常简单:

  1. $ make cg f=main
  2. Func: main
  3. Match: 3
  4. File:
  5. 1 ./init/main.c: * main() use the stack at all after fork(). Thus, no function
  6. 2 ./init/main.c: * won't be any messing with the stack from main(), but we define
  7. 3 ./init/main.c:void main(void) /* This really IS void, no error here. */
  8. Select: 1 ~ 3 ? 3
  9. File: ./init/main.c
  10. Target: ./init/main.c: main -> callgraph/main.__init_main_c.svg

需要注意的是,上面提供了三个选项用于选择需要展示的图片,原因是这个 callgraph 目前的函数识别能力还不够智能,可以看出 3 就是我们需要的函数,所以,上面选择序号 3。

生成的函数调用关系图默认保存为 callgraph/main.__init_main_c.svg。

图片导出后,默认会调用 chromium-browser 展示图片,如果不存在该浏览器,可以指定其他图片浏览工具,例如:

  1. $ make cg b=firefox

上面的 make cg 实际调用 callgraph

  1. $ callgraph -f main -b firefox

3.3 玩转它

类似 main 函数,实际也可渲染其他函数,例如:

  1. $ callgraph -f setup_rw_floppy
  2. Func: setup_rw_floppy
  3. File: ./kernel/blk_drv/floppy.c
  4. Target: ./kernel/blk_drv/floppy.c: setup_rw_floppy -> callgraph/setup_rw_floppy.__kernel_blk_drv_floppy_c.svg

因为只匹配到一个 setup_rw_floppy,无需选择,直接就画出了函数调用关系图,而且函数名自动包含了函数所在文件的路径信息。

  • 模糊匹配

    例如,如果只记得函数名的一部分,比如 setup,则可以:

    1. $ callgraph -f setup
    2. Func: setup
    3. Match: 4
    4. File:
    5. 1 ./kernel/blk_drv/floppy.c:static void setup_DMA(void)
    6. 2 ./kernel/blk_drv/floppy.c:inline void setup_rw_floppy(void)
    7. 3 ./kernel/blk_drv/hd.c:int sys_setup(void * BIOS)
    8. 4 ./include/linux/sys.h:extern int sys_setup();
    9. Select: 1 ~ 4 ?

    因为 setup_rw_floppy 函数是第 2 个被匹配到的,选择 2 就可以得到相同的结果。

  • 指定函数所在文件(或者文件所在的目录)

    1. $ callgraph -f setup -d ./kernel/blk_drv/hd.c

    类似的, make cg 可以这么用:

    1. $ make cg f=setup d=./kernel/blk_drv/hd.c

    看看效果:

4 分析新版 Linux

4.1 初玩

先来一份新版的 Linux,如果手头没有,就到 www.kernel.org 搞一份吧:

  1. $ wget -c https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.10.73.tar.xz
  2. $ tar Jxf linux-3.10.73.tar.xz && cd linux-3.10.73

玩起来:

  1. $ callgraph -f start_kernel -d init/main.c

4.2 酷玩

  • 砍掉不感兴趣的函数分支

    上面生成的图,有没有觉得 printk 之类的调用太多,觉得很繁琐。没关系,用 -F 砍掉。

    1. $ callgraph -f start_kernel -d init/main.c -F printk

    如果要砍掉很多函数,则可以指定一个函数列表:

    1. $ callgraph -f start_kernel -d init/main.c -F "printk boot_cpu_init rest_init"
  • 指定函数调用深度:

    用 -D 命令可以指定:

    1. $ callgraph -f start_kernel -d init/main.c -F "printk boot_cpu_init rest_init" -D 2
  • 指定函数搜索路径

    我们来看看 update_process_times 的定义,用 -d 指定搜索路径:

    1. $ callgraph -f update_process_times -d kernel/

    它会自动搜索 kernel/ 目录并生成一副图,效果如下:

    考虑到 callgraph 本身的检索效率比较低(采用grep),如果不能明确函数所在的目录,则可以先用 cscope 之类的建立索引,先通过这些索引快速找到函数所在的文件,然后用 -d 指定文件。

    例如,假设我们通过 cs find g update_process_times 找到该函数在 kernel/timer.c 中定义,则可以:

    1. $ callgraph -f update_process_times -d kernel/timer.c

5 原理分析

callgraph 实际上只是灵活组装了三个工具,一个是 cflow,一个是 tree2dotx,另外一个是 dot。

5.1 cflow:拿到函数调用关系

  1. $ cflow -b -m start_kernel init/main.c > start_kernel.txt

5.2 tree2dotx: 把函数调用树转换成 dot 格式

  1. $ cat start_kernel.txt | tree2dotx > start_kernel.dot

5.3 用 dot 工具生成可以渲染的图片格式

这里仅以 svg 格式为例:

  1. $ cat start_kernel.dot | dot -Tsvg -o start_kernel.svg

实际上 dot 支持非常多的图片格式,请参考它的手册:man dot

6 趣玩 tree2dotx

关于 tree2dotx,需要多说几句,它最早是笔者 2007 年左右所写,当时就是为了直接用图文展示树状信息。该工具其实支持任意类似如下结构的树状图:

  1. a
  2. b
  3. c
  4. d
  5. x
  6. y
  7. e
  8. f

所以,我们也可以把某个目录结构展示出来,以 Linux 0.11 为例:

  1. $ cd linux-0.11
  2. $ tree -L 2 | tree2dotx | dot -Tsvg -o tree.svg

如果觉得一张图显示的内容太多,则可以指定某个当前正在研读的内核目录,例如 kernel 部分:

  1. $ tree -L 2 kernel | tree2dotx -f Makefile | dot -Tsvg -o tree.svg

看下效果:

7 What’s more?

上文展示了如何把源码的调用关系用图文的方式渲染出来。好处显而易见:

  • 不仅可以清晰的理解源码结构,从而避免直接陷入细节,进而提高源码分析的效率。
  • 也可以基于这个结果构建流程图,然后用 inkscape 之类的工具做自己的调整和扩充,方便做后续展示。
  • 还可以把这些图文用到文档甚至书籍中,以增加可读性。

Callgraph 的图文展示基于 cflow 或者 calltree,它们都只是静态源码分析的范畴。

后续我们将从从运行时角度来动态分析源码的实际执行路径。我们计划分开展示应用部分和内核部分。

http://www.tinylab.org/callgraph-draw-the-calltree-of-c-functions/

http://blog.csdn.net/flyforfreedom2008/article/details/46418793

用callgraph生成的函数调用关系图的更多相关文章

  1. 用callgraph生成的两张函数调用关系图

    参考这里,感觉很Cool吧. Linux-0.11函数调用关系图: QEMU函数调用关系图:

  2. 分析函数调用关系图(call graph)的几种方法

    绘制函数调用关系图对理解大型程序大有帮助.我想大家都有过一边读源码(并在头脑中维护一个调用栈),一边在纸上画函数调用关系,然后整理成图的经历.如果运气好一点,借助调试器的单步跟踪功能和call sta ...

  3. [转] 使用CodeViz生成C/C++函数调用关系图

    运行环境:虚拟机下的Ubuntu 11.04 结合Graphviz工具,使用CodeViz可以生成直观和漂亮的C/C++程序函数之间的调用关系图. 1.安装graphviz 在安装CodeViz之前, ...

  4. c语言分析函数调用关系图(call graph)的几种方法

    一.基于 Doxygen或 lxr 的API形式的文档系统. 二.基于CodeViz, CodeViz是<Understanding The Linux Virtual Memory Manag ...

  5. 用CodeViz绘制函数调用关系图(call graph)

    CodeViz是<Understanding The Linux Virtual Memory Manager>(at Amazon,下载地址在页尾)的作者 Mel Gorman 写的一款 ...

  6. python函数调用关系图(python call graph)

    由于要重构项目的部分代码,要整理好主要的函数调用关系,不想自己看代码慢慢画出结构,想找出一种通用的,节省人力的方法得出函数间的调用关系图,于是发现以下几个工具.(内网没装好graphviz,还没真正用 ...

  7. 用cflow工具生成代码函数调用关系

    1. 安装 sudo apt-get install cflow 2.使用 cflow [options...] [file]... 例: cflow main.c 生成main.c文件例的函数调用关 ...

  8. 用cflow工具生成代码函数调用关系【转】

    转自:http://www.cnblogs.com/feng-zi/p/5469652.html . 安装 sudo apt-get install cflow .使用 cflow [options. ...

  9. visual studio 2013 生成依赖项关系图出错

    开始是说无法连接到sql服务器,我安装卸载localdb http://www.microsoft.com/zh-cn/download/details.aspx?id=29062 下载 CHS\x6 ...

随机推荐

  1. Chinese remainder theorem

    https://en.wikipedia.org/wiki/Chinese_remainder_theorem http://planetmath.org/ChineseRemainderTheore ...

  2. mariadb远程不能访问,出现Can't connect to MySQL server on '' (10061)

    一,现象: 1. 1 远程连接数据库mariadb时,报错 二,定位: 2. 1  首先本地连接上数据库,然后操作权限表数据 ,然后远程再次连接依然连接不上: 2. 2   搜索mariadb的配置文 ...

  3. ios开发多线程一:了解-NSOperation的基本使用

    #import "ViewController.h" @interface ViewController () @end @implementation ViewControlle ...

  4. js中动态创建json,动态为json添加属性、属性值的实例

    如下所示: ? 1 2 3 4 5 6 7 var param = {};  for(var i=0;i<fields.length;i++){  var field = fields[i]; ...

  5. active set method(激活集方法)

    在优化问题的求解中,如果待优化(最大最小)的目标函数,其解集受限于一组约束条件, g1(x)≥0,-,gk(x)≥0 约束条件定义着可行域(feasible region),对于可行域中的任一点 x ...

  6. 【codeforces 765C】Table Tennis Game 2

    [题目链接]:http://codeforces.com/contest/765/problem/C [题意] 枚举游戏先拿到k分的人胜; 然后两个人一个人得了a分,一个人得了b分; 问你最多可能进行 ...

  7. Oracle数据库表空间 数据文件 用户 以及表创建的SQL代码

    --create the tablespace CREATE SMALLFILE TABLESPACE "TABLE_CONTAINER" --创建表空间 DATAFILE 'E: ...

  8. Asp.net压缩网站中的文件

    为了说明自定义虚拟路径,这里弄个示例,仅仅用一个压缩包存放一个网站的多个文件. 这个东西是要需要通过实现3个抽象类来实现: System.Web.Hosting.VirtualPathProvider ...

  9. 【心情】2016ICPC青岛站打铁记

    Day0 下午到的青岛; 然后就在下面这两个地方转了很久:一直找不到公交站台 路上还看到了一个类似堡垒的东西:感觉屌屌的. 然后在落日的余晖下:我们找到了公交站台; 路上不知道他们在讨论什么:GPS什 ...

  10. 微信小程序唤起其他微信小程序 / 移动应用App唤起小程序

    微信小程序唤起其他微信小程序 / 移动应用App唤起小程序 1. 微信小程序唤起微信小程序 小程序唤起其他小程序很简单 先上链接 小程序跳转小程序 Navigator组件 推荐使用 小程序跳转小程序 ...