1. 程式人生 > 實用技巧 >用callgraph生成的函式呼叫關係圖【轉】

用callgraph生成的函式呼叫關係圖【轉】

轉自:https://www.cnblogs.com/findumars/p/7475911.html

Wu Zhangjin創作於 2015/04/05 評論打賞

By Falcon of TinyLab.org 2015/04/03

1故事緣由

原始碼分析是程式設計師離不開的話題。無論是研究開源專案,還是平時做各類移植、開發,都避免不了對原始碼的深入解讀。

工欲善其事,必先利其器。今天我們來玩轉一個小工具,叫 Callgraph,它可以把 C 語言的函式呼叫樹(或者說流程圖)畫出來。

傳統的命令列工具 Cscope, Ctags 可以結合 vim 等工具提供高效快捷的跳轉,但是無法清晰的展示函式內部的邏輯關係。

至於圖形化的IDE,如 QtCreator, Source Insight, Eclipse, Android Studio 等,卻顯得笨重,而且不一定支援匯出呼叫關係圖。

開源軟體線上程式碼交叉檢索一文中我們也介紹到了諸如 LXR, OpenGrok 之類的工具,它們避免了原生代碼庫而且提供了方便的 Web 展示,不過也無法提供函式關係的清晰展示。

下面開始 Callgraph 之旅。

2安裝 Callgraph

Callgraph 實際由三個工具組合而成。

  • 一個是用於生成 C 函式呼叫樹的 cflow 或者 calltree,下文主要介紹 cflow。
  • 一個處理 dot 文字圖形語言的工具,由 graphviz 提升。建議初步瞭解下:
    DOT 語言
  • 一個用於把 C 函式呼叫樹轉換為 dot 格式的指令碼:tree2dotx

以 Ubuntu 為例,分別安裝它們:

  1. $ sudo apt-get install cflow graphviz

如果確實要用 calltree,請通過如下方式下載。不過 calltree 已經年久失修了,建議不用。

  1. $ wget -c https://github.com/tinyclub/linux-0.11-lab/raw/master/tools/calltree

接下來安裝 tree2dotx 和 Callgraph,這裡都預設安裝到/usr/local/bin

  1. $ wget -c https://github.com/tinyclub/linux-0.11-lab/raw/master/tools/tree2dotx
  2. $ wget -c https://github.com/tinyclub/linux-0.11-lab/raw/master/tools/callgraph
  3. $ sudo cp tree2dotx callgraph /usr/local/bin
  4. $ sudo chmod +x /usr/local/bin/{tree2dotx,callgraph}

注:部分同學反饋,tree2dotx輸出結果有異常,經過分析,發現用了mawk,所以請提交安裝下gawk

  1. $ sudo apt-get install gawk

3分析 Linux 0.11

3.1準備

先下載泰曉科技提供的五分鐘 Linux 0.11 實驗環境:Linux-0.11-Lab

  1. $ git clone https://github.com/tinyclub/linux-0.11-lab.git && cd linux-0.11-lab

3.2初玩

回到之前在Linux-0.11-Lab展示的一副圖:

它展示了 Linux 0.11 的主函式 main 的呼叫層次關係,清晰的展示了核心的基本架構。那這樣一副圖是如何生成的呢?非常簡單:

  1. $ make cg f=main
  2. Func: main
  3. Match: 3
  4. File:
  5. 1 ./init/main.c: * main() use the stack at all after fork(). Thus, no function
  6. 2 ./init/main.c: * won't be any messing with the stack from main(), but we define
  7. 3 ./init/main.c:void main(void) /* This really IS void, no error here. */
  8. Select: 1 ~ 3 ? 3
  9. File: ./init/main.c
  10. Target: ./init/main.c: main -> callgraph/main.__init_main_c.svg

需要注意的是,上面提供了三個選項用於選擇需要展示的圖片,原因是這個 callgraph 目前的函式識別能力還不夠智慧,可以看出 3 就是我們需要的函式,所以,上面選擇序號 3。

生成的函式呼叫關係圖預設儲存為 callgraph/main.__init_main_c.svg。

圖片匯出後,預設會呼叫 chromium-browser 展示圖片,如果不存在該瀏覽器,可以指定其他圖片瀏覽工具,例如:

  1. $ make cg b=firefox

上面的make cg實際呼叫callgraph

  1. $ callgraph -f main -b firefox

3.3玩轉它

類似main函式,實際也可渲染其他函式,例如:

  1. $ callgraph -f setup_rw_floppy
  2. Func: setup_rw_floppy
  3. File: ./kernel/blk_drv/floppy.c
  4. Target: ./kernel/blk_drv/floppy.c: setup_rw_floppy -> callgraph/setup_rw_floppy.__kernel_blk_drv_floppy_c.svg

