用 Python 擴充 GDB(一)

發表於2016-06-17

之前寫的《GDB 自動化操作的技術》一文介紹了可在gdb內部使用的DSL(領域特定語言)來自動化gdb的操作。藉助該DSL,我們分別實現了一個名為mv的自定義命令,和“對賬”用的除錯指令碼。在末尾,我提到了也可以用python來實現擴充指令碼。從本篇開始,我會介紹如何使用python來給gdb編寫指令碼。由於篇幅所限,該教程會分成四篇,爭取在本週內更完。

作為開始的熱身,讓我們用python重新實現前文(《GDB 自動化操作的技術》)的mv命令。

實現自定義命令

引用前文的mv命令實現如下:

對應的python實現如下:

python指令碼完成了,該怎麼執行呢?在gdb裡使用python指令碼,需要用source命令:

在“gdb自動化一的技術”一文中,我們最後把自定義命令的實現放到~/.gdbinit裡面。這樣gdb每次啟動時就會執行它,而無需手動source。直接把python程式碼放進~/.gdbinit當然是不行的。需要變通一下,在~/.gdbinit加入source ~/move.py。這樣gdb每次啟動時都會替我們source一下。

有兩點需要注意的是:

  1. gdb會用python 3來解釋你的python指令碼,除非你用的gdb還處於版本感人的上古時代。
  2. 跟一般情況不同,gdb環境中的sys.path是不包括當前目錄的。這意味著,如果你的指令碼依賴於當前目錄下的其他模組,你需要手工修改sys.path。比如(gdb) python import sys; sys.path.append('')

gdb的python介面

gdb通過gdb模組提供了不少python介面。其中最為常用的是gdb.executegdb.parse_and_eval

如前所示,gdb.execute可用於執行一個gdb命令。預設情況下,結果會輸出到gdb介面上。如果想把輸出結果轉存到字串中,設定to_string為True:gdb.execute(cmd, to_string=True)

gdb.parse_and_eval接受一個字串作為表示式,並以gdb.Value的形式返回表示式求值的結果。舉例說,gdb當前上下文中有一個變數ii等於3。那麼gdb.parse_and_eval('i + 1')的結果是一個gdb.Value的例項,其value屬性的值為4。這跟(gdb) i + 1是等價的。

何為gdb.Value?在gdb會話裡,我們可以訪問C/C++型別的值。當我們通過python介面跟這些值打交道時,gdb會把它們包裝成一個gdb.Value物件。

舉個例子,struct Point有x跟y兩個成員。現在假設當前上下文中有一個Point型別的變數point和指向該變數的Point指標p,就意味著:

有時候我們需要轉換gdb.Value的型別。如果能在gdb上下文內完成轉換,那倒是不難:gdb.parse_and_eval('(TypeX)$a')

但如果只能在python程式碼這一邊完成轉換,倒是有些複雜,需要使用gdb.Type型別:typeX_point = point.cast(gdb.lookup_type('TypeX'))gdb.Value有一個cast方法用於型別轉換,接收一個gdb.Type物件。我們還需要使用lookup_type來構建一個gdb.Type物件。看上去是挺囉嗦。值得注意的是,’TypeX *’和’TypeX &’並非獨立的型別。如果你要獲得型別X的指標/引用,需要這麼寫gdb.lookup_type('X').pointer()/gdb.lookup_type('X').reference()

另外一個常用的介面是gdb.events.stop.connect。你可以使用該介面註冊gdb停止時的回撥函式。當gdb觸發斷點或收到訊號時,就會呼叫事先註冊的回撥函式。對應的,撤銷回撥函式的介面是gdb.events.stop.disconnect

藉助這些介面,我們可以這樣重新實現前文用到的“對賬”指令碼:

用法:sudo gdb -q -p $(pidof $your_project) -x malloc_free.py

小結

對比於前文的DSL實現,“對賬”指令碼的python實現裡直接完成了對資料的處理,免去了額外寫一個指令碼來處理輸出結果。能夠靈活方便地處理資料——這是諸如python一類的通用語言對於領域特定語言的優勢。當然,領域特定語言在其擅長的領域裡,具有通用語言無法比擬的親和力——直接輸入gdb命令,顯然比每次都gdb.execute('xxx')要順暢得多。無論是自定義的mv命令,還是“對賬”指令碼,python實現都要比DSL實現更長。當然,python比照DSL來說,有其自身的長處。本教程剩餘部分會提及這一點。

如果說本篇主要講了如何用python實現DSL實現過的內容,那麼接下來幾篇將關注於如何用python實現DSL實現不了的內容。敬請期待。

完整的python API參見官方文件:https://sourceware.org/gdb/current/onlinedocs/gdb/Python-API.html

另外本人寫過一個gdb介面的輔助模組,包裝了常用的gdb介面: https://github.com/spacewander/debugger-utils 。感興趣的話可以參考下里面的實現。

相關文章