callgraph工具, 函数调用关系 | cflow, egypt 和 doxygen 使用
概述
作为一名linux嵌入式开发人员,长时间与linux打交道,多数情况工作在终端 + vim的环境下,已经习惯了文字界面。在阅读代码时,经常会在cscope中跳来跳去,但对于大型软件项目来说,这种情况就有些脑栈不够。闲来无事,研究下有什么工具可以生成函数调用图,来提高阅读代码的效率。能够实现代码调用的工具很多,经过研究cflow, egypt和doxygen丰富程度依次递增,可以基本满足不同层次的多数需求。
cflow
GNU cflow 分析 C 源文件的集合并打印图形,绘制程序中的控制流。GNU cflow能够为C源生成直接和反转的流图。(可选)可以生成交叉引用列表。实现了两种输出格式:POSIX 和 GNU(扩展)。
可以选择在分析之前对输入文件进行预处理。
优点:
- 输出树形文本调用关系:方便快捷,可以直接在终端内使用
- 不需要编译运行,静态分析
- 简单轻便:使用简单方便
- 函数位置是按照代码位置输出,方便查看
缺点:
- 不能跨文件分析:不能分析调用的外部函数
- 分析不够准确:如果存在宏嵌套等情况将无法分析走的分支
使用
安装cflow
sudo apt-get install cflow
以simple-tftp代码client.c中的main函数为例进行分析
wsk@wsk:~/test/simple-tftp$ cflow -Tn -m main client.c
1 +-main() <int main (int argc, char *argv[]) at client.c:139>
2 +-getopt()
3 +-atoi()
4 +-fprintf()
5 +-exit()
6 +-sprintf()
7 +-printf()
8 +-memset()
9 +-getaddrinfo()
10 +-gai_strerror()
11 +-socket()
12 +-perror()
13 +-connect()
14 +-close()
15 +-inet_ntop()
16 +-get_in_addr() <void *get_in_addr (struct sockaddr sa) at client.c:130>
17 +-fcntl()
18 +-bzero()
19 +-fopen()
20 +-strcpy()
21 +-fill_header()
22 +-fill_packet()
23 +-add_checksum()
24 +-htons()
25 +-tftp() <int tftp (int sockfd, const void *packet, int expected_seqnum, int timeout, int flag) at client.c:44>
26 | +-send()
27 | +-perror()
28 | +-exit()
29 | +-printf()
30 | +-recvfromTimeOut() <int recvfromTimeOut (int socket, long sec, long usec) at client.c:29>
31 | | +-FD_ZERO()
32 | | +-FD_SET()
33 | | \-select()
34 | +-recv()
35 | \-read_header()
36 +-fclose()
37 +-fread()
38 \-freeaddrinfo()
还可以查看反向调用
1 +-FD_SET()
2 \-recvfromTimeOut() <int recvfromTimeOut (int socket, long sec, long usec) at client.c:29>
3 \-tftp() <int tftp (int sockfd, const void *packet, int expected_seqnum, int timeout, int flag) at client.c:44>
4 \-main() <int main (int argc, char *argv[]) at client.c:139>
5 +-FD_ZERO()
6 \-recvfromTimeOut() <int recvfromTimeOut (int socket, long sec, long usec) at client.c:29> [see 2]
7 +-add_checksum()
8 \-main() <int main (int argc, char *argv[]) at client.c:139> [see 4]
9 +-atoi()
10 \-main() <int main (int argc, char *argv[]) at client.c:139> [see 4]
11 +-bzero()
12 \-main() <int main (int argc, char *argv[]) at client.c:139> [see 4]
13 +-close()
14 \-main() <int main (int argc, char *argv[]) at client.c:139> [see 4]
15 +-connect()
16 \-main() <int main (int argc, char *argv[]) at client.c:139> [see 4]
17 +-exit()
18 +-tftp() <int tftp (int sockfd, const void *packet, int expected_seqnum, int timeout, int flag) at client.c:44> [see 3]
19 \-main() <int main (int argc, char *argv[]) at client.c:139> [see 4]
20 +-fclose()
21 \-main() <int main (int argc, char *argv[]) at client.c:139> [see 4]
22 +-fcntl()
23 \-main() <int main (int argc, char *argv[]) at client.c:139> [see 4]
24 +-fill_header()
25 \-main() <int main (int argc, char *argv[]) at client.c:139> [see 4]
26 +-fill_packet()
27 \-main() <int main (int argc, char *argv[]) at client.c:139> [see 4]
28 +-fopen()
29 \-main() <int main (int argc, char *argv[]) at client.c:139> [see 4]
30 +-fprintf()
31 \-main() <int main (int argc, char *argv[]) at client.c:139> [see 4]
32 +-fread()
33 \-main() <int main (int argc, char *argv[]) at client.c:139> [see 4]
34 +-freeaddrinfo()
35 \-main() <int main (int argc, char *argv[]) at client.c:139> [see 4]
egypt
Egypt 是一个用于创建 C 程序调用图的简单工具。 Egypt既不分析源代码也不布置图表。 相反,它将源代码分析留给 GCC,将图形布局留给 Graphviz,这两者在各自的工作中都比Egypt本身所希望的要好。 Egypt只是一个非常小的 Perl 脚本,它将这些现有工具粘合在一起。
优点:
- 软件小巧,易于使用
- 使用简单
缺点:
- 依赖编译生成的dump文件
- 不支持函数指针分析
- 生成结果依赖传入的dump文件
使用
使用路径:gcc生成dump文件
--> Egypt解析dump文件后生成dot
--> Graphviz将dot生成图表
安装Graphviz
sudo apt-get install graphviz
安装Egypt
解压Egypt软件包后进入到目录中
perl Makefile.PL
make
sudo make install
然后以simple-tftp软件包为例:
- 增加或修改编译参数
-fdump-rtl-expand
wsk@wsk:~/test/simple-tftp$ git diff Makefile
diff --git a/Makefile b/Makefile
index 25e88db..2d1e7c9 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-CC = gcc
+CC = gcc -fdump-rtl-expand
all: client server
- 编译生成dump文件
*.expand
wsk@wsk:~/test/simple-tftp$ ls
callgraph.svg checksum.c.229r.expand checksum.o client.c header.c header.h Makefile server server.c.229r.expand
checksum.c checksum.h client client.c.229r.expand header.c.229r.expand header.o Readme.md server.c
- 生成svg图片
这里面分成3中情况进行生成:
case 1:全部的expand生成在一起
case 2:仅包含client的(simple-tftp软件包会编译出两个程序client和server)
case 3:仅包含server的(simple-tftp软件包会编译出两个程序client和server)
对应的命令
case 1:
egypt *.expand | dot -Grankdir=LR -Tsvg -o callgraph.svg
case 2:
egypt client.c.229r.expand checksum.c.229r.expand header.c.229r.expand | dot -Grankdir=LR -Tsvg -o client.svg
case 3:
egypt server.c.229r.expand checksum.c.229r.expand header.c.229r.expand | dot -Grankdir=LR -Tsvg -o server.svg
生成的svg图表对比
【case 1】全部的dump
【case 2】client dump
【case 3】server dump
通过对比和分析,可以有初步的结果:
- 系统函数显示不出来(分析client svg)
- 传入不同的dump文件,显示的结果会有不同
doxygen
doxygen可以生成项目文档不仅包含调用关系,详见下面gjf,可以看到生成的内容非常的丰富。
优点:
- 功能强大,不仅可以生成代码调用关系
- 输出文件内容丰富:html,chm和pdf文件
- 配置容易,使用简单
还是以simple-tftp工程为例
安装doxygen
sudo apt-get install doxygen
sudo apt-get install graphviz
生成Doxyfile
进入到项目目录
doxygen -g
配置Doxyfile:(下面的修改也可以自己封装成脚本来实现)。下面的配置仅包含常用参数,有需要可以细看Doxyfile中的备注
需要设置YES的变量
HAVE_DOT
EXTRACT_ALL
EXTRACT_PRIVATE
EXTRACT_STATIC
CALL_GRAPH
OPTIMIZE_OUTPUT_FOR_C
RECURSIVE
下面参数按需配置:
INPUT:源码位置 | 按需修改
1. 为空时表示源码在当前位置
2. 支持多路径,路径间用空格隔开
GENERATE_HTML:是否生成html。生成html后可以通过web进行访问 | 建议开启
GENERATE_HTMLHELP:是否生成压缩HTML格式文档(.chm) | 按需开启
EXTRACT_LOCAL_CLASSES:是否解析源文件(cpp文件)中定义的类 | 按需开启
SOURCE_BROWSER:如果设定为YES,则Doxygen会产生出源文件的列表,以供查阅。 | 按需开启
执行命令创建:
doxygen Doxyfile
执行完成后,会生成html目录
创建http server:
进入到html目录
python3 -m http.server 8000
8000:是端口,可以按需修改
浏览内容:
浏览器中访问http://$hostip:8000
附件
《Doxygen v1.63 中文手册.pdf》链接: https://pan.baidu.com/s/1ptS6B4ovA911TzSDb3YtlQ?pwd=nwu4 提取码: nwu4
参考
- egypt | Egypt官网
- reveriel/callgraph: Call-graph generator. | 各种callgraph说明,但没有推荐和评分
- GNU cflow | 官方使用手册
本文来自博客园,作者:whilewell,转载请注明原文链接:https://www.cnblogs.com/viiv/p/15736106.html