[ZZ] 代碼分析方法

 

        LZ最近正在撓破頭的啃ChromiumOS,因爲之前沒有過分析開源系統的經驗,所以一籌莫展。

而這篇文章的出現,正好給了LZ以思路。非常感謝原作者!

        ref from:: http://hi.baidu.com/lyb1900/blog/item/06b3d31e520b03e71bd576f1.html

--------------------------------------------------------------------------------------------------------

        最近一段時間時間一直在做新產品的預研和代碼分析方面的工作。整個過程是及其富有挑戰性和探索性的,回顧這段時間的工作和學習,對代碼分析方法有一些總結和心得,隧記錄下來以備遺忘。這篇文檔並非最終文檔,在之後的工作和學習過程中會隨時補充。

        開源時代的來臨給程序員們帶來了豐盛的大餐,網絡上存在着大量的優秀產品和代碼可供我們品味。但是真正當你要公關一個具體的開源軟件時,會面臨代碼量大、文檔少、沒有技術支持等多方面的問題。如何能更有效、更加快速的瞭解一個產品的特性、實現細節?我想這是每個程序員都在關心和思考的問題。這裏記錄的僅僅是我在研究和學習代碼過程中的體會和心得,如果需要了解更多的有關代碼閱讀方法方面的朋友,請閱讀Diomidis Spinellis的Code Reading。

 

 

 

一、收集信息

 

        代碼閱讀的第一步是收集可以收集的所有信息,這些信息包括如下內容:

 

項目用戶文檔

項目設計文檔

項目FAQ

項目測試用列

         這些信息可以從項目的主頁、Wiki、Google、郵件列表、論壇,以及相關的論文和書籍中獲得,並且將收集的相關信息統一管理起來。這裏我推薦三個不錯的知識管理軟件:myBase、Moin和Google Notebook:

 

         myBase: 這個工具可以用於收集任何數字信息,包括網頁、文件、多媒體信息等,並且可以方便的按照樹形結構將其分門別類

 

         Moin:這是一個Wiki工具,主頁提供一個desktop版本,可以方便的copy到U盤攜帶,它最大的特點是每篇文章都是單獨存放的可以方便的使用版本管理工具管理起來

 

        Google Notebook:可以方便的爲每個知識點打Tag,方便檢索。貌似現在Notebook已經停止開發了,團隊重心放到Google Document了。

 

二、制定分析策略

 

        由於開源軟件的代碼量往往是驚人的,很多時候又缺少相關的設計文檔和資料,並且對於個人來說精力也是有限的。因此,在代碼分析之前制定一套分析策略是比較重要的。分析代碼之前首先要明確我們分析的最終目的是什麼?

 

        如果我們分析代碼的主要目的是瞭解該產品的特性,則我們的分析目標應該放在如下幾個方面:

 

產品適用的領域有那些?

相關的產品和實現有那些?

產品的優點和缺點有那些?

        通過對產品的橫向和縱向比較,我們大致的能定位產品,以及確定其是否可以應用到現有的產品框架中。

 

        如果我們分析代碼的主要目的是學習新的框架,則重點應當放在如下幾個方面:

 

產品採用了什麼核心技術?

產品的邏輯框架是什麼?

產品框架分成了那些模塊?模塊之間的關係是什麼?

        如果我們分析代碼的主要目的是研究某個模塊或者功能的具體實現細節,則應當將研究目標放在下面幾個方面:

 

實現採用了什麼算法?算法的思想和原理是什麼?

實現採用了什麼數據結構描述?

實現過程中使用了什麼實現技巧?

        不管分析的目的是以上三個方面的哪個方面,在分析過程中都應將重點放在擬定的分析目標的基礎上,不斷的向自己提出新問題。自己在分析具體代碼時,曾由於目標不夠明確最終迷失到龐大的代碼中了。結果不但花費了很多時間,最終的結果也並非預期想要的。

 

 

 

三、源碼分析

 

         再收集和閱讀相關資料,並且確定了分析策略後即可開始研讀代碼。代碼的分析主要分成幾個個部分:構建可運行環境、構建測試環境、源碼的靜態分析和源碼的動態分析。靜態分析過程重點在於把我整個代碼的邏輯結構和關係,而動態代碼分析主要是關注的數據流和實現方法。

 

 

 

