在程序崩溃时,需要对异常的情况进行捕获,并捕获到的堆栈信息保持下来。Windows操作系统提供了一个API函数可以在程序crash之前有机会处理这些异常,就是 SetUnhandleExceptionFilter函数。(C++也有一个类似函数set_terminate可以处理未被捕获的C++异常。)
一、函数介绍:
1、SetUnhandleExceptionFilter函数的定义如下:
SetUnhandledExceptionFilter(_In_opt_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
而LPTOP_LEVEL_EXCEPTION_FILTER的定义如下
typedef LONG (WINAPI *PTOP_LEVEL_EXCEPTION_FILTER)(_In_ struct _EXCEPTION_POINTERS *ExceptionInfo);typedef PTOP_LEVEL_EXCEPTION_FILTER LPTOP_LEVEL_EXCEPTION_FILTER;typedef struct _EXCEPTION_POINTERS {PEXCEPTION_RECORD ExceptionRecord;PCONTEXT ContextRecord;} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
所以定义一个lpTopLevelExceptionFilter函数去处理异常。而lpTopLevelExceptionFilter函数的参数类型是*PEXCEPTION_POINTERS。
2、另一个API函数是写生成dump文件的函数MiniDumpWriteDump,该函数定义如下:
BOOLWINAPIMiniDumpWriteDump(_In_ HANDLE hProcess,_In_ DWORD ProcessId,_In_ HANDLE hFile,_In_ MINIDUMP_TYPE DumpType,_In_opt_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,_In_opt_ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,_In_opt_ PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
第四个参数MINIDUMP_TYPE是转存奔溃信息的枚举类型,不同的枚举值保存奔溃信息范围不同,带来的影响就是dump文件的大小。MINIDUMP_TYPE的定义如下:
typedef enum _MINIDUMP_TYPE {MiniDumpNormal= 0x00000000,MiniDumpWithDataSegs = 0x00000001,MiniDumpWithFullMemory = 0x00000002,MiniDumpWithHandleData = 0x00000004,MiniDumpFilterMemory = 0x00000008,MiniDumpScanMemory = 0x00000010,MiniDumpWithUnloadedModules = 0x00000020,MiniDumpWithIndirectlyReferencedMemory = 0x00000040,MiniDumpFilterModulePaths = 0x00000080,MiniDumpWithProcessThreadData= 0x00000100,MiniDumpWithPrivateReadWriteMemory= 0x00000200,MiniDumpWithoutOptionalData = 0x00000400,MiniDumpWithFullMemoryInfo = 0x00000800,MiniDumpWithThreadInfo = 0x00001000,MiniDumpWithCodeSegs = 0x00002000,MiniDumpWithoutAuxiliaryState= 0x00004000,MiniDumpWithFullAuxiliaryState = 0x00008000,MiniDumpWithPrivateWriteCopyMemory= 0x00010000,MiniDumpIgnoreInaccessibleMemory = 0x00020000,MiniDumpWithTokenInformation = 0x00040000,MiniDumpWithModuleHeaders = 0x00080000,MiniDumpFilterTriage = 0x00100000,MiniDumpWithAvxXStateContext = 0x00200000,MiniDumpWithIptTrace = 0x00400000,MiniDumpValidTypeFlags = 0x007fffff,} MINIDUMP_TYPE;
关于上面枚举值对应保存数据范围的介绍可参考MINIDUMP_TYPE详解_神知道下一秒会发生什么的博客-CSDN博客_minidump_type
下面介绍部分枚举值:
MiniDumpNormal
常选值,该类型仅包含捕获进程中所有线程的堆栈跟踪的必要信息。
MiniDumpWithDataSegs
包含来自所有加载模块中的可写数据段,此选项会使minidump文件显著增大,对每个模块的控制使用了MODULE_WRITE_FLAGS枚举类型的ModuleWriteDataSeg枚举值。如果我们希望查看全局变来那个值,但有不想使用MiniDumpWithFullMemory,可以使用此选项。
MiniDumpWithFullMemory
包含进程中所有可以访问的内存信息,原始内存信息包含在文件的末端,所以不用原始内存信息可以直接映射数据结构。但是该选项会造成minidump文件非常巨大。
使用该选项可以查看存储在栈上、堆上、模块数据段的所有数据。甚至还可以看到线程和进程环境块(Process Environment Block和Thread Environment Bolck, PEB和TEB)的数据。这些没有公开的数据结构可以给我们的调试提供无价的帮助。
MiniDumpWithHandleData
包含生成dump文件时活跃的系统句柄的高级别信息。
使用该选项minidump会包括故障时刻进程故障表里面的所有句柄。可以用WinDbg的!handle来显示这些信息。
MiniDumpFilterMemory
栈内存的内容会在保存到minidump前被过滤,除了重建栈跟踪所必须的指针,其他都会被用0数据覆盖。即调用栈可以被重建,但是所有局部变量和函数参数的值都是0。此选项只影响线程栈占用内存的内容。其他内存(比如堆)不受影响。如果使用了MiniDumpWithFullMemory,这个标志就不起作用了。
第五个参数定义如下:
typedef struct _MINIDUMP_EXCEPTION_INFORMATION {DWORD ThreadId;PEXCEPTION_POINTERS ExceptionPointers;BOOL ClientPointers;} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
上述PEXCEPTION_POINTERS 为异常捕获指针。
二、demo如下:
//创建dmp文件头文件.h#pragma once#include <string>#include <memory>using namespace std;class CCreateDump{public:CCreateDump();~CCreateDump(void);static CCreateDump* Instance();static long __stdcall UnhandleExceptionFilter(_EXCEPTION_POINTERS* ExceptionInfo);//声明Dump文件,异常时会自动生成。会自动加入.dmp文件名后缀void DeclarDumpFile(std::string dmpFileName = "");private:static std::stringm_strDumpFile;static shared_ptr<CCreateDump*> m_sptrInstance;};/创建dmp的.cpp文件#include <Windows.h>#include "CListenDump.h"#include <DbgHelp.h>#pragma comment(lib, "dbghelp.lib")std::shared_ptr<CCreateDump*> CCreateDump::m_sptrInstance = make_shared<CCreateDump*>();std::string CCreateDump::m_strDumpFile = "";CCreateDump::CCreateDump(){}CCreateDump::~CCreateDump(void){}long CCreateDump::UnhandleExceptionFilter(_EXCEPTION_POINTERS* ExceptionInfo){HANDLE hFile = CreateFile(m_strDumpFile.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if (hFile != INVALID_HANDLE_VALUE){MINIDUMP_EXCEPTION_INFORMATION ExInfo;ExInfo.ThreadId = ::GetCurrentThreadId();ExInfo.ExceptionPointers = ExceptionInfo;ExInfo.ClientPointers = FALSE;// write the dumpBOOL bOK = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL);CloseHandle(hFile);if (!bOK){DWORD dw = GetLastError();//写dump文件出错处理,异常交给windows处理return EXCEPTION_CONTINUE_SEARCH;}else{ //在异常处结束return EXCEPTION_EXECUTE_HANDLER;}}else{return EXCEPTION_CONTINUE_SEARCH;}}void CCreateDump::DeclarDumpFile(std::string dmpFileName){SYSTEMTIME syt;GetLocalTime(&syt);char szTime[MAX_PATH];sprintf_s(szTime, MAX_PATH, "[%04d-%02d-%02dT%02d-%02d-%02d]", syt.wYear, syt.wMonth, syt.wDay, syt.wHour, syt.wMinute, syt.wSecond);m_strDumpFile = dmpFileName + std::string(szTime);m_strDumpFile += std::string(".dmp");SetUnhandledExceptionFilter(UnhandleExceptionFilter);}CCreateDump* CCreateDump::Instance(){if (*m_sptrInstance == NULL){m_sptrInstance = make_shared<CCreateDump*>(new CCreateDump);}return *m_sptrInstance;}//主函数cpp文件如下:// CreateDump.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。#include <iostream>#include "CListenDump.h"#include<windows.h>#include<stdio.h>int main(){char szPath[512] = { 0 };//查找可执行文件路径,将dump文件写入可执行文件路径下GetModuleFileName(NULL, szPath, sizeof(szPath) - 1);printf("path:%s\n", szPath);std::string strFilePathTmp = szPath;std::string strDumPath = "";if (strFilePathTmp.rfind(".exe") != string::npos){int nIndex = strFilePathTmp.rfind("\\");if (nIndex != -1){strDumPath = strFilePathTmp.substr(0,nIndex+1);}}strDumPath = strDumPath + "dumpfile";CCreateDump::Instance()->DeclarDumpFile(strDumPath);int nSub = 0;int nValue = 10 / nSub;std::cout << "Hello World!\n";return 0;}
上述产生dmp文件程序编写参考于Dump文件的生成和使用_不当初-CSDN博客_dump文件
三、调试dmp文件如下:
上述demo运行产生的dmp文件使用VS打开,本文工具使用VS,
(1)设置pdb路径
pdb路径如红笔所示
(2)设置源代码路径:
(3)点击运行
调试结果如下: