linux原始碼分析工具

lm_y發表於2017-09-10

linux原始碼分析和閱讀工具比較



Windows下的原始碼閱讀工具Souce Insight 憑 藉著其易用性和多種程式語言的支援,無疑是這個領域的“帶頭大哥”。Linux/UNIX環境下呢?似乎仍然是處於百花齊放,各有千秋的春秋戰國時代,實 際上,似乎其環境下的任何軟體都是處於一種逐鹿中原的態勢,也許這就是“集市”之於“大教堂”的優勢所在吧!主要的交叉索引工具有:ctags 、 cscope 、 global 、 lxr 、 KScope 、 sourcenav 、 calltree 、 CodeViz 、ncc 、gprof 等。 下面將就各軟體的優劣作一簡單介紹。這裡面當中,我當前分析過他們的原始碼的有lxr、sourcenav、calltree、ncc。現在正在 sourcenav的基礎上進行我需要的改造以更合適分析核心需要。KScope需要進一部考察和分析,如果KScope的程式碼分析能力(也就是 scope的能力)跟得上sourcenav可能到時會放棄sourcenav轉到KScope開發,因為sourcenav的介面和互動性都不怎麼好。

1. ctags


     ctags應該是幾個裡面最古老的一個了,當然也是獲得支援最多的一個,vim預設就支援ctags產生的tag資料庫,不過原來的ctags名符其實, 只支援C語言,且分析不夠細膩。其新版本名為exuberant ctags,但是它已經遠遠超出了它名字的範疇,支援包括C語言在內的33種程式語言,準確度上也不錯,並且它的開發仍在如火如荼地進行著,前途無量啊!
配合vim使用,基本上能滿足使用者的要求。
常用命令:
建立資料庫命令:

exuberant-ctags

在vim編輯器中瀏覽:

 

:ta x
跳轉到符號x的定義處,如果有多個符號,直接跳轉到第一處
:ts x
列出符號x的定義
:tj x
可以看做上面兩個命令的合併,如果只找到一個符號定義,那麼直接跳轉到符號定義處,如果有多個,則讓使用者自行選擇。
Ctrl+]
跳轉到當前游標下符號的定義處,和ta類似。
Ctrl+t
跳轉到上一個符號定義處,和上面的配合基本上就能自由跳轉了。
另外兩個:tn, :tp是在符號的多個定義之間跳轉

美中不足,只能查詢符號的定義,不能實現交叉引用查詢等其它功能。

2. Cscope

 

     相比ctags的只能查詢符號的定義,cscope則顯得更加豐富,雖然他支援C語言,但是它除了能查詢函式的定義,還能查詢函式在哪裡被呼叫過等,所以能在一定程度上彌補ctags的不足。
常用命令:
建立資料庫的命令:

cscope和vim配合使用:

不再重複vim文件了,自己通過命令
:help cscope
去詳細學習吧。

比較遺憾的是,cscope的最後一次更新是在2003年,這個專案似乎也是被人拋棄了,希望以後能被人收養,或者和其它專案合併。

3. Global


     Global是GNU出品的交叉索引工具,支援C, C++, Yacc, Java 和PHP4五種語言,命令列下表現不錯,但是和vim配合使用,就不如前面兩個更加親切了。其特色在於分析得很細膩,我所研究過的,暫時只能 soucenav能超過它。能生成交叉索引的web頁,很適合用來做程式的文件。資料庫是從berkeley BD 3上改過來的。但和我的要需求卻有比較大的差距。因為它只有一個WEB的前端,在功能上,他現有的前端也只實現符號查詢和交叉引用查詢。

4. LXR

 

     Linux超文字交叉程式碼檢索工具LXR(Linux Cross Reference),是由挪威奧斯陸大學數學系Arne Georg Gleditsch和Per Kristian Gjermshus編寫的。這個工具實際上執行在Linux或者Unix平臺下,通過對原始碼中的所有符號建立索引,從而可以方便的檢索任何一個符號,包 括函式、外部變數、檔名、巨集定義等等。不僅僅是針對Linux原始碼,對於C語言的其他大型的專案,都可以建立其lxr站點,以提供開發者查詢程式碼,以 及後繼開發者學習程式碼。   
     目前的lxr是專門為Linux下面的Apache伺服器設計的,通過執行perl指令碼,檢索原始碼索引檔案,將資料傳送到網路客戶端的Web瀏覽器上。 任何一種平臺上的Web瀏覽器都可以訪問,這就方便了習慣在Windows平臺下工作的使用者。 關於lxr的英文網站為http://lxr.linux.no/ ,在中國Linux論壇http://www.linuxforum.net 上有其鏡象。
     好的說完了。說說缺點,可能我分析得不深入,它的程式碼不是用C寫的,是網頁尾本,程式也很短,沒有看到有應用資料庫。很多的東西都是你瀏覽的時候才生成的,所以拿過來用或改造的潛力有限,而且程式碼分析的不夠Global細膩。

