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. thinkphp5 tp5 获取模块名控制器名方法名

    <?php namespace app\index\controller; use think\Db; use think\Controller; class Base extends Cont ...

  2. 关于win10输入法ctrl+shift+f和idea组合键冲突的解决办法。

    先Ctrl+F,按住Ctrl,再按Shift+F. 因为win10的输入法热键无法关闭(在后期的版本中好像可以了,不过没更新),在IEDA中ctrl+shift+f组合键没法使用,可以按如下按键组合使 ...

  3. [Node.js] Use Realm Object Database with Node.js

    Realm is an ACID compliant object database. In this lesson, you will learn how to install Realm, def ...

  4. Visual Stdio 环境下使用 GSL (GNU Scientific Library)

    Visual Stdio 环境下使用 GSL (GNU Scientific Library) 经測试.这里的方法不适用于VS2015. * 这篇文章有点过时了.建议从以下网址下载能够在 vs 环境下 ...

  5. [Typescript] Generics using TypeScript

    In this lesson we cover the key reason why programming languages need generics. We then show how use ...

  6. Linux下使用Python的Tkinter库出现的No module named _tkinter问题

    这是由于python的版本没有包含tkinter的模块,只需要把tk的package安装就可以了. 一般在linux才出现,windows版本一般已经包含了tkinter模块.

  7. Android的NDK开发(5)————Android JNI层实现文件的read、write与seek操作

    1. 在Android的Java层实现文件的读写操作是非常简单的,可以参看之前写的博文:http://blog.csdn.net/conowen/article/details/7296121 在JN ...

  8. 卡特兰(Catalan)数列

    卡特兰数又称卡塔兰数,英文名 Catalan number,是组合数学中一个常出现在各种计数问题中出现的数列.以比利时的数学家欧仁·查理·卡塔兰 (1814–1894)的名字来命名,其前几项为 : 1 ...

  9. ORACLE 数据库及表信息

    查看ORACLE 数据库及表信息   -- 查看ORACLE 数据库中本用户下的所有表 SELECT table_name FROM user_tables; -- 查看ORACLE 数据库中所有用户 ...

  10. Android 自动弹出软键盘(输入键盘)

    很多应用中对于一个界面比如进入搜索界面或者修改信息等等情况,为了用户体验应该自动弹出软键盘而不是让用户主动点击输入框才弹出(因为用户进入该界面必然是为了更改信息).具体实现这种效果如下: EditTe ...