dump文件
dump文件記錄當前程序運行某一時刻的信息,包括內存,線程,線程棧,變量等等,相當于調試程序時運行到某個斷點上,把程序運行的信息記錄下來。可以通過Windbg打開dump,查看程序運行的變量等,來調試程序。
在Liunx上也有類似的技術,Coredump,具體可以參考:coredump詳解_coredump文件分析_賀二公子的博客-CSDN博客
dump 文件分類
dump可以分為 minidump 和 Full dump
minidump通常只包含了一些關鍵信息,一般比較小,通常只要幾MB,
Full dump包含了程序運行時的所有信息,包括程序的所有內存,一般有幾十MB到幾GB。
minidump雖然只包含了部分信息,但這些信息大部分情況足夠用于調試,所以大部分情況都是使用minidump調試
生成dump文件
通過任務管理器導出
在進程上右擊->創建內存轉儲文件,這樣創建的是Full dump
通過Process Explorer導出
Process Explorer - Sysinternals | Microsoft Learn
選擇對應的進程->Process->Create Dump,然后選擇要創建minidum 還是 Full Dump
使用MiniDumpWriteDump函數序生成?
#include <iostream>
#include <Windows.h>
#include <Dbghelp.h>
#include <thread>
#pragma comment(lib, "Dbghelp.lib")void createMinidump()
{wchar_t DumpPath[MAX_PATH] = {0};SYSTEMTIME SystemTime;GetLocalTime(&SystemTime);WCHAR szExeFileName[256] = {0};GetModuleFileNameW(nullptr, szExeFileName, 99);wsprintfW(DumpPath, L"%s_%d-%d-%d_%d-%d-%d.dmp", szExeFileName, SystemTime.wYear, SystemTime.wMonth,SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond);HANDLE file = CreateFileW(DumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);if (file != INVALID_HANDLE_VALUE){DWORD Flags = MiniDumpWithHandleData |MiniDumpWithUnloadedModules |MiniDumpScanMemory|MiniDumpWithIndirectlyReferencedMemory |MiniDumpWithProcessThreadData |MiniDumpWithThreadInfo;if (MiniDumpWriteDump(GetCurrentProcess(), (DWORD) GetCurrentProcessId(), file,(MINIDUMP_TYPE) (Flags),nullptr, nullptr, nullptr) != 0){std::cout << "Create Minidump successful!! file:";std::wcout << DumpPath << std::endl;}else{std::cout << "Create Minidump failed!!" << std::endl;}}CloseHandle(file);
}void createFullDump()
{wchar_t DumpPath[MAX_PATH] = {0};SYSTEMTIME SystemTime;GetLocalTime(&SystemTime);WCHAR szExeFileName[256] = {0};GetModuleFileNameW(nullptr, szExeFileName, 99);wsprintfW(DumpPath, L"%s_%d-%d-%d_%d-%d-%d_full.dmp", szExeFileName, SystemTime.wYear, SystemTime.wMonth,SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond);HANDLE file = CreateFileW(DumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);if (file != INVALID_HANDLE_VALUE){const DWORD Flags = MiniDumpWithFullMemory |MiniDumpWithFullMemoryInfo |MiniDumpWithHandleData |MiniDumpWithUnloadedModules |MiniDumpWithProcessThreadData |MiniDumpWithThreadInfo;if (MiniDumpWriteDump(GetCurrentProcess(), (DWORD) GetCurrentProcessId(), file,(MINIDUMP_TYPE) (Flags),nullptr, nullptr, nullptr) != 0){std::cout << "Create Full dump successful!! file:";std::wcout << DumpPath << std::endl;}else{std::cout << "Create Full dump failed!!" << std::endl;}}CloseHandle(file);
}
程序崩潰時自動導出Dump
我們希望程序運行崩潰時可以自動導出dump,這樣可以通過分析dump文件找到崩潰原因。
程序崩潰很多情況都是由異常引起的,Windows提供了SetUnhandledExceptionFilter函數用來設置一個函數指針,用于處理未處理的異常,可以在這個函數中導出Dump文件。
1. 準備一個處理異常的函數,并在其中導出dump。異常處理函數有一個參數,這個參數記錄了當前異常信息,這個異常信息可以一起隨dump文件導出,方便后續查找文件
LONG WINAPI DumpException(EXCEPTION_POINTERS* info)
{std::cout << "DumpException, Thread ID:"<< std::this_thread::get_id() << std::endl;std::cout << "Exception: 0x" << std::hex << info->ExceptionRecord->ExceptionCode << std::endl;wchar_t DumpPath[MAX_PATH] = { 0 };SYSTEMTIME SystemTime;GetLocalTime(&SystemTime);WCHAR szExeFileName[100] = { 0 };GetModuleFileNameW(nullptr, szExeFileName, 99);wsprintfW(DumpPath, L"%s_%d-%d-%d_%d-%d-%d_crash.dmp", szExeFileName, SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond);HANDLE file = CreateFileW(DumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);if (file != INVALID_HANDLE_VALUE){MINIDUMP_EXCEPTION_INFORMATION mdei;mdei.ThreadId = (DWORD)GetCurrentThreadId();mdei.ExceptionPointers = info;mdei.ClientPointers = 0;DWORD Flags = MiniDumpWithHandleData |MiniDumpWithUnloadedModules |MiniDumpScanMemory|MiniDumpWithIndirectlyReferencedMemory |MiniDumpWithProcessThreadData |MiniDumpWithThreadInfo;// Flags = MiniDumpWithFullMemory |
// MiniDumpWithFullMemoryInfo |
// MiniDumpWithHandleData |
// MiniDumpWithUnloadedModules |
// MiniDumpWithThreadInfo;if (MiniDumpWriteDump(GetCurrentProcess(), (DWORD)GetCurrentProcessId(), file,(MINIDUMP_TYPE)(Flags),&mdei, nullptr, nullptr) != 0){std::cout << "Create Crash dump successful!! file:";std::wcout << DumpPath << std::endl;CloseHandle(file);return EXCEPTION_EXECUTE_HANDLER;}}std::cout << "Create Crash dump failed!!" << std::endl;CloseHandle(file);return EXCEPTION_CONTINUE_SEARCH;
}
2. 在程序啟動時設置異常處理函數
int main()
{std::cout << "Main Thread ID:" << std::this_thread::get_id() << std::endl;LPTOP_LEVEL_EXCEPTION_FILTER oldExceptionFilter = nullptr;oldExceptionFilter = SetUnhandledExceptionFilter(&DumpException);// ... ...// ... ...}
PS
1. 這里是通過異常捕獲生成dump,如果是調用abort,exit,TerminateProcess, TerminateThread函數,這些函數會立即結束函數,所以不會生成dump。
2.?SetUnhandledExceptionFilter是全局的,只需設置一次,設置后對所有線程有效。SetUnhandledExceptionFilter有些異常捕獲不到。
3. 可以使用第三方庫捕獲崩潰事件,例如:crashrpt,google breakpad,qBreakpad,Crashpad
UnhandledExceptionFilter未處理的異常
Windows中所有的函數都是從BaseThreadStart函數開始運行
VOID BaseThreadStart(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam) {
__try {
ExitThread((pfnStartAddr)(pvParam));
}
__except (UnhandledExceptionFilter(GetExceptionInformation())) {
ExitProcess(GetExceptionCode());
}
// NOTE: We never get here
}
這里的函數UnhandledExceptionFilter用來處理線程中捕獲的未處理的異常,調用SetUnhandledExceptionFilter就是用來設置這個函數。
這里的__try{}__except{} 是?Windows系統的結構化異常處理(SEH),具體參考 《Windows核心編程第五版》——第24章
使用VS調試Dump文件
調試Dump文件,dump文件以外,還需要pdb符號文件,pdb符號文件是編譯時和exe程序同時生成的,默認情況下Debug版本會生成符號文件,Release文件不生成符號文件,Release模式下需要收到打開生成符號文件。
打開Dump文件
文件->打開->文件,選擇dump文件
設置符號文件
符號文件(pdb)必須保證時和exe同時生成的,且不能改文件名,否則會加載失敗
有兩種方法
1. 把符號文件和dump文件放在同一個目錄下,VS在加載dump時會讀取dump目錄下的符號文件。
2. 通過 【工具->選項->調試->符號】設置符號文件路徑。
?3. 點擊右上角的 【使用 混合 進行調試】,則可以查看dump文件的內容,和調試模式下運行出現異常是一樣的。
?
使用Windbg調試Dump文件
Windbg 下載?Install WinDbg - Windows drivers | Microsoft Learn
Windbg打開dump后會提示是否有異常
調試步驟:
1. 設置符號文件 【文件->settings->debuging settings】
2. 輸入 .ecxr 命令
3. 輸入 kn 命令
查看exe編譯時間
lm vm test_win*
通過這個時間可以去查找exe對應的pdb文件?
完整代碼例子:小康6650/StudyProject - Gitee.com