5. KScope

 

     KScope是cscope的圖形前端,他為那些不熟悉vim或者是不習慣終端作業的人提供了圖形介面的選擇,介面是這一款軟體的一個亮點,比 source insight還好看,從windows轉過來做Linux開發的人可能感覺比較親切。這也是我最近動心的原因。另外,它還有個比較迷人的特點:能生成函 數呼叫樹。但說百了,其實是呼叫graphviz畫出來的。

6. sourcenav

     snavigator是一個不錯的原始碼編輯和專案管理軟體。有Windows和Linux版本。網址http://sourceforge.net/projects/sourcenav 。
     基本介紹:Source-Navigator is a source code analysis tool. With it, you can edit your source code, display relationships between classes and functions and members, and display call trees.
     這種工具號稱是source insight是linux對手,和確,經過我這一輪的GOOGLE後,從分析效能上,只有他和source insight的能平行的。但可操作性方面就遠遠沒有source insight強了,很多功能用起來都沒有source insight順心。介面就比kScope差很遠了,這也就是我對KScope心動的原因。和source insight比,還有一點不行的就是,需然生成交叉引用是在後臺進行了,但它是一次性連續生成的,而source insight生成是,則更為優先生成客戶當前的程式碼,source insight這一點對客戶的考慮真不錯。資料庫方面採用的也是berkeley BD 4,他和Global一樣,在資料庫的使用方面,第個表只採用了兩個字端,KEY和DATA,也就是說沒采用Secondary Databases,這導致很多時候都是把全部的記錄返回,然後再優程式來篩選,這種方式把資料庫的優點全給埋沒了,要知道對linux這類原碼,交叉引 用資料庫可能達幾百M,這麼幹記憶體也不夠,速度也更不用談。我還沒有研究KScope不知它在這方面怎麼樣,我覺得sourcenav這方面肯定要改掉。 把資料篩選的工作完全由資料庫來做,他們會做得很好。
     可開發性方面,sourcenav做得還算不錯,有使用者文件和開發文件,程式碼結構也比較可能,TCL程式設計也直對比較容易。可以在它原來功能的基礎上進行功能擴充。接下來的幾編BLOG,我會對它的原始碼進行分析。   

7. 其它原始碼分析工具

     除了上述的體系比較完整的工具外,還有很多源源分析的小工具。這些小工具多數是用來繪製函式呼叫關係圖的。

     繪製函式呼叫關係圖對理解大型程式大有幫助。我想大家都有過一邊讀原始碼(並在頭腦中維護一個呼叫棧),一邊在紙上畫函式呼叫關係,然後整理成圖的經歷。如 果運氣好一點,藉助偵錯程式的單步跟蹤功能和call stack視窗,能節約一些腦力。不過如果要分析的是指令碼語言的程式碼,那多半隻好老老實實用第一種方法了。如果在讀程式碼之前,手邊就有一份呼叫圖,豈不妙 哉?下面舉出我知道的幾種免費的分析C/C++函式呼叫關係的工具。

    函式呼叫關係圖(call graph)是圖(graph),而且是有向圖,多半還是無環圖(無圈圖)——如果程式碼中沒有直接或間接的遞迴的話。Graphviz 是專門繪製有向圖和無向圖的工具,所以很多call graph分析工具都以它為後端(back end)。那麼前端呢?就看各家各顯神通了。

     呼叫圖的分析分析大致可分為“靜態”和“動態”兩種,所謂靜態分析是指在不執行待分析的程式的前提下進行分析,那麼動態分析自然就是記錄程式實際執行時的函式呼叫情況了。

    靜態分析又有兩種方法,一是分析原始碼,二是分析編譯後的目標檔案。

