运行环境:虚拟机下的Ubuntu 11.04

结合Graphviz工具,使用CodeViz可以生成直观和漂亮的C/C++程序函数之间的调用关系图。

1、安装graphviz

在安装CodeViz之前,必须先安装它所依赖的工具dot,否则将无法完成./configure操作并提示以下错误信息:

checking for dot...not found
FATAL: The program dot was not in your path. This is probably available for your distribution
with the graphviz package. Install this before running configure again

既可以从http://www.graphviz.org/Download_linux_ubuntu.php 上下载最新版本的graphviz安装程序手动安装,也可以使用以下命令自动安装:

$ sudo apt-get install graphviz graphviz-dev graphviz-doc

程序简要说明:

graphviz - rich set of graph drawing tools
graphviz-dev - transitional package for graphviz-dev rename
graphviz-doc - additional documentation for graphviz

安装相关库:

$ sudo apt-get install libgv-*

相关库简要说明:

libgv-guile - Guile bindings for graphviz
libgv-lua - Lua bindings for graphviz
libgv-ocaml - OCaml bindings for graphviz
libgv-perl - Perl bindings for graphviz
libgv-php5 - Php5 bindings for graphviz
libgv-python - Python bindings for graphviz
libgv-ruby - Ruby bindings for graphviz
libgv-tcl - Tcl bindings for graphviz

2、安装CodeViz

从http://www.skynet.ie/~mel/projects/codeviz/ 上下载CodeViz安装包codeviz-1.0.10.tar.gz以及从ftp://ftp.gnu.org/pub/gnu/gcc/gcc-3.4.6 上下载GCC源码包gcc-3.4.6.tar.gz,并把它们拷贝到Ubuntu下的同一目录下。

然后解压CodeViz安装包,并把gcc-3.4.6.tar.gz拷贝到codeviz-1.0.10/compiler目录下:

$ tar zvxf codeviz-1.0.10.tar.gz
$ cd codeviz-1.0.10/
$ cp ../gcc-3.4.6.tar.gz compilers/ //也可以略过这一步,让ncftp(Ubuntu默认未安装)在make执行过程中自动下载gcc-3.4.6.tar.gz
$ ./configure

Patched GCC默认安装在/usr/local/gccgraph目录下。

  安装GCC时,需要安装下面的库

  apt-get install libgmp10-dev libmpfr-dev libmpc-dev

在执行make之前,先通过以下命令创建一个链接文件asm:

$ sudo ln -sf /usr/include/asm-generic/ /usr/include/asm

否则,在make执行过程中会出现在/usr/include/linux/errno.h文件中找不到asm/errno.h文件的错误信息并中止编译。

接下来执行make和make install命令即可在/usr/local/gccgraph目录下创建打过补丁的GCC编译器以及在/usr/local/bin目录下创建两个perl脚本程序genfull和gengraph。

$ make
$ sudo make install

3、CodeViz的使用举例

例子源代码如下:

/* test.c */

#include <stdio.h>
#include <stdlib.h>
#include <string.h> void func(char *p)
{
p = (char *)malloc(20);
} int main(void)
{
char *str = NULL; func(str); strcpy(str, "hello world"); printf("string is %s\n", str); return 0;
}

使用刚生成的编译器编译test.c,会相应地生成一个test.c.cdepn文件。

$ /usr/local/gccgraph/bin/gcc test.c

test.c.cdepn文件信息:

F {func} {test.c:6}
C {func} {test.c:7} {malloc}
F {main} {test.c:11}
C {main} {test.c:18} {printf}
C {main} {test.c:16} {strcpy}
C {main} {test.c:14} {func}

然后执行genfull脚本生成full.graph。

$ genfull

full.graph文件信息:

digraph fullgraph {
node [ fontname=Helvetica, fontsize=12 ];
"func" [label="func\ntest.c:5:"];
"main" [label="main\ntest.c:10:"];
"func" -> "malloc" [label="test.c:7"];
"main" -> "func" [label="test.c:14"];
"main" -> "strcpy" [label="test.c:16"];
"main" -> "printf" [label="test.c:18"];
}

最后执行gengraph生成函数关系调用图。

$ gengraph -f main --output-type "png"

-f指定最顶层的函数,--output-type指定图片的输出格式。

至于genfull和gengraph的详细使用说明请参考man手册,使用以下命令查看:

$ sudo apt-get install perl-doc //须先安装perl-doc
$ genfull --man
$ gengraph --man 另外参考:
http://blog.csdn.net/solstice/article/details/486788

CodeViz是《Understanding The Linux Virtual Memory Manager》(at Amazon,下载地址在页尾)的作者 Mel Gorman 写的一款分析C/C++源代码中函数调用关系的open source工具(类似的open source软件有 egypt、ncc)。其基本原理是给 GCC 打个补丁,让它在编译时每个源文件时 dump 出其中函数的 call graph,然后用 Perl 脚本收集并整理调用关系,转交给Graphviz绘制图形。

CodeViz 原本是作者用来分析 Linux virtual memory 的源码时写的一个小工具,现在已经基本支持 C++ 语言,最新的 1.0.9 版能在 Windows + Cygwin 下顺利地编译使用:)。需要注意的是:1) 下载 GCC 3.4.1 的源码 gcc-3.4.1.tar.gz 放到 codeviz-1.0.9/compilers,2) 安装 patch 程序(属于Utils类),3) 从 http://www.graphviz.org 下载并安装 Graphviz 2.6。

