Windows 程序捕获崩溃异常 生成dump

概述

事情的起因是,有个同事开发的程序,交付的版本程序,会偶尔随机崩溃了。

悲催的是没有输出log,也没有输出dump文件。

我建议他给程序代码加个异常捕获,在崩溃时生成dump,方便找出问题点。

隔了一天之后,短暂交流,发现他没有这个开发经验,我只好披挂上阵了。

开动

查阅MSDN文档,和stackoverlfow.com的相关文章,可知

SetUnhandledExceptionFilter 可以捕获触发系统崩溃的异常

风风火火开始写代码

    void exceptionHandler(PEXCEPTION_POINTERS excpInfo)
{
// your code to handle the exception. Ideally it should
// marshal the exception for processing to some other
// thread and wait for the thread to complete the job
std::unique_lock<std::mutex> lk(g_handlerLock);
generateMiniDump(nullptr, excpInfo);
} LONG WINAPI unhandledException(PEXCEPTION_POINTERS excpInfo = nullptr)
{
DebugBreak(); if (excpInfo == nullptr)
{
__try // Generate exception to get proper context in dump
{
RaiseException(EXCEPTION_BREAKPOINT, , , nullptr);
}
__except (exceptionHandler(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER)
{
}
}
else
{
exceptionHandler(excpInfo);
} return ;
} SetUnhandledExceptionFilter(unhandledException);

测试

在main函数入口,设置异常处理函数SetUnhandledExceptionFilter。

异常处理函数负责捕获异常,调用MiniDumpWriteDump生成dump文件,供开发者使用Windbg调试

编译运行

Access Volation C000005错误可以顺利捕获

令人费解的是,

abort,数组越界,虚函数调用异常等均无法捕获

系统把这些异常给拦截了,并给出了程序崩溃的提示窗口

改进

为了捕获这些异常并生成dump文件,必须要把系统拦截的那一层给禁止掉

1. 禁止系统弹出崩溃窗口,该窗口提示非常渣,对开发者和用户都不友好

  SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);

可以模仿腾讯的QQ程序,专门开发一个对用户界面友好Bug Report的程序,
在程序崩溃时转存储dump文件时,运行该程序提示用户有用的信息。

2. 注册异常捕获函数

  SetUnhandledExceptionFilter(unhandledException);

当异常发生时,系统会跳进我们的unhandleException回调中

在该回调函数中,我们可以弹出Bug Report这样的子进程,并存储异常dump文件

3. 拦截C Runtime的异常处理

  _set_invalid_parameter_handler(invalidParameter);
_set_purecall_handler(pureVirtualCall);
signal(SIGABRT, sigAbortHandler);
_set_abort_behavior(, );

这些异常处理只是简单的调用unhandleException函数

4. 开启系统的程序崩溃请求

Vista之后,微软加了一个特性

程序崩溃时,默认不交给程序崩溃处理

而是使用一个莫名其妙的机制,不让程序进入崩溃环节

搞得用户懵逼,开发者也让代码无法进入崩溃异常处理

    void EnableCrashingOnCrashes()
{
typedef BOOL(WINAPI *tGetPolicy)(LPDWORD lpFlags);
typedef BOOL(WINAPI *tSetPolicy)(DWORD dwFlags);
const DWORD EXCEPTION_SWALLOWING = 0x1; HMODULE kernel32 = LoadLibraryA("kernel32.dll");
tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32,
"GetProcessUserModeExceptionPolicy");
tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32,
"SetProcessUserModeExceptionPolicy");
if (pGetPolicy && pSetPolicy)
{
DWORD dwFlags;
if (pGetPolicy(&dwFlags))
{
// Turn off the filter
pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
}
}
}

5. 断了系统SetUnhandledExceptionFilter的后路

C Runtime等异常,运行时库会调用SetUnhandledExceptionFilter向系统注册一个NULL

从而使得我们之前注册的回调失效

真是无语(ˉ▽ˉ;)...

在这里,需要hook掉SetUnhandledExceptionFilter,在我们注册完回调之后,让它默认不做任何处理

用到Windows核心编程这本书里面,Jeffrey Richter开发的CAPIHook这个模块

    void PreventSetUnhandledExceptionFilter()
{
CAPIHook apiHook("kernel32.dll",
"SetUnhandledExceptionFilter",
(PROC)ExceptionFilterHookProc);
}

其中ExceptionFilterHookProc这个函数是个空函数,无需做多余操作,直接renturn null即可

6. 完整流程

    void setExceptionHandlers()
{
if (!IsDebuggerPresent() && !g_isHandlerSet)
{
g_isHandlerSet = true; SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
SetUnhandledExceptionFilter(unhandledException);
_set_invalid_parameter_handler(invalidParameter);
_set_purecall_handler(pureVirtualCall);
signal(SIGABRT, sigAbortHandler);
_set_abort_behavior(, ); EnableCrashingOnCrashes();
PreventSetUnhandledExceptionFilter();
}
}

在VS或者Windbg中调试时,我们就没有必要生成dump文件了

IsDebuggerPresent这个系统API会帮助我们判断我们是否在调试环境中

总结

