利用Graphviz + CodeViz生成C/C++函数挪用图(call graph)
副标题#e#
一、Graphviz + CodeViz简朴先容
CodeViz是《Understanding The Linux Virtual Memory Manager》的作者 Mel Gorman 写的一款阐明C/C++源代码中函数挪用干系的open source东西(雷同的open source软件有 egypt、ncc)。其根基道理是给 GCC 打个补丁(假如你的gcc版本不切合它的要求还得先下载正确的gcc版本),让它在编译每个源文件时 dump 出个中函数的 call graph,然后用 Perl 剧本收集并整理挪用干系,转交给Graphviz绘制图形(Graphviz属于后端,CodeViz属于前端)。
CodeViz 原本是作者用来阐明 Linux virtual memory 的源码时写的一个小东西,此刻已经根基支持 C++ 语言,最新的 1.0.9 版能在 Windows + Cygwin 下顺利地编译利用。
根基先容就到这儿,假如你对其道理较量感乐趣,可以参考这篇文章:阐明函数挪用干系图(call graph)的几种要领
二、Graphviz + CodeViz编译安装
1. 安装 GraphViz挪用图的生成依赖于 GraphViz,所以首先要安装 GraphViz。可以下载源码包编译、安装(下载主页:http://www.graphviz.org/Download.php)。
假如是Ubuntu系统可以直接apt安装: sudo apt-get install graphviz
2. 安装 CodeViz下载CodeVize源码包:http://www.csn.ul.ie/~mel/projects/codeviz/
解压:tar xvf codeviz-1.0.12.tar.gz (今朝最新版是1.0.12)
进入解压后的目次:cd codeviz-1.0.12/
CodeViz 利用了一个 patch 版本的 GCC 编译器,并且差异的 CodeViz 版本利用的GCC 版本也差异,可以下载 CodeViz 的源码包后查察 Makefile 文件来确定要利用的 GCC 版
本,codeviz-1.0.12 利用 GCC-4.6.2。实际上安装 CodeViz 时安装剧本make会查抄当前的GCC版本假如不切合则会自动下载对应的 GCC并打 patch,但由于GCC较大假如网速欠好且在虚拟机中的话容易下载失败或系统错误什么的,因此这里我们照旧分步安装较量好,先安装gcc再返来安装CodeViz。
(1)安装 GCC
下载gcc-4.6.2.tar.gz到 cd codeviz-1.0.12目次下的compilers里。
下载地点:ftp://ftp.gnu.org/pub/gnu/gcc/gcc-4.6.2/gcc-4.6.2.tar.gz
CodeViz 的安装剧本 compilers/install_gcc-4.6.2.sh 会自动检测 compilers 目次下是否有 gcc 的源码包,若没有则自动下载并打 patch。这里前面已经下载,直接移到该目次即可,则剩下的就是解压安装了。install_gcc-3.4.6.sh 会解压缩 gcc打 patch,并将其安装到指定目次,若是没有指定目次,则缺省利用$HOME/gcc-graph,凡是指定安装在/usr/
local/gcc-graph(这时需要 root 权限)。
安装: ./install_gcc-4.6.2.sh
留意:这里大概安装时有些错误,详细错误及办理方案见后头。
(2)安装 CodeViz
./configure && make install-codeviz
注1:不需要 make ,因为make的浸染就是检测是否有gcc若没有则下载源码包,所以这里只要安装 codeviz 即可。详细查察 Makefile 文件。
留意:这里为什么不是凡是用的make install,因为这里make install的浸染是先安装gcc再安装codeviz,而前面已经安装了 gcc,所以这里只需要安装 codeviz ,即make install-codeviz剧本,该剧本也就是将genfull 和 gengraph 复制到/usr/local/bin 目次下。
今朝为止,CodeViz 安装完成了。
#p#副标题#e#
三、根基实用要领
GraphViz 支持生成差异气势气魄的挪用图,可是一些需要安装特另外支持东西可能库措施,有乐趣的伴侣可以到官网上查找相关资料。这里重点报告 CodeViz 的利用要领,详细的图像气势气魄节制不再详述。
CodeViz 利用两个脚原来生成挪用图,一个是 genfull,该剧本可以生成项目标完整挪用图,因此挪用图大概很大很巨大,缺省利用 cdepn 文件来建设挪用图;另一个是gengraph,该剧本可以对给定一组函数生成一个小的挪用图,还可以生成对应的postscript 文件。安装时这两个剧本被复制到/usr/local/bin 目次下,所以可以直接利用而不需要指定路径。其根基步调如下:
下面以编译一个简朴的test.c文件为例举办说明:
1. 利用方才安装的gcc-4.6.2来编译当前目次下所有.c文件,gcc/g++为编译的每个 C/C++文件生成.cdepn 文件。只要编译(参数 -c)就行,无需链接。
即为:$ ~/gcc-graph/bin/gcc test.c
2.挪用genful会在当前目次生成一个full.graph文件,该剧本可以生成项目标完整挪用图信息文件,记录了所有函数在源码中的位置和它们之间的挪用干系。 因此挪用图信息文件大概很大很巨大,,缺省利用 cdepn 文件来建设挪用图信息文件。
即为:$ genfull
3. 利用gengraph可以对给定一组函数生成一个小的挪用图,显示函数挪用干系。
即为:$ gengraph
四、简朴示例演示
本身编写个简朴的措施,看下结果再说~~~
// test.c #include <stdio.h> void test1(); void test2(); void test3(); int main() { test1(); test2(); return 0; } void test1() { } void test2() { test2(); } void test3() { }
凭据上面的三个步调依次举办如下图所示:
打开main.ps看到结果如下,一目了然:
五、进阶利用
#p#分页标题#e#
虽然各人利用CodeViz都不是用来玩的,而是用于真正的项目中,四中简朴的利用基础不足,下面来点稍微高妙点的。
1. 先来阐明下上面的执行流程
首先利用方才安装的gcc编译我们的.c文件(PS:这里必然要指定方才安装gcc的处所,不然用的是系统gcc而非我们安装的gcc),然后genfull建设full.graph文件,可以利用genfull –help可能genfull –man来查察如何利用。最简朴的方法是在项目标顶级目次以无参数方法运行。由于项目标完全挪用信息很是复杂,所以凡是只是简朴的生成项目标full.graph,然后在后头利用genfull获取需要的挪用信息。若是需要完整信息则将full.graph由dot处理惩罚然后查察来生成的postscript文件。(dot是GraphViz中的一个东西,详细利用没有深究过,感乐趣的读者可以自行查阅~~~)。到test.c地址目次运行genfull看到生成了full.graph文件,各人可以用cat查察下。接下来利用gengraph生成函数挪用图,可以利用gengraph –help或gengraph –man来查察如何利用。对付我而言,今朝只存眷下面几个选项就够了,即:
-f:指定顶级函数,即进口函数,如main等(虽然不限定是main了);
-o:指定输出的postfile文件名,不指定的话就是函数名了,如上面的main;
–output-type:指定输出范例,譬喻png、gif、html和ps,缺省是ps,如上面的main.ps;
-d:指定最大挪用层数;
-s:仅仅显示指定的函数,而差池其挪用举办展开;
-i:忽略指定的函数
-t:忽略Linux特有的内核函数集;
-k:保存由-s忽略的内部细节形成的中间文件,为sub.graph
2. 利用gengraph时的选项参数值要利用""括起来,譬喻:
gengraph –output-type "png" -f main
3. 定名斗嘴问题
在一个巨大的项目中,full.graph并不十分完美。譬喻,kernel中的模块有很多同名函数,这时genfull不能区分它们,有两种要领可以办理,个中第一种要领太巨大易错不推荐利用,这里就先容下第二种要领,纵然用genfull的-s选项,-s指定了检测哪些子目次。譬喻kernel中在mm目次和drivers/char/drm目次下都界说了alloc_pages函数,那么可以以下列方法挪用genfull:
genfull -s "mm include/linux drivers/block arch/i386"
实际的利用中,-s很是利便,请各人记着这个选项。
4. 利用Daemon/Client模式
当full.graph很大时,大量的时间耗费到读取输入文件上了,譬喻kernel的full.graph是很大的,前面生成的约莫有15M,这还不是全部内核的函数挪用阐明信息。为了节减时间,可以讲gengraph以daemon方法运行,这药利用-p选项:
gengraph -p -g linux-2.6.25/full.graph
该呼吁返回时gengraph以daemon方法运行,同时在/tmp目次下生成了codeviz.pipe文件。要生成函数挪用图,可以利用-q选项:
gengraph -q -t -d 2 -f alloc_pages
要终止gengraph的运行,利用如下呼吁:
echo QUIT > /tmpcodeviz.pipe
六、进阶演示
以阐明《嵌入式及时操纵系统 uC/OS-II (第二版)》中的第一个典型措施为例,是什么措施没干系,这里主要看的是如何利用及利用后的结果。
首先阐明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):
七、安装进程呈现的错误及办理方案
1. 在运行./install_gcc-4.6.2.sh时呈现下面错误:
gcc configure: error: Building GCC requires GMP 4.2+, MPFR 2.3.1+ and MPC 0.8.0+
#p#分页标题#e#
从错误中可以看出:GCC编译需要GMP, MPFR, MPC这三个库(有的系统已经安装了就没有这个提示,我的没有安装),有两种安装要领(发起第二种):
(1)二进制源码安装(强烈不推荐)
我利用的版本为gmp-4.3.2,mpfr-2.4.2和mpc-0.8.1,在“ftp://gcc.gnu.org/pub/gcc/infrastructure/"下载,按照提示的顺序别离安装GMP,MPFR和MPC(mpfr依赖gmp,mpc依赖gmp和mpfr),这里全部本身指定了安装目次,假如没有指定则默认分装在在/usr/include、/usr/lib和/usr/share,打点起来不利便,好比想卸载的时候还得一个个去找:
安装gmp: ./configure –prefix=/usr/local/gmp-4.3.2; make install
安装mpfr: ./configure –prefix=/usr/local/mpfr-2.4.2 –with-gmp=/usr/local/gmp-4.3.2/; make install
安装mpc: ./configure –prefix=/usr/local/mpc-0.8.1 –with-gmp=/usr/local/gmp-4.3.2/ –with-mpfr=/usr/local/mpfr-2.4.2/; make install
PS:安装进程中大概又呈现新的错误提示,请看2、3、4条。
设置情况变量:我这里指定了安装位置,假如没有指定则这几个库的默认位置是/usr/local/include和/usr/local/lib,不管有没有指定GCC编译时都大概会找不到这三个库,需要确认库位置是否在情况变量LD_LIBRARY_PATH中,查察情况变量内容可以用呼吁
$echo $LD_LIBRARY_PATH
配置该情况变量呼吁如下:
指定安装:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/gmp-4.3.2/lib:/usr/local/mpfr-2.4.2/lib:/usr/local/mpc-0.8.1/lib
默认安装:$export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib
PS:十分不推荐这种安装要领,一般来说这样简直可以乐成安装,可是也不解除安装进程中又呈现新的问题,详细看问题5。
(2)gcc自带剧本安装(强烈推荐)
要领(1)的安装要领十分繁琐,安装进程中大概呈现各类预料不到的新错误,因此gcc源码包中自带了一个gcc依赖库安装剧本download_prerequisites,位置在gcc源码目次中的contrib/download_prerequisites,因此只需要进入该目次,直接运行剧本安装即可:./download_prerequisites
PS:该剧本内容如下:
<span style="font-size:14px;">#! /bin/sh # Download some prerequisites needed by gcc. # Run this from the top level of the gcc source tree and the gcc # build will do the right thing. # # (C) 2010 Free Software Foundation # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/. MPFR=mpfr-2.4.2 GMP=gmp-4.3.2 MPC=mpc-0.8.1 wget ftp://gcc.gnu.org/pub/gcc/infrastructure/$MPFR.tar.bz2 || exit 1 tar xjf $MPFR.tar.bz2 || exit 1 ln -sf $MPFR mpfr || exit 1 wget ftp://gcc.gnu.org/pub/gcc/infrastructure/$GMP.tar.bz2 || exit 1 tar xjf $GMP.tar.bz2 || exit 1 ln -sf $GMP gmp || exit 1 wget ftp://gcc.gnu.org/pub/gcc/infrastructure/$MPC.tar.gz || exit 1 tar xzf $MPC.tar.gz || exit 1 ln -sf $MPC mpc || exit 1 rm $MPFR.tar.bz2 $GMP.tar.bz2 $MPC.tar.gz || exit 1</span>
可见是通过wget的方法下载安装,因此假如没有安装wget则需要先安装下。
#p#分页标题#e#
各人仔细看下这个剧本,发明很是简朴,就是从网上自动下载三个依赖库并解压,然后成立三个更名后的软链接别离指向这三个库,这里成立软链接进程中也大概堕落,详细看问题6,各人也可以本身修改剧本,改成直接修更名称然后移到gcc目次下。
能力:从这里也可以看出,gcc所依赖的库其实只要解压了放在gcc当前目次下就行了,要领(1)的那么多步调其实都可以省掉,直接将下载的三个压缩包解压后更名移到gcc下面即可,也不消配置情况变量了。
2. 编译gmp时呈现错误:
No usable m4 in $PATH or /usr/5bin (see config.log for reasons).
由此可以看出是缺少M4文件。可以去这里下载:http://ftp.gnu.org/gnu/m4/然后编译安装,我由于是Ubuntu系统,就直接
sudo apt-get install m4安装了。
3. 安装mpfr时呈现错误:
configure: error: gmp.h can’t be found, or is unusable.
这是因为在安装mpfr时未先安装gmp导致的,mpfr依赖于gmp。
4. 安装mpc时呈现错误:
configure: error: libgmp not found or uses a different ABI.和configure: error: libmpfr not found or uses a different ABI.“。
同样是因为未安装mpc依赖的库gmp和mpfr。
5. 在运行./install_gcc-4.6.2.sh进程中呈现错误,即凭据gcc进程中呈现的问题:
(1)libmpfr.so.1: cannot open shared object file: No such file or directory
阐明:该剧本就是安装gcc,可是假如你呈现了问题1,而且利用要领(1)办理该问题,那么你后期就大概呈现这样的问题,虽然你命运没那么背的话一般不会呈现这样的问题,横竖我运行较量背,呈现了这样的问题。
办理要领:可以参考这篇文章http://blog.csdn.net/leo115/article/details/7671819办理。
(2)../../gcc-4.6.2/gcc/realmpfr.h:27:17: fatal error: mpc.h: No such file or directory
阐明:gcc没找到所依赖的库mpc,原因许多,最有大概是你没配置情况变量或mpc放的处所差池。
办理要领:配置情况变量,看问题1。
(3) /usr/include/stdc-predef.h:30:26: fatal error: bits/predefs.h: No such file or directory
阐明:用呼吁“locate bits/predefs.h”找下该头文件的路径,发明是在’/usr/include/x86_64-linux-gnu’
办理要领:配置情况变量:
#export C_INCLUDE_PATH=/usr/include/i386-linux-gnu && export CPLUS_INCLUDE_PATH=$C_INCLUDE_PATH
(4) /usr/bin/ld: cannot find crti.o: No such file or directory
阐明:同样用“locate crti.o” 找下这个文件,在’/usr/lib/i386-linux-gnu/crti.o’。
办理要领:配置LIBRARY_PATH (LDFLAGS)这个情况变量如下:
#export LIBRARY_PATH=/usr/lib/i386-linux-gnu
(5)unwind-dw2.c:1031: error: field `info’ has incomplete type
阐明:这个错误搞了良久,因为网上找不到对应的办理要领,只说这是gcc的一个bug。
办理要领:深入到源文件中,发明错误的处所是这样的:
static _Unwind_Reason_Code uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs) { struct dwarf_fde *fde; struct dwarf_cie *cie; const unsigned char *aug, *insn, *end; memset (fs, 0, sizeof (*fs)); context->args_size = 0; context->lsda = 0; fde = _Unwind_Find_FDE (context->ra - 1, &context->bases); //这里返回了NULL if (fde == NULL) { /* Couldn't find frame unwind info for this function. Try a target-specific fallback mechanism. This will necessarily not profide a personality routine or LSDA. */ #ifdef MD_FALLBACK_FRAME_STATE_FOR MD_FALLBACK_FRAME_STATE_FOR (context, fs, success); // 堕落的处所 return _URC_END_OF_STACK; success: return _URC_NO_REASON; #else return _URC_END_OF_STACK; //堕落返回 #endif } ..... }
堕落的处所用标注了,因为fde返回了NULL,导致不能找到frame unwind info,最重要的是下面这个要领
MD_FALLBACK_FRAME_STATE_FOR (context, fs, success);
堕落了,为什么返回NULL我必定研究不出来,只知道这个函数挪用失败了,导致不乐成,于是我的办理要领十分偷懒,就是将下面的两行注释掉了,直接success,哈哈,勿喷我,因为这样做事后就办理了,后头一路乐成~~~
// MD_FALLBACK_FRAME_STATE_FOR (context, fs, success); // 堕落的处所
// return _URC_END_OF_STACK;
6. 办理ln -s 软链接发生Too many levels of symbolic links错误
从网上查找了一下原因,本来是成立软毗连的时候回收的是相对路径,所以才会发生这样的错误,办理方法是回收绝对路径成立软链接:这样问题就办理了。
八、小结
#p#分页标题#e#
本文查阅了网上的很多资料较量具体的讲授了CodeViz的安装和利用。CodeViz依赖于GraphViz,因而可以生成十分富厚的函数挪用图。详细选项的利用及图像名目标选择可由读者按照小我私家需要和偏好本身料到利用。在阐明源码的时候,把这些图形打印在手边,在上面做条记,实在利便收益颇多。
作者:csdn博客 lanxuezaipiao