VC++使用pdb和dump恢復“案發現場”
目錄
pdb檔案
PDB檔案簡介
pdb符號檔案是連線二進位制指令和原始碼之間的紐帶,沒有符號你所面對只有地址,由連結器自動生成。
pdb在執行的時候沒有任何作用的,但是對於偵錯程式和我們除錯則有很大的幫助。
檔案由兩個部分構成,私有符號資料(private symbol data)和公共符號表(public symbol table)
-
私有符號資料(Private Symbol Data)
-
函式
-
全域性變數
-
區域性變數
-
使用者定義的結構體,類,資料型別
-
原始檔的名稱和原始檔中每個二進位制指令的行號
-
-
公共符號表(Public Symbol Table)
-
靜態變數
-
全域性變數(external)
-
EXE、DLL等與pdb檔案的匹配
偵錯程式是如何來判別EXE、DLL等是否和一個pdb檔案匹配呢?
每次我們連結EXE或者DLL或者SYS的時候,連結器都將產生一個唯一的GUID,然後將其寫入到PDB和可執行檔案。偵錯程式載入的時候將檢查兩者的GUID,如果一致就表示他們匹配。
如果我們需要除錯,我們需要查dmp檔案,那麼請妥善保管好自己的程式碼和pdb。每次重新編譯,即使所有程式碼均沒有變化,他們的GUID也不同。
編譯器產生符號的過程
如果指定生成除錯資訊,編譯器在每次編譯完檔案以後就會產生一個obj檔案,然後同時產生它對應的除錯資訊。當我們進行連線的時候,編譯器就會幫我們把所有obj統一編譯為一個可執行檔案,然後所有的除錯資訊統一生成一個PDB檔案。
Release程式生成pdb檔案
用VS除錯Release的程式,發現無法除錯。其實,並不是Release的程式不能除錯,而是沒有讓Release的程式生成pdb檔案,VS無法載入pdb檔案而無法除錯程式。
設定一下,讓Release的程式也生成pdb檔案,就好了。
dump檔案
使用背景介紹
當程式遇到未處理異常(主要指非指標造成)導致程式崩潰死,如果在異常發生之前呼叫了SetUnhandledExceptionFilter()函式,異常交給函式處理。MSDN中描述為:
Issuing SetUnhandledExceptionFilter replaces the existing top-level exception filter for all existing and all future threads in the calling process.
因而,在程式開始處增加SetUnhandledExceptionFilter()函式,並在函式中利用適當的方法生成Dump檔案,即可實現需要的功能。
Dump檔案是程序的記憶體映象。可以把程式的執行狀態通過偵錯程式儲存到dump檔案中。
dump檔案的生成
#include<iostream>
#include<Windows.h>
#include<DbgHelp.h>
using namespace std;
#pragma comment(lib,"DbgHelp.lib")
// 建立Dump檔案
void CreateDumpFile(LPCWSTR lpstrDumpFilePathName, EXCEPTION_POINTERS *pException)
{
HANDLE hDumpFile = CreateFile(lpstrDumpFilePathName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// Dump資訊
MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
dumpInfo.ExceptionPointers = pException;
dumpInfo.ThreadId = GetCurrentThreadId();
dumpInfo.ClientPointers = TRUE;
// 寫入Dump檔案內容
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);
CloseHandle(hDumpFile);
}
// 處理Unhandled Exception的回撥函式
LONG ApplicationCrashHandler(EXCEPTION_POINTERS *pException)
{
CreateDumpFile(L"Test.dmp", pException);
cout << "異常已記錄" << endl;
system("pause");
return EXCEPTION_EXECUTE_HANDLER;
}
void func1() {
cout << "正常函式" << endl;
}
void func2() {
int num = 10;
int in;
cout << "輸入一個整數:" << endl;
//當輸入為0時則會發生異常
cin >> in;
num = num / in;
cout << num << endl;
}
int main()
{
//註冊異常處理函式
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)ApplicationCrashHandler);
func1();
func2();
}
除錯dump檔案
VS除錯
本地dump除錯
直接用VS開啟Test.dmp檔案,測試時dmp檔案是本地產生的,因此VS會依據dmp檔案自行找到exe,pdb和原始碼的路徑。因此直接點選除錯,程式會出錯程式碼行中斷。
無原始碼dump除錯
但若dmp檔案是exe在另一臺機器上產生的,則我們最好把exe,pdb,dmp放到同一資料夾下,必須保證pdb與出問題的exe是同一時間生成的,用VS開啟dmp檔案後還需要設定符號表檔案路徑和原始碼路徑。
(1)當把pdb檔案與dmp檔案放入同一目錄下時,就不需設定其路徑,否則需要設定
工具->選項->除錯->符號:
(2)還需設定原始碼路徑:
屬性->除錯原始碼:
這樣點選“使用僅限本機進行除錯”,即可在出錯程式碼行中斷:
WinDbg除錯
基本思路與VS一致,winDbg會提供更為全面的除錯資訊
(1)設定pdb路徑:File ->Symbol File Path
(2)設定exe路徑:File -> Image File Path
(3)設定原始碼路徑:File -> Source File Path(指sln所在目錄)
(4)開啟dmp檔案:File ->Open Crash Dump
(5)執行命令 !analyze –v
可以得出詳細的異常分析