我應該怎麼快速掌握 C 語言專案架構 === :::info 今天你在因緣際會下需要研究一份很複雜的程式碼,其複雜的程度大概就是一個正常的開源工具,像是 trace libreoffice 實做等等 你需要怎麼做才能幫助你快速掌握這份程式專案呢? ::: ### 1. 你熟悉專案使用的語言,不過對專案整體架構不甚熟悉 #### 1. doxygen `doxygen` 是一款可以根據設定好的註解型式,自動建立程式架構文件的自由軟體,不少比較 hardcore 的自由軟體文件都是要透過它自動生成的(~~舉例當寫該程式的人不想寫文件的時候~~) :::warning 雖然 `doxygen` 功能已經十分強大,不過其並不支援 trace callback 函式的能力,同時也無法 trace 遞迴函式 這點在 trace 不少 multi-thread 程式(像是 QEMU)時需要小心 ::: #### 2. ncc `ncc` 類似 `gcc` 是一種 C 語言編譯器,不過 `ncc` 更注重提供程式的分析資訊 :::warning 非常可惜的是,`ncc` 相關的中文資訊非常的少(~~而且你通常會查到*國家通訊傳播委員會*~~),而且其用途通常會跟 `codeviz` 下面提到的工具一起使用 ::: #### 3. ctag/ cscope `ctag`/ `cscope` 是以強化 vim 編輯器 debug 的能力,提供像是快速查找 function 對應的定義以及其宣告的位置 :::warning 不過微軟家編輯器 visual studio code 為數眾多的擴充套件都可以幫你作到相同的事 ::: #### 4. codeviz `codeviz` 是結合 `gcc` + `graphviz` 下產出程式碼對應的 call graph,可以圖像化顯示 function 間呼叫的關係 :::warning 不過需要注意的是,`codeviz` 官方網站已經沒有在維護該工具,網路上的文件也偏少且相對較舊;其次為 `codeviz` 僅支援特定版本的 `gcc` ,而其特定版本通常(至少在 Ubuntu平台上需要)自行編譯 `gcc`,編譯過程對新手而言並不是很友善 雖然文件上(雖然網路上有許多文件,但是內容重複度極高)有提到 `ncc` 這款編譯器也可以用來代替 `gcc` 輸出 cdepn 格式檔案,不過文件本身有點語焉不詳 ::: #### 5. cflow `cflow` 是一個相當簡單直白的工具,`cflow` 在找到程式進入點之後會開始印出 function call flow ```shell= $ cflow qemu-img.c ``` ![](https://i.imgur.com/knXFpdU.png) 同時如果你在看完前面簡略式的程式呼叫之後,想要知道更加詳細的 function call (像是 linker 連結外部的程式碼實做呼叫) 你可以在指令後面多加 include 的函式庫,像是 ```shell= $ cflow qemu-img.c ./*.c ``` (看情況可以導流到外部檔案去,通常這種開源大專案的 call stack 都會很誇張,一個 `qemu-img` 的 function call stack 可以到 12000 行) :::warning 這個功能非常的陽春,不過也是我試到目前最容易成功的,使用最直接的(其他工具的文件都多少有點不齊全) ::: #### 6. `nm` #### 7. `-finstrument-functions` #### 8. `gprof` `gprof` 是類似 `perf` 的效能評估工具,不過與 `perf` 不同的是,`gprof` 需要在編譯流程上更動以獲得更多直觀的分析 * `gprof` 優點在於 *1. 可以自動產出程式碼呼叫對應的 call graph (文字顯示)* *2. 靜態顯示定義到的 function* *3. 分析程式執行期間效能分佈在各函式的情況* :::info 簡單來說,就是結合 `cflow` 與 `perf` (「列出定義到的函式」這個功能有點像 `nm`) ::: * 缺點也是相當明顯 *1. `gprof` 需要在 gcc 編譯時期額外加上 `-pg` 的編譯指令(如果 compile 與 link 分開執行,則兩個指令都需要加上 `-pg`)* :::info 這代表你可能會需要先理解該專案的 `Makefile`,尤其是當 `Makefile` 是由 `cmake` 等自動產生 `makefile` 工具所產生的程式碼時,又或者~~該專案把 `Makefile` 散落在各個模組裡然後名字又取得很奇怪的時候~~;你可能就要先花一段時間 trace `Makefile` 然後才能開始 trace source code …… ::: *2. `gprof` 實做上與 `-fininstrument-functions` 類似,一旦加上 `-pg` 編譯,其每次函式呼叫時會以 `mcount` 作為呼叫的第一個 operation* :::info 事實上有可能因為加入 `-pg` 而導致 `Makefile` 失敗,舉一個實際例子,如果在編譯 QEMU 的 `Makefile` 中加上 `-pg` 的編譯選項,編譯過程會因為 build 部份映像檔找不到 `mcount` 的定義因此編譯失敗(不過這應該是我還不熟其 `Makefile` 的緣故,我以為只要在 `CC` 跟 `LD` 步驟加上 `-pg` 還是太天真了嗎) ::: --- > `gprof` 要怎麼用 #### 9. `record function-call-history` ### 2. 你對專案架構已經有了初步的了解,需要深入研究實做 ### 使用心得 其實真的願意花時間閱讀這些錯誤訊息再動手更改像是 `Makefile` 執行流程 那麼以上出現的 bug 都不算是太大的問題 不過難題在於,會想用 tracing tool 通常都是因為對該程式碼掌握度不足 不了解程式執行流程加上還要為了「準備 tracing」而花費一堆時間 保持著沒有產出沒有進度的狀態實在令人焦慮 ![](https://i.imgur.com/7WRKeJ0.png) ### 參考資料