直接使用gdb調試Qt應用時,Qt的一些數據類型沒法友好的顯示出來,而qtcreator可以很好的展示出來,qtcreator也是通過gdb來調試的,在展示數據時,其實是gdb通過python腳本來處理後顯示的,這些python腳本位於/usr/share/qtcreator/debugger
這個位置(ubuntu20.04)。
gdb在啓動時,會自動在某些目錄尋找初始化腳本,一般爲:
~/etc/gdb/gdbinit
~/.gdbinit
./.gdbinit
也可以在gdb啓動後,手動指定加載初始化腳本source your_gdbinit
。按照/usr/share/qtcreator/debugger
裏面的README.txt
,我在~/.gdbinit加了加載命令後,發現不能正常工作。在網絡上搜索到kde項目用的kdevelop gdb printers,kde是使用Qt作爲GUI開發框架的一個東西,與之對應的是gnome。也可以直接在這裏下載
首先將連接裏面的3個py文件保存到~/.gdb/qt5prettyprints
目錄下,其中kde.py
可以不用。然後將gdbinit
文件保存爲~/.gdbinit
。需要稍微改下這個文件:
python
#新增下面兩行
import sys, os
sys.path.insert(0, "/home/a/.gdb/qt5prettyprinters")
from qt import register_qt_printers
register_qt_printers (None)
#這兩句看你是否需要做kde開發
from kde import register_kde_printers
register_kde_printers (None)
end
新增的兩行的意思是,給第三方的模塊(qt.py, kde.py, helper.py)設置搜索路徑,因爲這3個python文件保存在我自定義的位置。現在就可以在gdb,甚至是第三方IDE中通過gdb來直觀的觀察變量了。
具體支持哪些Qt內置類型,可以看qt.py
腳本的build_dictionary
函數,當前爲20個。
在CLion中使用這個qt pretty print的python腳本時,遇到一個bug,就是當QDateTime類型作爲參數傳遞時,如果在這在函數上下斷點會引起qt.py
這個腳本執行異常,導致程序直接退出。例如下面這個例子
如果僅下2,3號斷點,不進使用QDateTime作爲參數的foo函數,不會有異常,但是如果下了1號斷點,程序進入foo直接退出。一開始我使用CLion調試,總是莫名其妙的退出,折騰了1個小時才發現這個規律。但是不知道原因。無意中拋開CLion直接用gdb調試,才發現是python腳本出了問題,如下圖:
查看下qt.py
腳本里面打印QDateTime類型的處理函數:
class QDateTimePrinter:
def __init__(self, val):
self.val = val
def to_string(self):
time_t = gdb.parse_and_eval("reinterpret_cast<const QDateTime*>(%s)->toSecsSinceEpoch()" % self.val.address)
return time.ctime(int(time_t))
gdb.parse_and_eval
很容易看得懂,就是gdb的eval
命令,然後結果返回給time_t
,再通過time.ctime將time_t轉爲字符串,返回,也就是print一個QDateTime變量時,顯示的字符串。
再分析下不用qt.py
時,直接執行eval,看得到什麼結果,先註釋~/.gdbinit
裏面初始化語句,不加載qt.py
。然後在foo函數下斷點,執行eval。
可以看到eval執行時就報了內存訪問錯誤了。只能改qt.py
裏面打印QDateTime的邏輯了。經過調試我改爲下面的樣子:
class QDateTimePrinter:
def __init__(self, val):
self.val = val
def to_string(self):
sec = self.val['d']['data']['msecs'] / 1000
localsec = time.localtime(sec + time.timezone)
return time.strftime("%Y-%m-%d %H:%M:%S", localsec)
首先它拿到毫秒時間戳,取秒,然後根據本地時區,轉爲字符串格式。至於爲什麼self.val['d']['data']['msecs']
可以拿到毫秒時間戳,可以看下class QDateTime
的定義:
class QDateTime
{
.....
.....
struct ShortData
{
.....
.....
quintptr status : 8;
qintptr msecs : sizeof(void *) * 8 - 8;
}
union Data
{
.....
.....
QDateTimePrivate *d;
ShortData data;
.....
.....
}
private:
Data d;
.....
.....
}