3.1 構建源碼的可運行環境

 

         當源碼從網絡上down下來後,首先需要確保手頭的代碼是確實可執行的,因此首先應當構建一個完整的可執行系統。這樣,今後由於修改造成的編譯或者運行時錯誤,則可以確定是由於我們自己的修改導致的,而非代碼本身的問題。構建可執行系統還有一個好處是通過閱讀項目構建代碼(Makefile、Scons或者Shell),可以了了解產品的大體框架結構,並且可以瞭解系統目前支持那些特性、依賴那些庫和文件。一般來說,開源軟件都會提供README、 INSTALL文件或者在其主頁上提供構建的需求文件以及構建方法。參照文檔,我們可以構建出至少debug和release兩個不同的版本。但是文檔中往往只會提及一些基本的構建指令、編譯規則中有可能隱含了很多產品特性,這些特性可以通過閱讀Makefile文件或者使用config --help來獲得。通過不同的編譯特性組合我們可以構建出不同特性的測試版本。

 

        文檔化構建過程是一個很好的習慣,這樣不但可以以防遺忘,並且可以爲之後的分析提供一定的信息。這裏我通常使用Ooffice中的Excel工具將編譯選項和其構建的文件關係用表的形式記錄下來。

 

        在構建成功後,應當使用一個比較熟悉的版本管理工具(SVN,GIT,CVS)將代碼管理起來,因爲之後我們的每次修改,都可能引發代碼編譯和運行時的問題,通過版本管理工具可以很快速的回滾。

 

3.2 構建測試環境

 

        一般開源軟件都會提供測試方法和代碼,這些信息可以從項目主頁或者代碼樹中獲得。獲得測試代碼和方法後應當將這些測試代碼在以構建的產品中運行一次,這樣可以瞭解產品目前實現的狀態以及發現一些缺陷。當然,並不是所有產品都會提供有效的測試代碼,這種情況下只能自己動手寫一些簡單的測試用列,讓產品RUN 起來。最後,應當將這個測試過程應當記錄下來,並且編寫成測試腳本以供之後代碼動態分析和代碼修改時候使用。

 

 

 

3.3 代碼的靜態分析

 

      至此,我們應當大體瞭解了產品的邏輯框架結構,並且有了一定的用戶體驗。知道產品完成了那些功能,尚未支持哪些功能,有哪些特點。接着,我們應當更加深入的從代碼的靜態結構上深入瞭解產品的結構。

 

3.3.1 代碼統計

 

      這裏主要做一些代碼閱讀工作量方面的統計,主要了解如下幾個方面的內容:

 

有多少代碼文件?

採用一種語言還是多種語言?語言的分佈情況?

代碼行數有多少?

3.3.2 構建模塊關係

 

       一般來說,開源軟件的功能模塊都是按照目錄層次結構劃分的,並且目錄名稱或者文件名稱都會很明確的表示該模塊或者文件的功能是什麼。因此通過了解文件和目錄的組織關係可以很直觀的瞭解代碼的組成結構。不過也有特列,如果文件名稱或者目錄名稱不能很明確的標明功能,則需要通過分析代碼中包和類關係來確定組成關係了。

 

       在工作中,我專門爲模塊分析工作編寫了一些腳本,通過這些腳本可以很方便的將目錄、文件之間的關係以圖表的方式直觀的表現出來。

 

3.3.3 構建UML圖

 

       如果代碼過於複雜和龐大,應當考慮用一些逆向工程工具從源碼動態生成UML圖(rose、jude),這樣可以更加直觀的反應代碼的組成方式。這個過程重點應當放在弄清包之間的關係,以及對象之間的關係。

 

3.3.4 構建源碼閱讀環境

 

      閱讀代碼之前需要使用一些代碼閱讀工具將代碼整理和建立索引,這樣可以更方便定位代碼。這裏我比較推薦Global、Doxygen、Source Navigator。這三個工具應該是Linux下用過的比較好的源碼索引工具了。

 

     Global是GNU開發的開源軟件,可以構建成以HTML爲基礎的源碼索引頁面。通過和apache的配合,可以很方便地查找、定位函數和類。

 

     Source Navigator是目前在Linux下最好的圖形界面的源碼瀏覽工具,之前由RED HAT開發和維護,但是中間停滯了5年,最近由德國一個組織接手繼續開發。應該說這是Linux下唯一一個能和Source Insight比高下的產品了。

 

     Doxygen,該軟件不但可以建立代碼索引而且可以從代碼的註釋中獲得信息,生成幫助信息,並且可以繪製函數掉用關係圖和類關係圖。

 

 

 

