绘制函数调用图(call graph)(3):codeviz + graphviz

时间:2023-02-03 12:02:38

专栏导读

本专栏第一篇文章「专栏开篇」列出了专栏的完整目录,按目录顺序阅读,有助于你的理解。

前言

codeviz官网:https://github.com/petersenna/codeviz

codeviz是一款分析C/C++源代码函数调用关系的工具,通过它可以生成函数调用关系图(call graph)。其基本原理是给gcc打个补丁,让gcc在编译每个源文件时,将其中的函数的调用关系以文本形式保存在 .cdepn 文件中,然后使用 Perl 脚本(genfull和gengraph)把 .cdepn 文件转成 dot 文件,最后由graphviz 将dot文件转成图片文件(如gif、png等)。

给gcc打补丁的过程是:
1. 下载codeviz要求的特定版本的gcc源码(跟你环境中已安装的gcc是不同的);
2. 使用codeviz的补丁文件修改gcc源码(即打补丁);
3. 编译gcc源码,得到打完补丁的gcc可执行文件(利用这个gcc可执行文件再来编译我们项目的源文件,从而得到函数调用关系);

在安装codeviz的过程中,最难的也就是编译打完补丁的gcc源码,会遇到各种编译出错,得逐个解决,大部分时间也是耗在这上面。整个安装过程大致步骤如下:

  1. 安装graphviz,因为codeviz依赖graphviz,所以要先安装graphviz。
  2. 安装codeviz,这里面包括脚本自动下载依赖的gcc源码,自动打补丁,自动编译gcc源码,最后安装codeviz。

安装graphviz

codeviz 依赖 graphviz,所以要先安装 graphviz:

# sudo apt-get install graphviz

安装codeviz

codeviz源码包下载地址:
https://web.archive.org/web/20150502053825/http://www.csn.ul.ie/~mel/projects/codeviz/#download

安装步骤:

# wget https://web.archive.org/web/20150802030038/http://www.csn.ul.ie/~mel/projects/codeviz/codeviz-1.0.12.tar.gz
# tar zxvf codeviz-1.0.12.tar.gz
# cd codeviz-1.0.12/
# ./configure && make && make install

执行make的过程中,codeviz的安装脚本 compilers/install_gcc-4.6.2.sh 会自动检测 compilers 目录下是否有其依赖的 gcc 版本的源码包(版本codeviz-1.0.12 依赖的是 gcc-4.6.2),如果没有,则会将 gcc-4.6.2.tar.gz 源码自动下载到 compilers 目录中,并将 gcc-4.6.2.tar.gz 解压到 gcc-graph 目录,然后打上补丁,最后开始编译。这个过程,其实是codeviz-1.0.12/compilers/install_gcc-4.6.2.sh脚本自动实现的。

codeviz-1.0.12/
├── bin
├── compilers
│   ├── install_gcc-4.6.2.sh    <-- 执行该脚本将: 
│   ├── gcc-4.6.2.tar.gz                        1. download gcc-4.6.2.tar.gz              
│   ├── gcc-graph                               2. mkdir gcc-graph
│   │   ├── gcc-4.6.2                           3. untar it into gcc-graph
│   ├── gcc-patches                             4. patch it5. compile it

我在编译的过程中,遇到了以下几个问题,最后一个问题没能解决,所以安装也宣告失败,不管怎样,把这次安装过程做个记录,留给需要的人参考。

问题1:无法自动下载gcc-4.6.2

如果出现无法正常下载gcc的情况,就要检查下install_gcc-4.6.2.sh脚本了。截取该脚本部分内容:

#!/bin/bash 
INSTALL_PATH=$HOME/gcc-graph
if [ "$1" != "" ]; then INSTALL_PATH=$1; fi
if [ "$2" = "compile-only" ]; then export COMPILE_ONLY=yes; fi
echo Installing gcc to $INSTALL_PATH

NCFTP=`which ncftpget`
EXIT=$?
if [ "$EXIT" != "0" ]; then
  NCFTP=ftp
fi

if [ ! -e gcc-4.6.2.tar.gz ]; then
  echo gcc-4.6.2.tar.gz not found, downloading
  $NCFTP ftp://ftp.gnu.org/pub/gnu/gcc/gcc-4.6.2/gcc-4.6.2.tar.gz
  if [ ! -e gcc-4.6.2.tar.gz ]; then
    echo Failed to download gcc, download gcc-4.6.2.tar.gz from www.gnu.org
    exit
  fi