分析原始碼獲得的呼叫圖的質量取決於分析工具對程式語言的理解程度,比如能不能找出正確的C++過載函式。Doxygen 是原始碼文件化工具,也能繪製呼叫圖,它似乎是自己分析原始碼獲得函式呼叫關係的。GNU cflow也是類似的工具,不過它似乎偏重分析流程圖(flowchart)。

     對程式語言的理解程度最好的當然是編譯器了,所以有人想出給編譯器打補丁,讓它在編譯時順便記錄函式呼叫關係。CodeViz (其靈感來自Martin Devera (Devik) 的工具 )就屬於此類,它(1.0.9版)給GCC 3.4.1打了個補丁。另外一個工具egypt 的思路更巧妙,不用大動干戈地給編譯器打補丁,而是讓編譯器自己dump出呼叫關係,然後分析分析,交給Graphviz去繪圖。不過也有人另起爐灶,自己寫個C語言編譯器(ncc ),專門分析呼叫圖,勇氣可嘉。不如要是對C++語言也這麼幹,成本不免太高了。分析C++的呼叫圖,還是藉助編譯器比較實在。

     分析目標檔案聽起來挺高深,其實不然,反彙編的工作交給binutils的objdump去做,只要分析一下反彙編出來的文字檔案就行了。下面是Cygwin下objdump -d a.exe的部分結果:

00401050 <_main>:
401050:       55                      push   %ebp
401051:       89 e5                   mov    %esp,%ebp
401053:       83 ec 18                sub    $0x18,%esp
   ......
40107a:       c7 44 24 04 00 20 40    movl   $0x402000,0x4(%esp)
401081:    &n

401082:       c7 04 24 02 20 40 00    movl   $0x402002,(%esp)
401089:       e8 f2 00 00 00          call   401180 <_fopen>

     從中可以看出,main()呼叫了fopen()。CodeViz帶有分析目標檔案的功能。

     動態分析是在程式執行時記錄函式的呼叫,然後整理成呼叫圖。與靜態分析相比,它能獲得更多的資訊,比如函式呼叫的先後順序和次數;不過也有一定的缺點,比如程式中語句的某些分支可能沒有執行到,這些分支中呼叫的函式自然就沒有記錄下來。

     動態分析也有兩種方法,一是藉助gprof的call graph功能(引數-q),二是利用GCC的 -finstrument-functions 引數。

gprof生成的輸出如下:

index % time    self children    called     name
                0.00    0.00       4/4           foo [4]
[3]      0.0    0.00    0.00       4         bar [3]
-----------------------------------------------
                0.00    0.00       1/2           init [5]
                0.00    0.00       1/2           main [45]
[4]      0.0    0.00    0.00       2         foo [4]
                0.00    0.00       4/4           bar [3]
-----------------------------------------------
                0.00    0.00       1/1           main [45]
[5]      0.0    0.00    0.00       1         init [5]
                0.00    0.00       1/2           foo [4]
-----------------------------------------------

     從中可以看出,bar()被foo()呼叫了4次,foo()被init()和main()各呼叫了一次,init()被main()呼叫了一次。用 Perl指令碼分析gprof的輸出,生成Graphviz的dot輸入,就能繪製call graph了。這樣的指令碼不止一個人寫過:http://www.graphviz.org/Resources.php ,http://www.ioplex.com/~miallen/ 。

GCC的-finstrument-functions 引數的作用是在程式中加入hook,讓它在每次進入和退出函式的時候分別呼叫下面這兩個函式:

void __cyg_profile_func_enter( void *func_address, void *call_site )
                                __attribute__ ((no_instrument_function));
void __cyg_profile_func_exit ( void *func_address, void *call_site )
                                __attribute__ ((no_instrument_function));

     當然,這兩個函式本身不能被鉤住(使用no_instrument_function這個__attribute__),不然就反反覆覆萬世不竭了:) 這裡獲得的是函式地址,需要用binutils中的addr2line這個小工具轉換為函式名,如果是C++函式,還要用c++filt進行name demangle。具體方法在《用Graphviz 視覺化函式呼叫 》中有詳細介紹,這裡不再贅述。

     從適應能力上看,原始碼分析法是最強的,即便原始碼中有語法錯,標頭檔案不全也沒關係,它照樣能分析個八九不離十。而基於編譯器的分析法對原始碼的要求要高一些, 至 少能編譯通過(gcc 引數 -c)——能產生object file,不一定要連結得到可執行檔案。這至少要求原始碼沒有語法錯,其中呼叫的函式不一定有定義(definition),但要有宣告 (declaration),也就是說標頭檔案要齊全。當然,真的不全也沒關係,自己放幾個函式宣告在前面就能糊弄編譯器:) 至於動態分析,要求最高——程式需得執行起來。如果你要分析的是作業系統中某一部分,比如記憶體管理或網路協議棧,那麼這裡提到的兩種動態分析法恐怕都不適 用了。

8. 網上的其它資源

[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 
[9] 各類分析函式呼叫關係圖的工具 
http://oss.lzu.edu.cn/blog/blog.php?do_showone/tid_1636.html

相關文章