1. dump文件介绍

1.1 什么是dump文件

Dump 文件是进程的内存镜像 , 可以把程序的执行状态通过调试器保存到dump文件中 ;
Dump 文件是用来给驱动程序编写人员调试驱动程序用的 , 这种文件必须用专用工具软件打开 , 比如使用 WinDbg , VisualStudio 打开 ;
当我们的程序发布出去之后 , 在客户机上是无法跟踪自己代码的 BUG 的 , 所以 Dump 文件对于我们来说特别有用 ; 我们可以通过 .dmp 文件把出现 BUG 的情况再现 , 然后再现客户环境 (包括堆栈调用等情况) , 设置源码调试路径 , 可以找到出现 BUG 的语句 。

1.2 dump文件发展

在过去几年里,崩溃转储(crash dump)成为了调试工作的一个重要部分。第一代的crash dump通常被称为“全用户转储(full user dump)”,它包含了进程的虚拟内存的全部内容。但是,这样的dump经常非常大,使得通过电子方式发送给开发者非常困难,甚至没法完成。

随着Windows XP,微软发布了一组新的被称为“minidump”的崩溃转存技术。Minidump很容易定制。按照最常用的配置,一个minidump只包括了最必要的信息,用于恢复故障进程的所有线程的调用堆栈,以及查看故障时刻局部变量的值。这样的dump文件通常很小(只有几K字节)。所以,很容易通过电子方式发送给软件开发人员。一旦需要,minidump甚至可以包含比原来的crash dump更多的信息。例如,可以包含进程使用的内核对象的信息。另外,DbgHelp.dll提供了通过编程创建minidump的公开API。而且,它是可以重新发布的。我们可以不再依赖于外部工具。

minidump可以定制,也给我们带来了一个问题:保存多少应用程序状态信息才能既保证调试有效,又能够尽量保证minidump文件尽可能小?幸运的是我们可以通过DbgHelp函数组(MiniDumpWriteDump和MiniDumpCallback)来控制这些功能,甚至可以更复杂。在这篇文章里面,我们会解释怎么样使用这些函数来创建mindump,保证文件足够小但是又能有效调试。也会讲解minidump中应该包括哪些数据,并且如何使用通用调试器(WinDbg和VS.NET)来看这些信息。

2. Minidump类型

MiniDumpWriteDump的函数声明

BOOL MiniDumpWriteDump(HANDLE hProcess,      //inDWORD ProcessId,        //inHANDLE hFile,       //inMINIDUMP_TYPE DumpType,     //inPMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,     //inPMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,      //inPMINIDUMP_CALLBACK_INFORMATION CallbackParam        //in
);

使用这个函数创建简单的minidump

void CreateMiniDump(EXCEPTION_POINTERS* pep )
{//首先,打开或者创建文件HANDLE hFile = CreateFile(_T("MiniDump.dmp"), GENERIC_READ | GENERIC_WRITE,0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );if( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) ){  //然后,创建minidumpMINIDUMP_EXCEPTION_INFORMATION mdei;//一个结构体mdei.ThreadId           = GetCurrentThreadId();mdei.ExceptionPointers  = pep;mdei.ClientPointers     = FALSE;MINIDUMP_TYPE mdt       = MiniDumpNormal;BOOL rv = MiniDumpWriteDump( GetCurrentProcess(), //获取当前进程的一个伪句柄GetCurrentProcessId(),hFile, //打开的文件mdt, (pep != 0) ? &mdei : 0, 0, 0 );if( !rv )_tprintf( _T("MiniDumpWriteDump failed. Error: %u \n"), GetLastError() );else_tprintf( _T("Minidump created.\n") );// 关闭文件CloseHandle( hFile );}else_tprintf( _T("CreateFile failed. Error: %u \n"), GetLastError() );
}

在这个例子里面,我们如何指定minidump应该包括哪些数据呢?主要取决于MiniDumpWriteDump的第四个参数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,MiniDumpWithoutManagedState            = 0x00004000,
} MINIDUMP_TYPE;

