1、背景
在做 Windows 用戶端應用開發時,難免遇到程式的崩潰,當程式在 debug 崩潰時,我們可以直接定位到崩潰點,但是當程式打包成 release 釋出時,難免也會遇到一些崩潰問題(當然在開發時要盡量保障程式的穩定性),一般遇到這樣的崩潰,我們就需要使用 dump 檔案加上符号表檔案來進行調試程式,是以一般的 CI 除了将 exe 上傳以外還需要将符号表資訊上傳。
2、在代碼中生成 dump 檔案
#include <Windows.h>
#include <DbgHelp.h>
#include <tchar.h>
#include <shlobj_core.h>
#include <io.h>
#include <direct.h>
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
// 生成 dump 檔案的目錄名稱
const std::string& kDUMPDir = "\\MyDumpTest";
typedef BOOL(WINAPI * MiniDumpWriteDumpT)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE,
PMINIDUMP_EXCEPTION_INFORMATION, PMINIDUMP_USER_STREAM_INFORMATION, PMINIDUMP_CALLBACK_INFORMATION);
int CreateDump(PEXCEPTION_POINTERS pointers) {
HMODULE dbg_help = LoadLibrary("DbgHelp.dll");
if (NULL == dbg_help) {
return EXCEPTION_CONTINUE_EXECUTION;
}
MiniDumpWriteDumpT dump_writer = (MiniDumpWriteDumpT)GetProcAddress(dbg_help, "MiniDumpWriteDump");
if (NULL == dump_writer) {
FreeLibrary(dbg_help);
return EXCEPTION_CONTINUE_EXECUTION;
}
// 擷取到 %localappdata% 目錄
char buffer[256] = "";
SHGetSpecialFolderPathA(NULL, buffer, CSIDL_LOCAL_APPDATA, FALSE);
std::string dump_dir = buffer;
dump_dir += kDUMPDir;
std::cout << "dump_dir:" << dump_dir << std::endl;
// 判斷檔案夾是否存在
if (_access(dump_dir.c_str(), 0) == -1) {
// 如果不存在,那麼就建立
_mkdir(dump_dir.c_str());
}
// dmp 檔案名
SYSTEMTIME local_time;
GetLocalTime(&local_time);
std::stringstream ss;
ss << dump_dir << "\\dump-" << local_time.wYear << "-" <<
std::setw(2) << std::setfill('0') << local_time.wMonth << "-" <<
std::setw(2) << std::setfill('0') << local_time.wDay << "-" <<
std::setw(2) << std::setfill('0') << local_time.wHour <<
std::setw(2) << std::setfill('0') << local_time.wMinute <<
std::setw(2) << std::setfill('0') << local_time.wSecond << ".dmp";
HANDLE dump_file = CreateFile(ss.str().c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
if (INVALID_HANDLE_VALUE == dump_file) {
FreeLibrary(dbg_help);
return EXCEPTION_CONTINUE_EXECUTION;
}
// 寫入 dmp 檔案
MINIDUMP_EXCEPTION_INFORMATION param;
param.ThreadId = GetCurrentThreadId();
param.ExceptionPointers = pointers;
param.ClientPointers = FALSE;
// MiniDumpWithDataSegs : dump 檔案的類型,一般這個就夠了
// MiniDumpNormal 比較小 MiniDumpWithFullMemory 比較大
dump_writer(GetCurrentProcess(), GetCurrentProcessId(),
dump_file, MiniDumpWithDataSegs, (pointers ? ¶m : NULL), NULL, NULL);
// 釋放檔案
CloseHandle(dump_file);
FreeLibrary(dbg_help);
return 0;
}
LONG WINAPI ExceptionFilter(LPEXCEPTION_POINTERS pointers) {
// 這裡做一些異常的過濾或提示
if (IsDebuggerPresent()) {
return EXCEPTION_CONTINUE_SEARCH;
}
return CreateDump(pointers);
}
int main(int argc, char *argv[]) {
SetUnhandledExceptionFilter(ExceptionFilter);
// 執行程式
int* p = nullptr;
p[0] = 1;
std::cout << "end" << std::endl;
getchar();
return 0;
}
3、運作程式