Windows下生成dump文件的三种方式
文章目录
- 前言
- 一、什么是dump文件?
- 概述
- dump分类
- 内核模式Dump
- 用户模式Dump
- 二、dump生成方式
- 实现方法
- 方法一:修改注册表
- 参数说明
- 方法二:生动创建转储文件
- 方法三:通过代码设置异常回调函数
- 设计一个记录dump功能的类
- dumpFileManager.h
- dumpFileManager.cpp
- SetUnhandledExceptionFilter函数说明
- 方法使用
- main.cpp
- 技术说明
- 总结
前言
提示:本文为描述windows平台下的dump文件生成:
windows程序当遇到异常,没有try-catch或者try-catch也无法捕获到的异常时,程序就会自动退出。
windows系统默认是不产生程序dmp文件的。dump文件是C++程序发生异常时,保存当时程序运行状态的文件。 是调试异常程序重要的方法。
一、什么是dump文件?
概述
简单来说
就是程序崩溃时,产生的文件,它记录着程序崩溃时侯的一些信息、片段。
复杂来说
Dump文件是内存转储文件,也称为内存快照文件(内存镜像)。它是一个进程或系统在某一给定的时间的快照。比如在进程崩溃时或则进程有其他问题时(比如蓝屏),甚至是任何时候,我们都可以通过工具将系统或某进程的内存备份出来供调试分析用。dump文件中包含了程序运行的模块信息、线程信息、堆栈调用信息、异常信息等数据,方便系统技术人员进行错误排查。
dump分类
分类:
Windows下Dump文件分为两大类:内核模式Dump和用户模式Dump。
内核模式Dump
是操作系统创建的崩溃转储,最经典的就是系统蓝屏,这时候会自动创建内核模式的Dump。
如果你抓取整个系统的内存dump文件, 那么你抓取的是内核态的dump文件。
用户模式Dump
如果你抓一个进程的dump文件, 那么你抓取的是用户态的dump文件。
进一步可以分为完整Dump(Full Dump)和迷你Dump(Minidump)。
Full Dump:包含了某个进程完整的地址空间数据,以及许多用于调试的信息
Minidump:随着Windows XP,微软发布了一组新的被称为“minidump”的崩溃转存技术。Minidump很容易定制。按照最常用的配置,一个minidump只包括了最必要的信息,用于恢复故障进程的所有线程的调用堆栈,以及查看故障时刻局部变量的值。这样的dump文件(.dmp)通常很小(只有几KB)。所以,很容易通过电子方式发送给软件开发人员。一旦需要,minidump甚至可以包含比原来的crash dump更多的信息。minidump可以定制,给我们带来了一个问题,保存多少应用程序状态信息才能既保证调试有效,又能够尽量保证minidump文件尽可能小?尽管调试简单的异常访问只需要调用堆栈和局部变量的信息,但是解决更复杂的问题需要更多的信息。例如,我们可能需要查看全局变量的值、检查堆的完整性和分析进程虚拟内存的布局。同时,可执行程序的代码段往往是多余的,开发用的机器上可以很容易找到这些执行程序。
二、dump生成方式
实现方法
方法一:修改注册表
使用管理员权限,执行一下脚本内容,运行后: 任何程序崩溃都会在C:\xxx 产生dmp文件(full dmp)。
[bat脚本示例]:
@echo 启用Dumpreg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps"
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpFolder /t REG_EXPAND_SZ /d "C:\xxx" /f
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpType /t REG_DWORD /d 2 /f
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpCount /t REG_DWORD /d 10 /f@echo Dump已经启用
参数说明
DumpFolder
dump文件生成路径 eg:C:\xxx
DumpType
0 = Create a custom dump //自定义dump
1 = Mini dump //mini核心dump文件
2 = Full dum //所有dump文件,产生的文件较大
DumpCount
默认:10
方法二:生动创建转储文件
当windows应用程序出现无响应时,可以打开任务管理器,找到无响应的进程,右键选择创建转储文件即可,即生成崩溃对应的dmp文件,该文件一般位于 C:\Users\happy\AppData\Local\Temp目录(显然这需要用户自己动手,不合适)
方法三:通过代码设置异常回调函数
使用windows系统api,程序中加入存储Dump的代码。通过SetUnhandledExceptionFilter
设置捕获dump的入口,然后通过MiniDumpWriteDump生成dump文件
设计一个记录dump功能的类
代码如下(示例):
dumpFileManager.h
#ifndef __DUMPFILEMANAGER_H__
#define __DUMPFILEMANAGER_H__#include <string>
//#include "safeQuit.h" //自定义的事件不需要可以不加
#include <Windows.h>namespace koal {namespace ztaClient {namespace dump {class DumpFileManager {public:static DumpFileManager* Instance() {static DumpFileManager ins;return &ins;}public:DumpFileManager();~DumpFileManager();public:// 设置dump文件路径名void* setDumpFilePath(const char* rootPath, const char* pAppName);// 注册异常处理回调函数void installDumpCollect(void* pInstallEvent = NULL);// 注册程序安全退出回调函数void installProcessQuit();public:// 判断是否为需要的数据区域BOOL isDataSectionNeeded(const WCHAR* pModuleName);// 创建小转储文件BOOL createMiniDump(PEXCEPTION_POINTERS pep, LPCTSTR strFileName);// 回调函数static LONG CALLBACK unhandledExceptionFilterEx(PEXCEPTION_POINTERS pException);private:// 要生成的小转储文件文件名称std::string exeDumpFilePath;std::string dumpRootPath;// 事件指针void* pEvent;
};
} // namespace dump
} // namespace ztaClient
} // namespace koal
#endif
dumpFileManager.cpp
#include "dumpFileManager.h"
#include "io.h"
#include <DbgHelp.h>
#include <time.h>#pragma comment(lib, "dbghelp.lib")namespace koal {namespace ztaClient {namespace dump {BOOL CALLBACK miniDumpCallback(PVOID pParam, const PMINIDUMP_CALLBACK_INPUT pInput, PMINIDUMP_CALLBACK_OUTPUT pOutput) {if (pInput == 0 || pOutput == 0) {return FALSE;}switch (pInput->CallbackType) {case ModuleCallback:if (pOutput->ModuleWriteFlags & ModuleWriteDataSeg) {if (!(DumpFileManager::Instance()->isDataSectionNeeded(pInput->Module.FullPath))) {pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);}}case IncludeModuleCallback:case IncludeThreadCallback:case ThreadCallback:case ThreadExCallback:return TRUE;default:break;}return FALSE;
}LONG CALLBACK DumpFileManager::unhandledExceptionFilterEx(PEXCEPTION_POINTERS pException) {if (NULL == pException) {return EXCEPTION_CONTINUE_SEARCH;}int ret = access(Instance()->dumpRootPath.c_str(), 0);if (-1 == ret) {CreateDirectory(LPCSTR(Instance()->dumpRootPath.c_str()), NULL);}if (Instance()->createMiniDump(pException, Instance()->exeDumpFilePath.c_str())) {MessageBox(NULL, LPCSTR("程序出错,创建小转储文件成功"), LPCSTR("致命错误"), MB_OK | MB_ICONINFORMATION);} else {MessageBox(NULL, LPCSTR("程序出错,创建小转储文件成功"), LPCSTR("致命错误"), MB_OK | MB_ICONINFORMATION);}if (Instance()->pEvent) {Instance()->installProcessQuit();}return EXCEPTION_CONTINUE_SEARCH;
}DumpFileManager::DumpFileManager() { pEvent = NULL; }DumpFileManager::~DumpFileManager() {}BOOL DumpFileManager::isDataSectionNeeded(const WCHAR* pModuleName) {WCHAR szFileName[_MAX_FNAME] = L"";if (NULL == pModuleName) {return FALSE;}_wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);if (_wcsicmp(szFileName, L"ntdll") == 0) {return TRUE;}return FALSE;
}BOOL DumpFileManager::createMiniDump(PEXCEPTION_POINTERS pep, LPCTSTR strFileName) {MINIDUMP_EXCEPTION_INFORMATION mdei;MINIDUMP_CALLBACK_INFORMATION mci;HANDLE hFile = NULL;if (NULL == pep || NULL == strFileName) {return FALSE;}hFile = CreateFile(strFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) {mdei.ThreadId = GetCurrentThreadId();mdei.ExceptionPointers = pep;mdei.ClientPointers = FALSE;mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)miniDumpCallback;mci.CallbackParam = NULL;MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &mdei, NULL, &mci);CloseHandle(hFile);return TRUE;}return FALSE;
}void DumpFileManager::installDumpCollect(void* pInstallEvent) {if (pInstallEvent) { // 注册了事件pEvent = pInstallEvent;}SetUnhandledExceptionFilter(unhandledExceptionFilterEx);
}void* DumpFileManager::setDumpFilePath(const char* rootPath, const char* pAppName) {if (NULL == rootPath || NULL == pAppName) {return FALSE;}char TIME[64] = {0};char DAY[64] = {0};SYSTEMTIME sys;GetLocalTime(&sys);sprintf(TIME, "%02d_%02d_%02d", sys.wHour, sys.wMinute, sys.wSecond);time_t curTm;time(&curTm);struct tm pTm = *localtime(&curTm);sprintf(DAY, "%04d%02d%02d", pTm.tm_year + 1900, pTm.tm_mon + 1, pTm.tm_mday);char exeName[256] = {0};sprintf(exeName, "%s.mini_%s_%s.dmp", pAppName, DAY, TIME);std::string dumpFilePath = rootPath;std::string fileName = exeName;dumpRootPath = dumpFilePath;exeDumpFilePath = dumpFilePath + "\\" + fileName;return (void*)this;
}
//程序崩溃后需要做的事情,自定义,不需要可以不加
void DumpFileManager::installProcessQuit() {//koal::ztaClient::safeQuit* reg = (koal::ztaClient::safeQuit*)pEvent;//reg->sendMsg();
}} // namespace dump
} // namespace ztaClient
} // namespace koal
SetUnhandledExceptionFilter函数说明
简单使用SetUnhandledExceptionFilter()函数让程序优雅崩溃
最后网上查了一番,发现SetUnhandledExceptionFilter这个函数解决了一切。
设置异常捕获函数:
当异常没有处理的时候,系统就会调用SetUnhandledExceptionFilter所设置异常处理函数。例如一些程序
在出错的时候,会向用户报告说程序那出错就是利用这个.例如QQ..异常处理中的一部分
当发生异常时,比如内存访问违例时,CPU硬件会发现此问题,并产生一个异常(你可以把它理解为中断)
然后CPU会把代码流程切换到异常处理服务例程。操作系统异常处理服务例程会查看当前进程是否处于调试状态
如果是,则通知调试器发生了异常,如果不是则操作系统会查看当前线程是否安装了的异常帧链(FS[0])
如果安装了SEH(try.... catch....),则调用SEH,并根据返回结果决定是否全局展开或局部展开。
如果异常链中所有的SEH都没有处理此异常,而且此进程还处于调试状态,则操作系统会再次通知调试器发生异常
(二次异常)。如果还没人处理,则调用操作系统的默认异常处理代码UnhandledExceptionHandler
不过操作系统允许你Hook这个函数,就是通过SetUnhandledExceptionFilter函数来设置。
大部分异常通过此种方法都能捕获,不过栈溢出、覆盖的有可能捕获不到。
总结了下搜到的资料,这个函数的返回值有三种情况:
EXCEPTION_EXECUTE_HANDLER equ 1 表示我已经处理了异常,可以优雅地结束了
EXCEPTION_CONTINUE_SEARCH equ 0 表示我不处理,其他人来吧,于是windows调用默认的处理程序显示一个错误框,并结束
EXCEPTION_CONTINUE_EXECUTION equ -1 表示错误已经被修复,请从异常发生处继续执行
方法使用
在需要需用的cpp里,直接调用就行
代码如下(示例):
main.cpp
#include "dumpFileManager.h"
#define KOAL_ZTA_DUMP koal::ztaClient::dump::DumpFileManagerint main(int argc, char *argv[]){//dump文件生成的路径std::string dumpPath = "D:\\test";//根据自己需求改(注意windows下目录为\\双斜杠)// 崩溃记录((KOAL_ZTA_DUMP *)KOAL_ZTA_DUMP::Instance()->setDumpFilePath(dumpPath.c_str(), "test.exe"))->installDumpCollect(/*(void *)reg 不需要注册事件可以不传入回调函数的地址,函数默参数为NULL*/);//休眠五秒,五秒后程序崩溃。Sleep(5000);//这段代码可以使程序崩溃,作为测试用_asm int 3;
}
技术说明
知识点:
- windows的api函数
- 回调函数
- 指针函数
开发环境
- vs2010
编码格式
- GB2312
(记得在vscode中或者记事本中设置代码的编码格式为GB2312,用UTF-8的话,弹窗中文会乱码)
如果编译报错,可以试着修改一下vs的配置。我这边代码是没有问题的。
总结
例如:以上就是今天要讲的内容,本文仅仅简单介绍了windows下dump的生成,其中用到了大量的windows的api函数。可以多研究研究。
Windows下生成dump文件的三种方式相关推荐
- JVM 在遇到OOM(OutOfMemoryError)时生成Dump文件的三种方式
JVM 在遇到OOM(OutOfMemoryError)时生成Dump文件的三种方式,以及如何使用Eclips Memory Analyzer(MAT)插件进行堆内存分析. 方法一: jmap -du ...
- java oom dump_JVM 在遇到OOM(OutOfMemoryError)时生成Dump文件的三种方式
JVM 在遇到OOM(OutOfMemoryError)时生成Dump文件的三种方式,以及如何使用Eclips Memory Analyzer(MAT)插件进行堆内存分析. 方法一: jmap -du ...
- ios 应用和电脑共享文件夹_堪比AirDrop,苹果 iPhone与Windows电脑互传文件的三种方式...
如果你是苹果全家桶用户,一定会对 「AirDrop(隔空投送)」 功能赞誉有加,使用 AirDrop 可以在 iPhone 与 MacBook.iPad 等设备之间快速传递照片.视频或文件. 遗憾的是 ...
- python生成excel文件的三种方式
在我们做平常工作中都会遇到操作excel,那么今天写一篇,如何通过python操作excel.当然python操作excel的库有很多,比如pandas,xlwt/xlrd,openpyxl等,每个库 ...
- windows和虚拟机互传文件的三种方式
大家好,在平时学习工作的时候可能有这样的需求:要将windows中的文件传到虚拟机中或者将虚拟机的文件传到windows,大家都是怎么实现的呢? 今天给大家介绍下windows和虚拟机互传文件的三种方 ...
- KEIL / MDK生成BIN文件的两种方式
KEIL / MDK生成BIN文件的两种方式 1 KEIL工程配置入口 点击"魔术棒"图标(Option for Target) 在After Build/Rebuild选项卡中, ...
- 生成随机字符串(三种方式)
生成随机字符串(三种方式) 一.org.apache.commons.lang包下有一个RandomStringUtils类,其中有一个randomAlphanumeric(int length)函数 ...
- 创建文件的三种方式和一些操作方法
package IO;import org.junit.jupiter.api.Test; import java.io.File; import java.io.IOException;/*** @ ...
- ubuntu创建文件的三种方式
ubuntu下创建文件的三种方式 1.touch命令:touch 文件名.后缀 在当前工作目录底下新建一个文件,如 touch velocity_publisher.cpp 2.gedit命令:ged ...
最新文章
- C语言与OpenCL的编程示例比较
- 关于Java中各种修饰符与访问修饰符的说明
- 弱电工程网络传输基础知识讲解
- 【JavaScript脚本】——T1基本语法——重点笔记
- 案例 录入并获取一个班级的平均分
- exp imp 及参数
- 浮动float的一些规则
- qmenu qt 关闭,Qt实现点击菜单项后QMenu不关闭功能
- mysql+基本代码_PHP+MySQL扎实基本功十句话_php
- 国外NetDevOps资源工具清单分享
- 论文阅读笔记——拥塞控制算法PCC
- 物联网技术的基站能耗监控解决方案
- Linux搭建Nacos及Nacos集群
- Linux用户对文件进行加密
- php网站系统设计的原则,php设计模式的六大原则(一):单一职责原则
- Java对象数组的初始化
- CrystalDiskInfo硬盘检测工具 标准版及萌妹版
- 元启发式如何跳出局部最优?
- php behaviors,在PHP的Yii框架中使用行为Behaviors的方法
- 即时通信---环信SDK(IOS)使用教程