Windows下生成dump文件的三种方式

这篇具有很好参考价值的文章主要介绍了Windows下生成dump文件的三种方式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

提示:本文为描述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 启用Dump

reg 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::DumpFileManager

int 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函数。可以多研究研究。文章来源地址https://www.toymoban.com/news/detail-403456.html

到了这里,关于Windows下生成dump文件的三种方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Java创建文件的三种方式

    Java创建文件的三种方式

    内容来自于韩顺平学Java 在学习其视频下跟着编写 文件创建成功

    2024年04月11日
    浏览(19)
  • sql文件导入数据库的三种方式

    sql文件导入数据库的三种方式

    方式一:在数据库的表中直接右键运行sql文件    方式二:终端窗口 cmd,管理员身份运行 C:WINDOWSsystem32mysql -uroot -p 输入密码 use 数据库名; mysql use reggie; source sql文件路径(不能含有中文) 方式三:在数据库或终端输入sql指令 新建查询--输入sql语句--运行

    2024年02月03日
    浏览(22)
  • linux:文件替换的三种方式sed、awk、perl

    linux:文件替换的三种方式sed、awk、perl

    linux 文件内容替换,网上看了下大致就这三种 sed、awk、perl,今天挨个使用一下看看怎么样 语法 Linux sed 命令是利用脚本来处理文本文件。详细文档 搭配 find 可以对文件夹进行查找替换: find ./ -name \\\"*.js\\\" | xargs sed -i \\\'\\\' \\\'s/aaa/hhh/g\\\' 问题 一般在 linux 上该命令就可以生效。 但是我

    2024年02月03日
    浏览(13)
  • Spring Boot获取resources目录下的文件的三种方式

    在Spring Boot项目中,经常需要获取 resources 目录下的文件。这些文件可以包括配置文件、模板文件、静态资源等。本文将介绍三种常用的方法来获取 resources 目录下的文件。 ResourceLoader 接口是Spring框架提供的用于加载各种资源的接口,包括 classpath 下的资源。在Spring Boot中,可

    2024年02月16日
    浏览(14)
  • mysql查询结果命令行方式导出/输出/写入到文件的三种方法

    mysql查询结果命令行方式导出/输出/写入到文件的三种方法

    直接执行命令: 在目录/tmp/下会产生文件test.xls 遇到的问题: 可能原因:mysql没有向/data/下写的权限 查询都自动写入文件: 跳出mysql命令行

    2024年02月11日
    浏览(12)
  • uni-app实现上传文件至云存储的三种方式

    uni-app实现上传文件至云存储的三种方式

    目录 前言 1.在uniCloud WEB控制台中可以直接上传文件 2.客户端api上传或者组件 组件上传 客户端手动api上传 3. 云函数上传文件到云存储 总结 开发者使用 uniCloud 的云存储,无需再像传统模式那样单独去购买存储空间、CDN映射、流量采购等,本篇文章主要讲解如何使用uni-app实现

    2024年02月08日
    浏览(15)
  • C#蓝牙连接及传输数据的三种方式(蓝牙传输文件、二进制数据)

    C#蓝牙连接及传输数据的三种方式(蓝牙传输文件、二进制数据)

          先下载InTheHand.Net.Personal.dll并在C#中引用,这个需要在网上下载      先看界面            这种方式优点是稳定性较强,基本无错误,就是偶尔需要提前蓝牙配对。        这种方式直接与蓝牙设备进行配对的时候会报错,请求的地址无效,这时候需要在被检测的蓝牙

    2024年02月11日
    浏览(14)
  • 分布式文件服务器——Windows环境MinIO的三种部署模式

    分布式文件服务器——Windows环境MinIO的三种部署模式

    上节简单聊到MinIO:分布式文件存储服务——初识MinIO-CSDN博客,但没具化,本节开始展开在Windows环境下 MinIO的三种部署模式: 单机单节点、单机纠删码、集群模式 。 部署的几种模式简要概括 所谓单机单节点模式:即MinIO服务只部署到一台机器上,且只挂载一块磁盘(目录)

    2024年02月07日
    浏览(20)
  • 还原Sql Server数据库BAK备份文件的三种方式及常见错误

    还原Sql Server数据库BAK备份文件的三种方式及常见错误

    这是演示的是Sql Server 2008R2版本,不同版本可能有细微差别 右键点击数据库→还原数据库    在还原的源中选择源设备→点击选择框  在指定备份中点击添加→选择具体文件→确定→确定  勾选用于还原的备份集→这时目标数据库中会自动生成目标数据库名,在此选择即可→

    2023年04月08日
    浏览(46)
  • uni-app小程序引入iconfont的三种方式详解(无需下载文件到项目)

    uni-app小程序引入iconfont的三种方式详解(无需下载文件到项目)

    官网iconfont的引入方式有三种分别为: Unicode 、 Font class 、 Symbol , 其中已明确说明 Unicode、Font class 这两种引入方式 不支持多色 。 单色如何理解呐?具体请看效果对比图(左图为UI上传的图标样式,右边为我们使用这两种方式引入后不加任何样式的效果) 是不是很丑? 另外

    2024年02月09日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包