fi
......

因为我的环境没有安装ncftpget,但有wget,所以我将:

NCFTP=`which ncftpget`

改为:

NCFTP=`which wget`

就能顺利下载gcc了。

当然,你也可以自己下载 gcc-4.6.2.tar.gz ,再把它拷贝到 codeviz-1.0.12/compilers 目录中。我还是更倾向于修改 install_gcc-4.6.2.sh 脚本,让这些过程「自动化」。

问题2:找不到依赖库gmp、mpfr、mpc

编译gcc-4.6.2的过程遇到如下错误:

configure: error: Building GCC requires GMP 4.2+, MPFR 2.3.1+ and MPC 0.8.0+.

这是因为缺少相应的依赖库,要先执行gcc源码中自带的依赖库安装脚本download_prerequisites,如下所示:

# cd codeviz-1.0.12/compilers/gcc-graph/gcc-4.6.2/
# ./contrib/download_prerequisites

注意:以上 cd 的路径是有讲究的,务必跟以上命令保持一致,这样gmp、mpfr、mpc才能下载到codeviz-1.0.12/compilers/gcc-graph/gcc-4.6.2/目录下,才能保证顺利编译。

为了让这个过程「自动化」,也可以把以上命令放到 install_gcc-4.6.2.sh 脚本中去执行。当然,如果你的环境没有报这个错,就没必要了。

问题3:linux-unwind.h编译失败

编译gcc-4.6.2的过程遇到如下错误:

../../../../gcc-4.6.2/libgcc/../gcc/config/i386/linux-unwind.h:138:17: error: field ‘info’ has incomplete type

解决方法1:

这是因为我的环境是linux 64-bit内核, struct siginfo不兼容64-bit内核,将以下文件:

codeviz-1.0.12/compilers/gcc-graph/gcc-4.6.2/gcc/config/i386/linux-unwind.h

中的 struct siginfo 替换为siginfo_t 即可,因为每次重新执行make,脚本install_gcc-4.6.2.sh都会重新解压gcc-4.6.2并覆盖linux-unwind.h,所以这样的替换要在 install_gcc-4.6.2.sh 脚本中执行,按如下修改install_gcc-4.6.2.sh 脚本内容:

tar -zxf gcc-4.6.2.tar.gz -C gcc-graph || exit

sed -i 's/struct siginfo/siginfo_t/g'  ./gcc-graph/gcc-4.6.2/gcc/config/i386/linux-unwind.h

即在解压gcc-4.6.2后,使用sed命令将linux-unwind.h文件中的struct siginfo 替换为siginfo_t,如此,重新执行make指令即可编译通过。

解决方法2:

在脚本 install_gcc-4.6.2.sh 文件中增加编译选项:

--disable-multilib

问题4:找不到crt1.o

编译gcc-4.6.2的过程遇到如下错误:

cannot find crt1.o: No such file or directory

先找出crt1.o在哪(你的路径可能跟我不一样):

# find /usr/ -name crti.o
/usr/lib/x86_64-linux-gnu/crti.o

解决方法:

# export LIBRARY_PATH=/usr/lib/x86_64-linux-gnu && make

问题5:找不到CXXABI_1.3.8

编译gcc-4.6.2的过程遇到如下错误:

msgfmt: /opt/codeviz-1.0.12/compilers/gcc-graph/objdir/x86_64-unknown-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6: version `CXXABI_1.3.8' not found (required by /usr/lib/x86_64-linux-gnu/libicuuc.so.55)

根据提示,查看其 libstdc++.so.6,确实没有 CXXABI_1.3.8:

# strings /opt/codeviz-1.0.12/compilers/gcc-graph/objdir/x86_64-unknown-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6 | grep CXXABI
CXXABI_1.3
CXXABI_1.3.1
CXXABI_1.3.2
CXXABI_1.3.3
CXXABI_1.3.4
CXXABI_1.3.5
CXXABI_1.3
CXXABI_1.3.2
CXXABI_1.3.1
CXXABI_1.3.5
CXXABI_1.3.4
CXXABI_1.3.3

尝试了网上说的很多方法,该问题依然没有解决,感觉跟版本依赖性有关,水挺深的,最终放弃,如果有读者知道怎么解决,诚盼你的留言告知。

总结

由于codeviz必须依赖特定版本的gcc(给其打补丁,并重新编译),codeviz 的版本迭代远远跟不上gcc,造成安装过于费劲(甚至以失败告终,本次体验就没能安装成功),让人望而却步