文章目录

  • 前言
  • 一、什么是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文件的三种方式相关推荐

  1. JVM 在遇到OOM(OutOfMemoryError)时生成Dump文件的三种方式

    JVM 在遇到OOM(OutOfMemoryError)时生成Dump文件的三种方式,以及如何使用Eclips Memory Analyzer(MAT)插件进行堆内存分析. 方法一: jmap -du ...

  2. java oom dump_JVM 在遇到OOM(OutOfMemoryError)时生成Dump文件的三种方式

    JVM 在遇到OOM(OutOfMemoryError)时生成Dump文件的三种方式,以及如何使用Eclips Memory Analyzer(MAT)插件进行堆内存分析. 方法一: jmap -du ...

  3. ios 应用和电脑共享文件夹_堪比AirDrop,苹果 iPhone与Windows电脑互传文件的三种方式...

    如果你是苹果全家桶用户,一定会对 「AirDrop(隔空投送)」 功能赞誉有加,使用 AirDrop 可以在 iPhone 与 MacBook.iPad 等设备之间快速传递照片.视频或文件. 遗憾的是 ...

  4. python生成excel文件的三种方式

    在我们做平常工作中都会遇到操作excel,那么今天写一篇,如何通过python操作excel.当然python操作excel的库有很多,比如pandas,xlwt/xlrd,openpyxl等,每个库 ...

  5. windows和虚拟机互传文件的三种方式

    大家好,在平时学习工作的时候可能有这样的需求:要将windows中的文件传到虚拟机中或者将虚拟机的文件传到windows,大家都是怎么实现的呢? 今天给大家介绍下windows和虚拟机互传文件的三种方 ...

  6. KEIL / MDK生成BIN文件的两种方式

    KEIL / MDK生成BIN文件的两种方式 1 KEIL工程配置入口 点击"魔术棒"图标(Option for Target) 在After Build/Rebuild选项卡中, ...

  7. 生成随机字符串(三种方式)

    生成随机字符串(三种方式) 一.org.apache.commons.lang包下有一个RandomStringUtils类,其中有一个randomAlphanumeric(int length)函数 ...

  8. 创建文件的三种方式和一些操作方法

    package IO;import org.junit.jupiter.api.Test; import java.io.File; import java.io.IOException;/*** @ ...

  9. ubuntu创建文件的三种方式

    ubuntu下创建文件的三种方式 1.touch命令:touch 文件名.后缀 在当前工作目录底下新建一个文件,如 touch velocity_publisher.cpp 2.gedit命令:ged ...

最新文章

  1. C语言与OpenCL的编程示例比较
  2. 关于Java中各种修饰符与访问修饰符的说明
  3. 弱电工程网络传输基础知识讲解
  4. 【JavaScript脚本】——T1基本语法——重点笔记
  5. 案例 录入并获取一个班级的平均分
  6. exp imp 及参数
  7. 浮动float的一些规则
  8. qmenu qt 关闭,Qt实现点击菜单项后QMenu不关闭功能
  9. mysql+基本代码_PHP+MySQL扎实基本功十句话_php
  10. 国外NetDevOps资源工具清单分享
  11. 论文阅读笔记——拥塞控制算法PCC
  12. 物联网技术的基站能耗监控解决方案
  13. Linux搭建Nacos及Nacos集群
  14. Linux用户对文件进行加密
  15. php网站系统设计的原则,php设计模式的六大原则(一):单一职责原则
  16. Java对象数组的初始化
  17. CrystalDiskInfo硬盘检测工具 标准版及萌妹版
  18. 元启发式如何跳出局部最优?
  19. php behaviors,在PHP的Yii框架中使用行为Behaviors的方法
  20. 即时通信---环信SDK(IOS)使用教程

热门文章

  1. Java之初步识别网络编程:IP、端口号、TCP/UDP、Socket、URL等
  2. Python中采用scapy来构建IP数据包
  3. 扶贫工作-结对帮扶公示牌 自动化填写
  4. 微信小程序--操作示例2
  5. MySQL 数据库基础
  6. 如何下载tomcat各个版本
  7. java编写md5加密解密算法
  8. 常用CASE工具介绍 ZZ
  9. 360浏览器模拟百度搜索引擎蜘蛛访问
  10. python 频数表_python里计算每个数字出现的频数