MINIDUMP_TYPE枚举是一些标志,允许我们来控制minidump包含哪些内容。我们来看一下这些值的内容,以及如何使用它们。

MiniDumpNormal
MiniDumpNormal是一个特别的标志。它的值是0,意味着这个值永远隐含存在,甚至不需要显示指定。因此,我们可以假定这个标记代表了minidump中永远存在的一组基础数据集合。通过指定用户自定义的回调函数,可以过滤这些值。
下面表格显示了数据基础数据集合中的数据类型。

确实,MiniDumpNormal指定的基础信息集合非常有用。我们可以定位出现问题的指令,检查线程怎么样进入到这种状态。甚至可以产看到函数参数和局部变量的值。另外,这些信息也可以用来调试死锁,因为我们可以看到所有线程的调用栈,并且知道他们在等待什么。

同时,所有这些信息的代价非常小,minidump的大小通常不超过20KB。主要影响大小的因素的线程栈的大小-他们占用的内存越多,minidump的文件越大。

但是,如果需要调试的问题比较复杂,而不是像非法访问或者死锁这样的简单问题,我们就会发现MiniDumpNormal标记收集的信息还不够。我们有可能需要查看全局变量,但是里面没有。也有可能需要查看堆里面分配的结构体的内容,minidump也没有包括相应的堆信息。当我们需要更多的minidump数据时,就需要研究MINIDUMP_TYPE的其他成员了。

MiniDumpWithFullMemory

这可能是除了MiniDumpNormal以外使用最多的标志了。如果指定了这个标志,minidump会包含进程地址空间中所有可读页面的内容。我们可以看到应用程序分配的所有内存,这使我们有很多的调试方法。可以查看存储在栈上、堆上、模块数据段的所有数据。甚至还可以看到线程和进程环境块(Process Environment Block和Thread Environment Bolck, PEB和TEB)的数据。这些没有公开的数据结构可以给我们的调试提供无价的帮助。

使用这个标记的唯一问题是会使minidump变得很大,至少有几MByte。另外,minidump的内容里面包含了冗余信息,所有可执行模块的代码段都包含在了里面。但是很多时候,我们很容易从其他地方获得可执行代码。让我们一起来看看MINIDUMP_TYPE,是否能够找到更好的选项。

MiniDumpWithPrivateReadWriteMemory

如果指定这个标志,minidump会包括所有可读和可写的私有内存页的内容。这使我们可以察看栈、堆甚至TLS的数据。PEB和TEB也包括在里面。

这时候,minidump没有包括共享内存也的内容。也就是说,我们不能查看内存映射文件的内容。同样,可执行模块的代码和数据段也没有包括进来。不包括代码段意味着dump没有占用不需要的空间。但是,我们也没有办法查看全局变量的值。

无论如何,通过组合其他一些选项,MiniDumpWithPrivateReadWriteMemory是一个非常有用的选项。我们会在后面看到。

MiniDumpWithIndirectlyReferencedMemory

如果指定这个标志,MiniDumpWriteDump检查线程栈内存中的每一个指针。这些指针可能指向线程地址空间的其他可读内存页。一旦发现这样的指针,程序会读取指针附近1024字节的内容存到minidump中(指针前的256字节和指针后的768字节)。

3. C++生成dump文件

当程序遇到未处理的异常(主要指非指针造成)导致程序崩溃,如果在异常发生之前调用了SetUnhandledExceptionFilter()函数,异常交给函数处理。因而,在程序开始处增加SetUnhandledExceptionFilter()函数,并在函数中利用适当的方法生成Dump文件,即可实现需要的功能。

3.1 开启生成dump文件

int main()
{// 加入崩溃dump文件功能SetUnhandledExceptionFilter(ExceptionFilter);//添加回调// 使程序崩溃产生 Dump 文件int *p = NULL;*p = 1;
}

