今天忽然有個網友加我QQ,說他正在用gprof分析一個項目的源代碼,想打印出該項目的函數調用關係圖,不料它參考的資料[1]中用於打印函數調用關係 圖的Mkgraph腳本已經沒法下載了,因此想問我要一份。可我當初看到這篇資料時也由於也沒下到,因此給它推薦了另一個一樣可以產生函數調用關係圖的 工具——calltree。不過剛纔又忽然想起,其實還有另一個工具能夠代替Mkgraph的,那就是kprof。爲了方便你們往後分析源代碼,這裏一 並再把幾個相關工具介紹一下。html
先列出我當前用的這幾個工具的版本和它們的下載地址: calltree 2.3 下載地點 http://linux.softpedia.com/progDownload/calltree-Download-971.html 或者 http://mirror.lzu.edu.cn/software/calltree/calltree-2.3.tar.bz2
gprof 2.18.0.20080103 在ubuntu/debian下直接安裝便可 http://citeseer.ist.psu.edu/graham82gprof.htmllinux
kprof 1.4.3 (在ubuntu/debian下直接用apt-get安裝) http://kprof.sourceforge.net/程序員
graphviz (在ubuntu/debian下直接用apt-get安裝便可,須要它的一個dot工具) http://www.graphviz.org/ubuntu
introductionvim
對於一個C語言編寫的項目,它的框架能夠反應爲一棵函數調用樹。若是在分析項目以前,可以獲得這樣一顆調用樹,那麼就能夠了解項目的總體框架;若是在項目 運行以後,可以跟蹤到該次運行過程當中的函數調用,那麼將有利於分析某些測試條件下項目的執行流程;而若是在項目運行過程當中(好比調試項目時)可以跟蹤出某 個位置以前的函數調用,那麼將有利於肯定潛在bug可能存在的位置。框架
對於這三種狀況,雖然沒有任何一個工具可以徹底知足,不過"聰明"和"樂於奉獻"的程序員們仍是分別貢獻了不一樣的工具:編輯器
無須運行項目自己,calltree就可以根據整個項目的源代碼產生一棵函數調用樹,並可把該調用樹導出爲dot格式的圖形。所以能夠說calltree可以在不運行項目的條件下對項目進行函數級別的分析。svg
gprof則可以在項目運行以後,把該次運行過程當中的函數調用以文本的形式反應出來,不過善於思考的人們老是喜歡更美好的生活,因而kprof產生了,它 不只能夠輔助gprof更好的分析程序代碼級別的運行狀況,並且可以導出當前執行過程當中的函數調用樹,並一樣能夠把調用樹導出爲dot格式的圖形。函數
gdb(Gnu DeBugger),這個應該很熟悉吧,它是一個調試工具。它提供專門的backtrace命令來跟蹤程序執行到某個位置(好比指定的斷點處)以前的函數調用。不過這個目前仍是文本輸出的,感興趣的能夠hack一下gdb,給它加上漂亮的輸出。工具
上面提到了DOT格式的圖形。這個DOT[2]是什麼呢?是graphviz[3]定義的一種圖形描述語言,它能夠經過graphviz提供的dot工具 (安裝graphviz以後就有了)把用DOT描述的圖形轉化爲各類其餘格式的圖形。雖然有一些專門的DOT圖形瀏覽工具,如dotty,不過這個東西不 怎麼好用,因此仍是建議經過dot工具轉換爲比較常見的圖形格式,如svg,jpg,gif,png,ps,它還能夠轉換成dia格式,進而能夠經過「超 級牛力」的dia繪圖工具來進行進一步的編輯。
demo
下面來介紹這幾個工具的具體用法,更多細節請參考它們本身的文檔。 爲了方便演示,這裏寫一個很是「糟糕的」可是卻對此次演示頗有用的代碼。
Code: /**
void a(void), b(void), c(void), d(void), e(void);
void a(void) { b (); } void b(void) { c (); } void c(void) { d (); e (); } void d(void) { ; } void e(void) { ; }
int main (int argc, char **argv) { if (argc < 2) { a (); } else { b (); } }
[Ctrl+A Select All]
對這個代碼而言,很是容易看出其調用關係,即:
當main的參數個數少於2個時,調用關係爲 a -> b -> c -> d,e
當main的參數個數大於3個時,則調用關係爲b -> c -> d,e 不過,若是一個項目包含幾十個文件或者幾千行甚至上萬行代碼,這個調用關係恐怕就沒這麼容易看出來了,因此還得藉助後面的工具。
2.1 不用運行程序就能夠打印整個項目的函數調用關係圖: calltree
下載calltree後本身先編譯安裝好,放到/usr/bin下面。而後經過"calltree -help"查看該工具的幫助,這裏經過使用-mb參數打印以main爲樹根的函數調用關係圖。
$ calltree -mb test.c main: | a | | b | | | c | | | | d | | | | e | b | | c | | | d | | | e
從這個結果能夠很是方便的看出函數調用關係,不過仍是不夠美觀哦,因此加上-dot參數,產生一個dot圖形吧。
$ calltree -mb test.c -dot > test.dot
okay,如今獲得了一個關係調用圖,即test.dot,由於這個格式不太經常使用,咱們給它轉換成jpg,見附圖calltree.jpg。
$ dot -Tjpg test.dot -o calltree.jpg
不過貌似函數d和e沒有打印出來,因此這個應該說是值得改進一下。還好我以前專門寫了一個腳本,能夠產生完整的輸出,這個腳本見附件 tree2dot.sh.tar.gz,具體原理見資料[5],附圖calltree1.jpg是這個腳本產生的。這裏簡單介紹它的用法:
先經過腳本tree2dot.sh獲得一個DOT圖形 $ calltree -mb test.c | ./tree2dot.sh > test.dot 而後用dot轉換爲jpg格式 $ dot -Tjpg test.dot -o calltree1.jpg
須要補充一下的是,calltree自己支持過濾掉某些字符,包括想列出的(經過listfile或者list)以及想忽略的(igorefile),例如若是僅僅想列出c函數的調用關係,則能夠:
$ calltree list=c -b -np test.c c: | d | e
若是僅僅想導出這個調用圖,經過加上-dot參數則沒有做用,它仍是會打印出全部的函數調用關係,因此這個時候tree2dot.sh又起做用:
$ calltree list=c -b -np test.c | ./tree2dot.sh
2.2 打印項目當次運行過程當中的函數調用關係圖: gprof & kprof
首先經過gcc加上-pg參數編譯程序(若是編譯和連接分開,都須要加上該參數)。這個參數就是爲了產生一些用於gprof&kprof的信息。 gprof只有字符界面,而kprof提供了圖形界面,下面僅介紹kprof,由於它和gprof相比,能夠產生圖形化的函數調用關係。
$ gcc -pg -o test test.c
編譯完之後,運行一下就能夠產生一個名爲gmon.out的文件,它記錄了該項目當次運行過程當中的相關信息,包括函數調用關係。
$ ./test $ls gmon.out gmon.out
這樣咱們就能夠用kprof來獲得這個項目在此次運行過程當中的函數調用關係圖了( 實際上指定./test是爲了告訴kprof,gmon.out和test在同一個目錄下,kprof會去找gmon.out)。
$ kprof -f ./test
啓動kprof之後找到Graph View標籤,能夠看到一個函數調用關係圖。若是要把這個圖導出來,找到Tools菜單,點擊Generate Call Graph就能夠導出一個DOT圖形,咱們命名爲kprof_noargument.dot,而後咱們就能夠相似2.1用dot工具把它轉換爲其餘格式, 獲得的效果圖如kprof_noargument.jpg。
$ dot -Tjpg kprof_noargument.dot -o kprof_noargument.jpg
在上面,咱們直接鍵入了./test執行它,若是給它傳遞上兩個參數呢,這個時候argc等於二,在main中就不會再調用a函數,而是調用c函數,這樣的話,函數調用關係圖就不同了,此次獲得的結果圖如kprof_twoargument.jpg。
帶上兩個參數運行test $ ./test 1 2 經過kprof來查看調用關係圖,並導出一個名爲kprof_twoargument.dot的圖形 $ kprof -f ./test 把DOT圖形轉換爲jpg格式 $ dot -Tjpg kprof_twoargument.dot -o kprof_twoargument.jpg
這裏沒有提到gprof,由於它只產生一些不太好看的文本調用關係圖,因此沒有演示,不過它仍是有很大做用的,具體參考一下資料[4]吧。 結合2.1和2.2,咱們能夠發現calltree和kprof二者都可以獲得項目的函數調用關係圖,不過前者可以獲得整個項目的函數調用關係,然後者則 可以獲得某次運行過程當中的函數調用關係,各有不一樣做用。經過前者咱們能夠了解整個項目的框架;而經過後者,咱們能夠找出一個項目在某些測試條件下的執行路 徑,從而更好地輔助源代碼的分析。 有時候,這兩種結果仍是沒法知足咱們的要求,好比在調試過程當中,咱們設置了一個斷點,並想了解一下這以前執行過哪些函數,進而找出潛在的bug可能出現的位置。
2.3 項目調試過程當中打印某個位置(如斷點)以前的函數調用關係圖:gdb & backtrace command
爲了可以用gdb調試程序,編譯時請使用-g選項。
$ gcc -g -o test test.c
經過gdb的backtrace命令打印程序執行到某個位置以前的函數調用信息。
$ gdb ./test ... (gdb) set args 1 2 //這裏設置爲兩個參數,因此選擇了路徑b->c->d,e (gdb) l 6 7 void a(void) { b (); } 8 void b(void) { c (); } 9 void c(void) { d (); e (); } 10 void d(void) { ; } 11 void e(void) { ; } 12 13 int main (int argc, char **argv) 14 { 15 if (argc < 2) { (gdb) break c Breakpoint 1 at 0x804833c: file test.c, line 9. (gdb) r Starting program: /home/falcon/Programming/test 1 2
Breakpoint 1, c () at test.c:9 9 void c(void) { d (); e (); } (gdb) backtrace #0 c () at test.c:9 #1 0x08048334 in b () at test.c:8 #2 0x08048380 in main (argc=3, argv=0xbf924e24) at test.c:18
在上面的調試過程當中,咱們首先經過set命令設置了兩個參數,選擇了main函數的第二個分支,並在c函數的入口設置了一個斷點,而後運行程序直到該斷點 處,以後經過backtrace命令打印出以前的函數調用信息。經過最後幾行,咱們看到c最後被調用,以前是b,再以前是main。 到這裏,開頭提到的三種狀況都介紹完了。不過呢,除了上面這些跟函數關係緊密的工具外,還有一個叫cscope[6]的工具,結合它和vim編輯器,在我 們閱讀源代碼的過程當中,能夠利用它提供的":cs find d function"命令打印出函數function調用的全部函數,從而幫助咱們瞭解某個函數內部的函數調用關係。固然該工具還有更豐富的用法,具體參考 資料[6]。 除了這些分析應用程序的工具外,還有一個叫KFT[7]的工具能夠用來分析linux內核。做爲linux內核的一個補丁,它可以跟蹤內核中某個系統調用 的函數調用關係圖,經過KFT提供的一個kd工具,能夠獲得一個文本格式的函數調用關係圖,結合我上面用到的tree2dot.sh(建議用資料[5]中 的tree2dot.sh),能夠獲得一個圖形輸出。 更多相關資料見後面。有任何建議和疑問,歡迎回帖交流,也能夠直接給我發郵件。
補充:
帶-pg編譯程序 $ gcc -pg -o test test.c 先運行程序 $ ./test 讓mkgraph.sh能夠運行 $ chmod +x mkgraph.sh 經過gprof產生函數調用圖並導出爲dot圖形 $ gprof ./test gmon.out -bq | ./mkgraph.sh > mkgraph.dot 把DOT圖轉換爲jpg格式 $ dot -Tjpg mkgraph.dot -o mkgraph.jpg
圖的結果見附圖mkgraph.jpg,這個尚未進行足夠的測試,若是發現問題,歡迎回復。
參考資料
[1] 使用Gnu gprof進行Linux平臺下的程序分析 [2] The DOT Language http://www.graphviz.org/doc/info/lang.html [3] Graphviz - Graph Visualization Software http://www.graphviz.org/ [4] Coverage Measurement and Profiling http://www.linuxjournal.com/article/6758 [5] 用Graphviz進行可視化操做──繪製函數調用關係圖 [6] cscope http://cscope.sourceforge.net/ [7] KFT(Kernel Function Tracing) http://elinux.org/Kernel_Function_Trace ftp://dslab.lzu.edu.cn/pub/kft [8] Call Graph -- Gprof http://sourceware.org/binutils/docs-2.17/gprof/Call-Graph.html#Call-Graph