因為只匹配到一個setup_rw_floppy,無需選擇,直接就畫出了函式呼叫關係圖,而且函式名自動包含了函式所在檔案的路徑資訊。

  • 模糊匹配

    例如,如果只記得函式名的一部分,比如setup,則可以:

    1. $ callgraph -f setup
    2. Func: setup
    3. Match: 4
    4. File:
    5. 1 ./kernel/blk_drv/floppy.c:static void setup_DMA(void)
    6. 2 ./kernel/blk_drv/floppy.c:inline void setup_rw_floppy(void)
    7. 3 ./kernel/blk_drv/hd.c:int sys_setup(void * BIOS)
    8. 4 ./include/linux/sys.h:extern int sys_setup();
    9. Select: 1 ~ 4 ?

    因為setup_rw_floppy函式是第 2 個被匹配到的,選擇 2 就可以得到相同的結果。

  • 指定函式所在檔案(或者檔案所在的目錄)

    1. $ callgraph -f setup -d ./kernel/blk_drv/hd.c

    類似的,make cg可以這麼用:

    1. $ make cg f=setup d=./kernel/blk_drv/hd.c

    看看效果:

4分析新版 Linux

4.1初玩

先來一份新版的 Linux,如果手頭沒有,就到 www.kernel.org 搞一份吧:

  1. $ wget -c https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.10.73.tar.xz
  2. $ tar Jxf linux-3.10.73.tar.xz && cd linux-3.10.73

玩起來:

  1. $ callgraph -f start_kernel -d init/main.c

4.2酷玩

  • 砍掉不感興趣的函式分支

    上面生成的圖,有沒有覺得printk之類的呼叫太多,覺得很繁瑣。沒關係,用-F砍掉。

    1. $ callgraph -f start_kernel -d init/main.c -F printk

    如果要砍掉很多函式,則可以指定一個函式列表:

    1. $ callgraph -f start_kernel -d init/main.c -F "printk boot_cpu_init rest_init"
  • 指定函式呼叫深度:

    -D命令可以指定:

    1. $ callgraph -f start_kernel -d init/main.c -F "printk boot_cpu_init rest_init" -D 2
  • 指定函式搜尋路徑

    我們來看看update_process_times的定義,用-d指定搜尋路徑:

    1. $ callgraph -f update_process_times -d kernel/

    它會自動搜尋kernel/目錄並生成一副圖,效果如下:

    考慮到callgraph本身的檢索效率比較低(採用grep),如果不能明確函式所在的目錄,則可以先用cscope之類的建立索引,先通過這些索引快速找到函式所在的檔案,然後用-d指定檔案。

    例如,假設我們通過cs find g update_process_times找到該函式在kernel/timer.c中定義,則可以:

    1. $ callgraph -f update_process_times -d kernel/timer.c

5原理分析

callgraph實際上只是靈活組裝了三個工具,一個是 cflow,一個是 tree2dotx,另外一個是 dot。

5.1cflow:拿到函式呼叫關係

  1. $ cflow -b -m start_kernel init/main.c > start_kernel.txt

5.2tree2dotx: 把函式呼叫樹轉換成 dot 格式

  1. $ cat start_kernel.txt | tree2dotx > start_kernel.dot

5.3用 dot 工具生成可以渲染的圖片格式

這裡僅以 svg 格式為例:

  1. $ cat start_kernel.dot | dot -Tsvg -o start_kernel.svg

實際上 dot 支援非常多的圖片格式,請參考它的手冊:man dot

6趣玩 tree2dotx

關於tree2dotx,需要多說幾句,它最早是筆者 2007 年左右所寫,當時就是為了直接用圖文展示樹狀資訊。該工具其實支援任意類似如下結構的樹狀圖:

  1. a
  2. b
  3. c
  4. d
  5. x
  6. y
  7. e
  8. f

所以,我們也可以把某個目錄結構展示出來,以 Linux 0.11 為例:

  1. $ cd linux-0.11
  2. $ tree -L 2 | tree2dotx | dot -Tsvg -o tree.svg

如果覺得一張圖顯示的內容太多,則可以指定某個當前正在研讀的核心目錄,例如kernel部分:

  1. $ tree -L 2 kernel | tree2dotx -f Makefile | dot -Tsvg -o tree.svg

看下效果:

7What’s more?

上文展示瞭如何把原始碼的呼叫關係用圖文的方式渲染出來。好處顯而易見:

  • 不僅可以清晰的理解原始碼結構,從而避免直接陷入細節,進而提高原始碼分析的效率。
  • 也可以基於這個結果構建流程圖,然後用inkscape之類的工具做自己的調整和擴充,方便做後續展示。
  • 還可以把這些圖文用到文件甚至書籍中,以增加可讀性。

Callgraph的圖文展示基於cflow或者calltree,它們都只是靜態原始碼分析的範疇。

後續我們將從從執行時角度來動態分析原始碼的實際執行路徑。我們計劃分開展示應用部分和核心部分。

http://www.tinylab.org/callgraph-draw-the-calltree-of-c-functions/

http://blog.csdn.net/flyforfreedom2008/article/details/46418793