概述
作為一名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 | 官方使用手冊