用 Python 擴充 GDB(二)

發表於2016-06-17

歡迎來到《用python擴充gdb》的第二篇。在上一篇,我們學習了gdb提供的常用python介面,並用python實現了自定義命令和除錯指令碼。

到目前為止,我們都是在用python實現內建DSL(領域特定語言)也能實現的效果。從本篇開始,我們將繼續上路,去欣賞內建DSL所缺乏的新風景。

下一站,Pretty-Printer。

什麼是Pretty-Printer

當我們在gdb中列印一個類/結構體時,gdb會嘗試輸出該型別的所有成員和它們的值。對於指標,即是輸出指標所指向的地址。如果要想進一步檢視指標指向的值,需要使用p *cls->x@range這樣的語法,來轉換出該地址上對應的值。畢竟,C/C++是一門接近硬體的語言,如果你不指明某個地址上的具體意義,在計算機看來,不過是些位元組罷了。

如果你除錯過C++的STL容器,就會(驚喜地)發現:gdb並不會把容器裡面各種亂七八糟的成員都列印一通,相反它僅僅輸出容器裡面的資料(除非你使用的gdb版本感人)。這一特性的背後,離不開Pretty-Printer的功勞。Pretty-Printer允許使用者使用python給指定類編寫自定義的列印方式。事實上,gdb內建了一個python指令碼,正是這個指令碼決定了STL容器的列印輸出。

專案中的某個類太過於複雜?
正在使用某個自定義的資料結構?
想要快速看出某個屬性的編碼代表什麼?

Pretty-Printer可以幫你解決以上所有問題。

實現一個Pretty-Printer

跟自定義命令不同,Pretty-Printer不需要使用者去繼承某一類,使用者編寫的Pretty-Printer類僅需要實現指定的方法。你也可以視之為繼承介面。

順便一提,考慮到Pretty-Printer實在太長,請允許我為了偷懶,從下文開始用pprinter來簡寫之。

使用者實現的pprinter會收到一個表示被列印物件的gdb.Value作為構造引數,另外還需要實現一個to_string方法,返回一個字串作為該物件的列印結果。

這就是pprinter的全部要求了。此外你還可以實現children方法用於輸出該類裡面複雜成員的值,display_hint方法用於定義輸出的樣式。

還是老樣子,邊上程式碼邊解釋。

假設我們有如下一個Buffer結構體的定義:

現在我們需要編寫一個pprinter,它能夠輸出該Buffer裡面的資料,以及Buffer當前的使用程度。

事實上,我們完全可以把children方法列印的內容放到to_string中。下面是等價的程式碼:(列印的結果有所不同,不過差異不大)

註冊Pretty-Printer

接下來是向gdb註冊我們自定義的pprinter:

使用效果如下:

小結

從本篇開始,我們接觸了gdb更多的特性,登上了DSL所無法到達的高處。能通過python來自定義列印方式,無疑為gdb的使用開啟新的大門。現在,gdb工具箱裡又多了項新工具。

專案中的某個類太過於複雜?在pprinter中僅顯示關鍵的成員屬性。

正在使用某個自定義的資料結構?通過編寫pprinter,我們也能像列印STL容器一樣列印出它們的資料。
想要快速看出某個屬性的編碼代表什麼?可以在pprinter中實現編碼到可讀字串的轉換,正如在示例中,我們從encoding中讀出data屬性的型別。

下一篇中,我們會談論另一個內建DSL實現不了的功能——convenience function(可以理解為gdb會話中的內建函式)。敬請期待!

相關文章