我用 CodeViz 分析《嵌入式实时操作系统 uC/OS-II (第二版)》中的第一个范例程序,步骤如下:

1. 想办法让 gcc 能编译uC/OS 2.52和范例程序的源码,每个C源文件生成对于的.c.cdepn文件。只要编译(参数 -c)就行,无需连接。

2. 调用genfull生成full.graph,这个文件记录了所有函数在源码中的位置和它们之间的调用关系。

3. 使用gengraph生成我关心的函数的调用关系。

首先分析main():

1. gengraph --output-type gif -f main
分析main()的call graph,得到的图如下,看不出要领:

2. gengraph --output-type gif -f main -s OSInit
暂时不关心OSInit()的内部实现细节(参数 -s),让它显示为一个节点。得到的图如下,有点乱,不过好多了:

3. gengraph --output-type gif -f main -s OSInit -i "OSCPUSaveSR;OSCPURestoreSR"
基本上每个函数都会有进入/退出临界区的代码,忽略之(参数 -i)。得到的图如下,基本清楚了:

4. gengraph --output-type gif -f main -s "OSInit;OSSemCreate" -i "OSCPUSaveSR;OSCPURestoreSR" -k
OSSemCreate()的内部细节似乎也不用关心,不过保留中间文件sub.graph(参数 -k),得到的图如下,

5. dot -Tgif -o main.gif sub.graph
修改sub.graph,使图形符合函数调用顺序,最后得到的图如下,有了这个都不用看代码了:)

接着分析OSTimeDly()的被调用关系:

gengraph --output-type gif -r -f OSTimeDly

看看哪些函数调用了OSTimeDly(),参数 -r ,Task()和TaskStart()都是用户编写的函数:

最后看看Task()直接调用了哪些函数:

gengraph --output-type gif -d 1 -f Task

只看从Task出发的第一层调用(参数 -d 1):

在分析源码的时候,把这些图形打印在手边,在上面做笔记,实在方便得很。

[转] 使用CodeViz生成C/C++函数调用关系图的更多相关文章

  1. 利用graphviz软件和pycallgraph库自动生成Python代码函数调用关系图

    参考博文:https://blog.csdn.net/qq_36408085/article/details/82952846 https://blog.csdn.net/fondax/article ...

  2. 看开源代码利器—用Graphviz + CodeViz生成C/C++函数调用图(call graph)

    一.Graphviz + CodeViz简单介绍 CodeViz是<Understanding The Linux Virtual Memory Manager>的作者 Mel Gorma ...

  3. 用callgraph生成的函数调用关系图

    Wu Zhangjin 创作于 2015/04/05 评论打赏 By Falcon of TinyLab.org 2015/04/03 1 故事缘由 源码分析是程序员离不开的话题.无论是研究开源项目, ...

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

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

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

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

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

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

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

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

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

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

  9. 使用egypt+graphviz生成函数调用关系图示例

    总结: make  (-fdump-rtl-expand)  去除编译优化,比如-O3 egypt test.c.128r.expand >test.dot  可以手动打开dot文件去除一些孤立 ...

随机推荐

  1. Why Doesn’t Drag-and-Drop work when my Application is Running Elevated? – A: Mandatory Integrity Control and UIPI(转载)

    f you run notepad elevated (Right click | Run as Administrator), and you try and drag-and-drop a fil ...

  2. php实现返回上一页的功能的3种有效方法

    php实现返回上一页的功能的3种有效方法 header(location:你的上一页的路径);   //   注意这个函数前不能有输出 header(location:.getenv("HT ...

  3. how to use a xml_id in field domain

    "[('parent_id','child_of', %(other_module.xml_id)d)]"

  4. assert sys.modules[modname] is old_mod

    使用了pypiwin32 包中的pythoncom的时候,当跑在apache下,日志报错: [Thu Aug 27 17:06:44 2015] [error] [client 127.0.0.1] ...

  5. 修改app名字

    一张图说明问题 如果没有成功clean一下,或者卸载掉原有的重新生成一下 如果要修改路径名和工程名有个复杂的方法 http://blog.sina.com.cn/s/blog_a42013280101 ...

  6. Perl常用特殊变量

    perl 内置变量 $- 当前页可打印的行数,属于Perl格式系统的一部分 $! 根据上下文内容返回错误号或者错误串 $” 列表分隔符 $# 打印数字时默认的数字输出格式 $$ Perl解释器的进程I ...

  7. 关于C#的委托

    作者  陈嘉栋(慕容小匹夫) 阅读目录 0x00 前言 0x01 从观察者模式说起 0x02 向Unity3D中的SendMessage和BroadcastMessage说拜拜 0x03 认识回调函数 ...

  8. Can deep learning help you find the perfect girl?

    Can deep learning help you find the perfect girl? One of the first things I did when I moved to Mont ...

  9. 9.DataPager

    ListView搭配DataPager控件实现分页.有两种使用方式:一是将DataPager声明到ListView中:一种是DataPager\ListView没有嵌套关系,然后将DataPager的 ...

  10. oracle中的exists 和not exists 用法详解(转)

    有两个简单例子,以说明 “exists”和“in”的效率问题 1) select * from T1 where exists(select 1 from T2 where T1.a=T2.a) ; ...