即使看起来这么简单的一个功能,也是需要挺多细节处理的。

Windows 程序 dump 崩溃调试的更多相关文章

  1. 编写的windows程序,崩溃时产生crash dump文件的办法

    一.引言 dump文件是C++程序发生异常时,保存当时程序运行状态的文件,是调试异常程序重要的方法,所以程序崩溃时,除了日志文件,dump文件便成了我们查找错误的最后一根救命的稻草.windows程序 ...

  2. windows下dump文件调试

    dump调试:在系统中异常或者崩溃的时候,来生成dump文件,然后用调试器来调试.这样就可以在生产环境中的dmp文件,拷贝到自己的开发机器上,调试就可以找到错误的位置,配合程序调试符号pdb文件,直接 ...

  3. [转]让程序在崩溃时体面的退出之Dump文件

    原文地址:http://blog.csdn.net/starlee/article/details/6630816 在我的那篇<让程序在崩溃时体面的退出之CallStack>中提供了一个在 ...

  4. 让程序在崩溃时体面的退出之Dump文件

             在我的那篇<让程序在崩溃时体面的退出之CallStack>中提供了一个在程序崩溃时得到CallStack的方法.但是要想得到CallStack,必须有pdb文件的支持.但 ...

  5. 自定义VS程序异常处理及调试Dump文件(一)

    自定义VS程序异常处理及调试Dump文件(一) 1. Dump文件 1. Dump文件介绍 Dump文件(Dump File),也叫转储文件,以.DMP为文件后缀.dump文件是进程在内存中的镜像文件 ...

  6. [转]让程序在崩溃时体面的退出之SEH+Dump文件

    原文地址:http://blog.csdn.net/starlee/article/details/6649605 在我上篇文章<让程序在崩溃时体面的退出之SEH>中讲解了SEH中try/ ...

  7. vc/vs开发的应用程序添加dump崩溃日志<转>

    原贴地址:https://blog.csdn.net/wangkui1331/article/details/78029940 vc/vs开发的应用程序出现崩溃的时候,由于没有任何记录,导致开发人员很 ...

  8. 程序异常崩溃后用windbg辅助调试解决的经验 以及 堆栈问题调试经验

    1,程序异常崩溃后用windbg辅助调试解决的经验  状况:我的程序调用别人的库做 文件写入工作. 在这一过程中出现异常,程序崩溃. 经反复检查,认为自己的程序没有错,但无法判断在别人库里哪里有错. ...

  9. 使用Windows事件查看器调试崩溃

    本文讨论如何使用Windows事件查看器获取实际崩溃的模块以及代码中崩溃的位置.示例代码是用C++编写的,以生成不同类型的崩溃,例如访问冲突和堆栈溢出. 简介 我经常听同事和QA那里听说,一个特定的崩 ...

随机推荐

  1. spring Security简介

    它是spring的权限管理框架

  2. GitLab 使用指南(IntelliJ IDEA)

    一.环境 GitLab Community Edition 10.6.4 IntelliJ IDEA 2017.03 二.Git 使用 (Linux/MAC,cmd 模式) 本地新建项目(从Git服务 ...

  3. 雷林鹏分享:Ruby Dir 类和方法

    Ruby Dir 类和方法 Dir 是一个表示用于给出操作系统中目录中的文件名的目录流.Dir 类也拥有与目录相关的操作,比如通配符文件名匹配.改变工作目录等. 类方法 序号方法 & 描述 1 ...

  4. hdu——1873(优先队列)

    看病要排队 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  5. mysql插入中文数据报错 java.sql.SQLException: Incorrect string value: '\xE5\x90\x88\xE8\xAE\xA1' for column

    1.我们创建数据库的时候没有更改数据库的字符集为utf8. 在mysql工具中,右击数据库,->"改变数据库",->选择“基字符集”为utf-8; 2,数据库中表的字符 ...

  6. 物理内存不够用,临时增大Linux交换分区的方法

    当系统的物理内存不够用的时候,就需要将物理内存中的一部分空间释放出来,以供当前运行的程序使用.那些被释放的空间可能来自一些很长时间没有什么 操作的程序,这些被释放的空间被临时保存到Swap空间中,等到 ...

  7. Alpha阶段第1周Scrum立会报告+燃尽图 01

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2246 一.小组介绍 组长:刘莹莹 组员:朱珅莹 孙韦男 祝玮琦 王玉潘 ...

  8. 20155322 2016-2017-2 《Java程序设计》第8周学习总结

    20155322 2016-2017-2 <Java程序设计>第8周学习总结 教材学习内容总结 第八周学习的主要内容是课本的第十四第十五章,主要学习了以下知识点: 了解NIO 会使用Cha ...

  9. InputStream,InputStreamReader和Reader的关系

    InputStream:得到的是字节输入流,InputStream.read("filename")之后,得到字节流 Reader:读取的是字符流 InputStreamReade ...

  10. DecimalFormat格式化数字

    DecimalFormat格式化数字 DecimalFormat类也是Format的一个子类,主要作用是格式化数字.当然,在格式化数字时要比直接使用NumberFormat更加 方便,因为可以直接指定 ...