一、dump和pdb是什么
????????在Windows系統下,當我們寫的程序跑在客戶的機器上,因為一個bug,導致程序崩潰,我們該如何定位并修復這個bug呢?
????????有人會說記錄日志,即便有日志,也是不好定位的,因為你只能推測出大概的模塊或者位置,無法定位到具體出錯的代碼行。
????????此時,我們可以讓程序崩潰后,自動生成一個*.dmp文件,并配合在編譯該程序時生成的pdb文件,來準確定位到調用堆棧、代碼行上。這樣很輕易就可以找到該bug。
-
dump文件,后綴*.dmp,是程序崩潰時的內存轉儲文件;
-
pdb文件,后綴*.pdb,是程序的符號文件。
二、Breakpad與qbreakpad簡介
????????Breakpad是由Google開發的開源跨平臺崩潰報告系統,用于捕獲程序崩潰時的內存狀態并生成輕量級minidump文件(.dmp)。其核心功能包括崩潰攔截、堆棧記錄和寄存器狀態捕獲,適用于C++應用的多平臺部署(Windows/Linux/macOS)?。
????????QBreakpad是Breakpad的?Qt專用封裝庫?,它將Breakpad的復雜集成簡化為Qt模塊,提供更便捷的API和跨平臺兼容性,專門服務于Qt應用程序的崩潰管理?。
????????BreakPad工作原理示意圖:
表達的意思就是:
-
我們在編譯的時候,需要在Release版程序中生成調試信息。
-
使用Breakpad提供的dump_syms工具或者cv2pdb工具,從release版本程序導出符號文件。
-
當程序崩潰時,breakpad會捕捉崩潰,并生成dump文件。
-
dump文件可以直接發送到指定服務器,或者由用戶手動發給開發者。
-
收到dump文件后,結合符號文件,可通過minidump_stackwalk工具或Visual Studio工具生成堆棧調用信息文件,這個文件可以直接閱讀,定位bug。
三、開發環境說明
? ? ? ? 本人使用的開發環境如下:
? ? ? ? 操作系統:Windows10
? ? ? ? IDE:Qt Creator4.4.1
? ? ? ? 編譯器:mingw53_32
? ? ? ? Qt庫:Qt5.9.3?
四、源碼準備
????????我們知道qBreakpad是對Breakpad的封裝,所以qBreakpad的編譯,還依賴2套源碼Breakpad、LSS。
(1)下載Breakpad源碼
下載地址:https://github.com/google/breakpad
(2)下載LSS源碼
下載地址:https://github.com/ithaibo/linux-syscall-support
(3)下載qBreakpad源碼
下載地址:https://github.com/buzzySmile/qBreakpad
?(4)下載cv2pdb工具
下載地址:
注意:這個工具最好下載最新版本的,作者發布本文時,最新版本是cv2pdb 0.53
五、編譯qBreakpad
(1)將Breakpad、LSS源碼放入third_party目錄
????????解壓qBreakpad源碼后,在qBreakpad-master\third_party目錄下,有如下2個目錄,如下:
????????分別解壓Breakpad、LSS源碼至breakpad和lss目錄,此2個目錄下源碼需要參與qBreakpad的編譯。放置好后,如下所示:
(2)qBreakpad工程介紹
????????在qBreakpad源碼目錄下,使用QtCreator打開qBreakpad.pro工程,如下:
-
demo工程下,有2個演示程序program和reporter,分別實現了演示生成dump文件,上報dump文件的功能。
-
handler為靜態庫工程,該工程封裝了Breakpad,直接編譯此工程,可生成qBreakpad.lib。
-
tests為一個簡單的測試工程。
(3)編譯生成qBreakpad.lib
????????分別在Debug、Release模式下,編譯handler工程,生成2個版本的qBreakpad.lib靜態庫。
因為程序調用qBreakpad.lib時,只能debug版程序鏈接debug版庫,release版程序鏈接release版庫。debug版程序鏈接release版庫會報錯。
(4)編譯生成demo
? ? ? 在program.pro文件添加下圖所示內容,目的是編譯release版程序時生成調試信息:
############ for qBreakpad ############
# qBreakpad中需要使用到network模塊
QT += network# 啟用多線程、異常、RTTI、STL支持
CONFIG += thread exceptions rtti stl# without c++11 & AppKit library compiler can't solve address for symbols
CONFIG += c++11
macx: LIBS += -framework AppKit# 啟用調試信息(關鍵!)
QMAKE_CXXFLAGS += -g
QMAKE_CXXFLAGS_RELEASE += -g
QMAKE_CFLAGS_RELEASE += -g
#release在最后link時默認有"-s”參數,表示"Omit all symbol information from the output file",因此要去掉該參數
QMAKE_LFLAGS_RELEASE = -mthreads -Wl,# 配置頭文件搜索路徑和鏈接庫路徑
INCLUDEPATH += $$PWD/qBreakpad/include
CONFIG(debug, debug|release) {
LIBS += -L$$PWD/qBreakpad/lib/debug -lqBreakpad
} else {
LIBS += -L$$PWD/qBreakpad/lib/release -lqBreakpad
}
############ for qBreakpad ############
?
? ? ? 在main函數添加以下內容,啟用崩潰時生成.dmp文件的功能:
六、生成.pdb文件
? ? ? ? 將program這個demo程序編譯出release版本:
? ? ? ? 可見這個demo程序編譯出來后體積是比較大的:
????????然后將前面下載的cv2pdb工具解壓放到目標程序同一個目錄:
? ? ? ? 在這個目錄打開終端,執行命令生成.pdb文件:
cv2pdb.exe? ?目標程序.exe
? ? ? ? 執行成功后,會在目標程序的同級目錄下生成.pdb文件,同時也可以看到目標程序的體積變小了:
七、通過.dmp文件追蹤程序崩潰的堆棧信息
? ? ? ? 運行前面生成的test.exe文件,程序崩潰后會生成.dmp文件:
????????使用Visual Studio打開.dmp文件,這里以Visual Studio2022為例:
????????點擊上圖中的“設置符號路徑”,將.pdb文件所在路徑添加進去:
????????擊下圖中的“使用僅限本機進行調試”:
????????如下圖所示,可以看到程序崩潰時的堆棧調用情況:
????????雙擊堆棧調用的行內容,將源碼文件路徑設置一下,便可查看源碼及變量的實時值。
八、總結
? ? ? ? 在Windows系統下,使用Mingw編譯器編譯出來的程序,重點和難點是如何生成.pdb文件。而生成.pdb文件的重點有兩方面,一是在.pro文件添加-g編譯參數,二是使用cv2pdb工具的最新版本,舊版本的cv2pdb工具可能無法生成.pdb文件(這一點困擾了我好久)。
? ? ? ? 如果應用程序調用了很多自己開發的動態庫,那么動態庫的.pro文件也需要添加-g編譯參數,并且動態庫也需要用cv2pdb工具生成.pdb文件。
? ? ? ? 最后,為了方便提取多個文件的.pdb文件,本人寫了2個批處理腳本,一個用于提取.exe文件,另一個用于提取.dll文件,我將腳本給出,供大家參考:
? ? ? ? 批量提取D:\package\bin目錄下所有exe文件的pdb:
@echo off
echo Hello,I am processing pdb files...set OBJECT_HOME=D:\packageset CV2PDB_HOME=%~dp0
CHDIR /D %OBJECT_HOME%set COMPILE_BIN=%OBJECT_HOME%\binecho "step1: del *.pdb..."
del *.pdbecho "step2: create *.pdb..."set FILE_TYPE_EXE=*.exe
for /r "%COMPILE_BIN%" %%i in (%FILE_TYPE_EXE%)do ( if exist %%i %CV2PDB_HOME%/cv2pdb.exe "%%i" )
? ? ? ? 批量提取D:\package\lib目錄下所有dll文件的pdb:
@echo off
echo Hello,I am processing pdb files...set OBJECT_HOME=D:\packageset CV2PDB_HOME=%~dp0
CHDIR /D %OBJECT_HOME%set COMPILE_LIB=%OBJECT_HOME%\libecho "step1: del *.pdb..."
del *.pdbecho "step2: create *.pdb..."set FILE_TYPE_EXE=*.dll
for /r "%COMPILE_LIB%" %%i in (%FILE_TYPE_EXE%)do ( if exist %%i %CV2PDB_HOME%/cv2pdb.exe "%%i" )