3.3.5 瞭解關鍵的數據結構和算法

 

     這個活動主要是以閱讀代碼爲主,通過閱讀代碼的頭文件可以對關鍵的數據結構和涉及的算法有一個初步的認識。瞭解這些數據結構的大致功能,數據結構之間的關係。最後,可以通過對數據結構代碼的瞭解繪製出數據結構的大體的關係圖。

 

 

 

3.3.6 產出物

 

     通過以上的分析活動,可以得到如下的一些產出物。

 

     1)瞭解代碼閱讀的工作量

 

     2)瞭解代碼的組成結構(模塊關係、包關係、類關係)

 

     3)瞭解核心的數據結構

 

     4)繪製出代碼數據統計表、模塊和包關係圖、類關係圖、數據結構邏輯關係圖

 

 

 

4. 源碼的動態分析

 

     源碼的動態分析過程,主要的目的在於瞭解系統運行過程中,關鍵數據結構的操作,關鍵功能的函數調用關係,數據的組織和流向等方面。這個分析過程分成幾個部分:運行時環境分析、函數調用分析、運行時數據分析。

 

4.1 運行時環境分析

 

     這個步驟需要了解下面幾個方面的問題:

 

運行時需要的環境變量是什麼?

參數是什麼?

運行時以來的庫有那些?

運行時訪問的資源有那些(文件,網絡,內存分配等)?

      這個過程可以通過讀取/proc/xxx下面相關的文件、使用ldd、objdump等工具獲得。

 

4.2 運行時函數調用分析

 

       之前,大建的測試環境在這裏就有用途了,通過運行不同的測試用列,並且使用gdb、callgrind、gprof、codeviz等工具可以很方便的獲取函數的調用序列。將其捕獲的數據經過腳本處理,去除一些不必要的噪聲信息,可以繪製出一個非常直觀的函數運行時調用序列圖。這個過程對我們理解特定的產品特性的實現過程是相當有幫助的。

 

 

 

4.3 運行時數據分析

 

       運行時數據分析過程,重點主要在關鍵數據結構的創建、操作和銷燬過程,通過gdb或者logger工具,將不同執行過程中的數據內容以可讀的方式輸出出來。通過測試用列的配合,可以最終繪製出關鍵數據結構的運行時關係圖。這裏並沒有一套比較統一的方法可以完成數據採集工具,我使用的比較多的方式是 gdb,通過gdb以及自定義的宏,可以動態的抓取和輸出指定數據結構內容。當然這個方法對那些時間敏感的調用過程並不有效,因此這種情況下多半採用 logger的方式將數據輸出。在很多開源軟件中,開發者往往爲代碼的測試提供了一些宏或者數據遍歷方法,可以使用這些功能接口,很便捷的輸出需要的信息。

 

 

 

4.5 產出物

 

      這個工程活動的最後大致有幾個方面的產出物:

 

      a)不同用列情況下的函數調用信息

 

      b)數據結構關係圖

 

      c)數據流圖

 

      d)算法實現的原理文檔

 

 

 

5. 添加和修改功能

 

     瞭解一個系統的最終目的還是爲了使用,因此爲了檢驗對軟件的理解是正確的,我們應當在現有的基礎上去修改代碼以驗證我們的理解是沒有問題的。但是整個過程需要建立在良好的測試環境基礎上。這個活動的主要步驟有:

 

      a)瞭解現有代碼的實現規則(按照代碼的統一規則去增添和修改代碼)

 

      b)確定修改或者添加功能的目標(實現或者修改什麼功能,達到的效果是什麼樣的?)

 

      c)實現代碼

 

      d)編寫測試代碼驗證實現過程是正確的

 

四、總結

 

      通過以上的4個工程過程,我們可以對一個產品有一個較爲客觀的認識,並且最後應當以文檔的方式表達出來。如果爲軟件增添了新功能,還需要爲新增功能增加接口文檔和設計文檔。

 

下圖是總結後的思維導圖:

 

發佈了27 篇原創文章 · 獲贊 25 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章