Windows程序崩溃捕获dump文件
Windows上程序崩溃时需要分析崩溃原因,需要程序崩溃的dmp文件和对应版本的pdb,Dump文件的获取方式有两大类:
- 通过WER或外部工具来捕获生成。
- 通过在代码中预埋,崩溃时自动生成dump文件。
一、通过WER或外部工具获取dump文件
对于已经发布的程序,基本主要使用该方式,通过修改注册表让WER或借助工具来生成dump文件。
1、 使用 procdump.exe 来捕获崩溃
procdump.exe 可以在程序启动之前或程序启动之后来进行监视,并在程序崩溃的时候生成dmp文件,其作为一个命令行工具,主要参数用法如下:
-accepteula: 自动接受许可协议(第一次运行不弹窗)-e: 捕获未处理的异常(崩溃)-ma: 生成 Full Dump-w: 等待进程启动(wait),常用于捕获一启动就崩溃的进程- 在最后可以指定程序崩溃生成dump文件的路径,如果不指定则会保存在
procdump.exe所在目录下
测试崩溃程序名称为 Test.exe
1)、程序未启动之前启动监视
在cmd中切换到 procdump 所在路径,并执行如下命令。如果指定dump文件所在路径,需要确保路该径存在
# 这里Test.exe为所监视程序的名称
procdump64.exe -accepteula -e -ma -w Test.exe ".\CrashDumps"
2)、程序启动之后启动监视
有些程序不需要未启动之前就监视,此时可以去掉 -w 参数,简洁命令如下:
procdump64.exe -e -ma Test.exe
根据需要可以将其封装成一个脚本,同 procdump放在同一个目录下,直接执行脚本来简化操作。
2、使用注册表启用WER来生成dump文件
WER自动捕获指定程序崩溃生成的dump文件,并指定dump文件的生成路径,需要修改注册表来启用对应的设置,注册表如下,针对特定的测试程序,其中 DumpFolder 为生成dump文件的路径,要保证程序要写入的权限
Windows Registry Editor Version 5.00
; 针对 64 位进程 (或 32 位系统上的 32 位进程)
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\Test.exe]
"DumpFolder"="D:\\CrashDumps"
"DumpCount"=dword:00000010
"DumpType"=dword:00000002
; 针对 64 位系统上的 32 位进程 (关键补充)
[HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\Windows Error Reporting\LocalDumps\Test.exe]
"DumpFolder"="D:\\CrashDumps"
"DumpCount"=dword:00000010
"DumpType"=dword:00000002
执行这个注册表文件之后,会看到注册表的 WER 项有对应的修改
二、代码中内置崩溃异常生成dump文件
在当前的源代码中直接预埋对应注册函数,当异常崩溃发生时,直接自动捕获这个异常,并在指定位置生成对应的dump文件,对应的代码如下:
#include <windows.h>
#include <dbghelp.h>
#include <iostream>
#include <shlobj.h> // 用于创建目录
#include <string>
/** *************************************************************/
// 如果不存在会递归创建
void CreateFullDirectory(std::wstring path) {
size_t pos = 0;
do {
pos = path.find_first_of(L"\\/", pos + 1);
CreateDirectory(path.substr(0, pos).c_str(), NULL);
} while (pos != std::wstring::npos);
}
void CreateDumpFile(EXCEPTION_POINTERS* pException) {
// 方案 A: 自动获取 %LOCALAPPDATA% 路径
wchar_t path[MAX_PATH];
ExpandEnvironmentStrings(L"%LOCALAPPDATA%\\TestCrash\\", path, MAX_PATH);
// 方案 B: 指定路径
// wcscpy_s(path, L"D:\\MyDebugDumps\\");
// 1. 确保目标文件夹存在
CreateFullDirectory(path);
// 2. 构造带时间戳的文件名,避免覆盖
SYSTEMTIME st;
GetLocalTime(&st);
wchar_t fileName[MAX_PATH];
swprintf_s(fileName, L"%lsCrash_%04d%02d%02d_%02d%02d%02d.dmp",
path, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
// 3. 创建文件
HANDLE hFile = CreateFile(fileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
dumpInfo.ThreadId = GetCurrentThreadId();
dumpInfo.ExceptionPointers = pException;
dumpInfo.ClientPointers = TRUE;
BOOL bSuccess = MiniDumpWriteDump(
GetCurrentProcess(),
GetCurrentProcessId(),
hFile,
MiniDumpNormal,
&dumpInfo,
NULL,
NULL
);
if (bSuccess) {
std::wcout << L"Dump saved to: " << fileName << std::endl;
}
CloseHandle(hFile);
}
}
LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo) {
CreateDumpFile(ExceptionInfo);
return EXCEPTION_EXECUTE_HANDLER;
}
/** *************************************************************/
// 主函数或dll程序入口注册这个异常过滤器,异常崩溃产生的时候自动生成dump文件
int main()
{
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
/** crash code **/
}
这种方式在一些复杂场景下可能不会如期生成对应的dump文件,如C++ 标准库的 abort() 有时会绕过这个过滤器,或者多线程下的栈破坏问题,此时需要使用成熟的捕获崩溃的开源库
浙公网安备 33010602011771号