对于SetUnhandledExceptionFilter()函数,返回值有三种情况
EXCEPTION_EXECUTE_HANDLER equ 1 表示我已经处理了异常,可以优雅地结束了
EXCEPTION_CONTINUE_SEARCH equ 0 表示我不处理,于是windows调用默认的处理程序显示一个错误框,并结束
EXCEPTION_CONTINUE_EXECUTION equ -1 表示错误已经被修复,请从异常发生处继续执行

3.2 使用举例

#include "stdafx.h"
#include "Windows.h"
#include "DbgHelp.h"int GenerateMiniDump(PEXCEPTION_POINTERS pExceptionPointers)
{// 定义函数指针typedef BOOL(WINAPI * MiniDumpWriteDumpT)(HANDLE,DWORD,HANDLE,MINIDUMP_TYPE,PMINIDUMP_EXCEPTION_INFORMATION,PMINIDUMP_USER_STREAM_INFORMATION,PMINIDUMP_CALLBACK_INFORMATION);// 从 "DbgHelp.dll" 库中获取 "MiniDumpWriteDump" 函数MiniDumpWriteDumpT pfnMiniDumpWriteDump = NULL;HMODULE hDbgHelp = LoadLibrary(_T("DbgHelp.dll"));if (NULL == hDbgHelp){return EXCEPTION_CONTINUE_EXECUTION;}pfnMiniDumpWriteDump = (MiniDumpWriteDumpT)GetProcAddress(hDbgHelp, "MiniDumpWriteDump");if (NULL == pfnMiniDumpWriteDump){FreeLibrary(hDbgHelp);return EXCEPTION_CONTINUE_EXECUTION;}// 创建 dmp 文件件TCHAR szFileName[MAX_PATH] = { 0 };TCHAR* szVersion = _T("DumpDemo_v1.0");SYSTEMTIME stLocalTime;GetLocalTime(&stLocalTime);wsprintf(szFileName, L"%s-%04d%02d%02d-%02d%02d%02d.dmp",szVersion, stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond);HANDLE hDumpFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE,FILE_SHARE_WRITE | FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);if (INVALID_HANDLE_VALUE == hDumpFile){FreeLibrary(hDbgHelp);return EXCEPTION_CONTINUE_EXECUTION;}// 写入 dmp 文件MINIDUMP_EXCEPTION_INFORMATION expParam;expParam.ThreadId = GetCurrentThreadId();expParam.ExceptionPointers = pExceptionPointers;expParam.ClientPointers = FALSE;pfnMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),hDumpFile, MiniDumpWithDataSegs, (pExceptionPointers ? &expParam : NULL), NULL, NULL);// 释放文件CloseHandle(hDumpFile);FreeLibrary(hDbgHelp);return EXCEPTION_EXECUTE_HANDLER;
}LONG WINAPI ExceptionFilter(LPEXCEPTION_POINTERS lpExceptionInfo)
{// 这里做一些异常的过滤或提示if (IsDebuggerPresent()){return EXCEPTION_CONTINUE_SEARCH;}return GenerateMiniDump(lpExceptionInfo);
}int main()
{// 加入崩溃dump文件功能SetUnhandledExceptionFilter(ExceptionFilter);// 使程序崩溃产生 Dump 文件int *p = NULL;*p = 1;
}

1. C++dump编程相关推荐

  1. dump文件_零基础编程——Python文件、JSON数据存储

    摘要 书接前文: 01-<零基础编程--起步并搭建Python环境> 02-<零基础编程--变量与数据类型> 03-<零基础编程--Python循环.函数.类> 教 ...

  2. [Windows编程] 如何捕捉程序异常/crash 并生成 dump 文件

    前面介绍如如何用WinDBG 生成crash dump <WinDBG 技巧:如何生成Dump 文件(.dump 命令) >,但是用户机器上通常不安装WinDBG, 而且多数用户也不知道怎 ...

  3. Linux系统编程30:进程信号之产生信号的四种方式(Core Dump,kill,raise)

    文章目录 (1)通过按键产生信号-Core Dump (2)调用系统函数向进程发送信号 A:kill B:raise C:abort (3)由软件条件产生信号 (4)硬件异常产生信号 总结: 为了方便 ...

  4. Linux Wi-Fi 编程API介绍

    本文为<深入理解Android Wi-Fi.NFC和GPS卷>读书笔记,Android源码为Android 5.1 Linux平台上目前常用的专门针对无线网络设备编程的API有两套 最早的 ...

  5. Java并发编程71道面试题及答案

    Java并发编程71道面试题及答案 1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方 ...

  6. Java并发编程73道面试题及答案——稳了

    点击上方"方志朋",选择"置顶或者星标" 你的关注意义重大! 1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户 ...

  7. 网络编程中的关键问题总结

    网络编程中的关键问题总结 总结下网络编程中关键的细节问题,包含连接建立.连接断开.消息到达.发送消息等等: 连接建立 包括服务端接受 (accept) 新连接和客户端成功发起 (connect) 连接 ...

  8. 自学入门不在困难,初学者挑战学习Python编程30天 (三)

    坚持坚持再坚持!!! 如果文章对于你有帮助,欢迎收藏.关注.点赞与订阅专栏 有任何疑问欢迎私信 目录 第 11 天 - 函数 定义函数 声明和调用函数 无参数函数 返回值的函数 - 第 1 部分 带参 ...

  9. 初探可编程网关 Pipy

    有幸参加了 Flomesh[1] 组织的workshop,了解了他们的 Pipy 网络代理,以及围绕 Pipy 构建起来的生态.Pipy 在生态中,不止是代理的角色,还是 Flomesh 服务网格中的 ...

  10. HBase眼高手低从Shell到IDEA编程、心路笔记、踩坑过程

    HBase眼高手低从Shell到IDEA编程.心路笔记.踩坑过程 HBase眼高手低 通过shell操作Hbase Foundation 在terminal中输入hbase,就可以查看hbase命令的 ...

最新文章

  1. 深入浅出Redis五种基本数据类型
  2. mac os x常用快捷键及用法
  3. php 单一职责,PHP之单例模式(职责模式)
  4. 形态数轴上的反密码子和氨基酸
  5. Python教程:序列的增量赋值
  6. Python tab 补全
  7. java jdbc reparecall_Java Connection.prepareCall方法代碼示例
  8. 拥抱.NET Core,跨平台的轻量级RPC:Rabbit.Rpc
  9. java安全(四) JNDI
  10. JS闭包的简单理解。优缺点以及垃圾回收机制
  11. [Python] L1-026. I Love GPLT-PAT团体程序设计天梯赛GPLT
  12. 去除本机利用ssh协议登陆远程机器的痕迹
  13. 【水果识别】基于matalb GUI水果分类系统【含Matlab源码 174期】
  14. SD敢达服务器源码,《sd高达wars》金手指代码 SD高达G世纪WARS金手指
  15. Unity 预编译选项
  16. STP配置 HSRP配置 端口追踪
  17. Kubernetes核心原理(二)之Controller Manager
  18. mysql对韵母分组,基于MySQL的中文发音查询的元级实现
  19. easyPOI和导入复杂excel的各种坑
  20. 前端工程化实践总结 | QQ音乐商业化Web团队

热门文章

  1. NHibernate初学者指南(3):创建Model
  2. Intel汇编与程序设计第五版3.4定义数据小节
  3. iSCSI存储的3种连接方式
  4. Android中常用适配器理解及使用
  5. html加载本地pdf,WkHTMLtoPDF不加载本地CSS和图像
  6. c语言删除文件remove_「Python」 - 文件基本操作
  7. Python基础--02
  8. 简述linux命令的,简述linux系统以及一些简单的命令
  9. php facebook授权登录获取头像_IdentityServer4从数据库获取User登录并对Claims授权验证(五)...
  10. php 计算两点地理坐标的距离