向大厂看齐!为自己的程序增加自动转储的功能!

如果你还不清楚什么是转储文件,不知道什么时候需要转储文件,请参考转储文件系列文章的第一篇 —— 转储文件知多少。

前言

不知道各位小伙伴有没有遇到过 微信 或者 QQ 崩溃的情况。它们在崩溃的时候都会自动弹出一个对话框,提示用户上传相关文件,供开发人员分析问题的原因。有的小伙伴儿可能不太清楚我在说什么。没关系,下图就是微信崩溃后自动弹出的界面。

微信 Crash Report 界面

如果勾选了 发送错误报告(S) 按钮,点击 确定(O) 按钮后,会把收集到的文件上传给开发人员。同样的,如果勾选了 重启程序(R),点击 确定(O) 按钮后,微信会自动重启。有没有觉得很酷?我们自己的程序可以做到类似的效果吗?答案是肯定的。如果感兴趣,就请继续阅读吧!

MiniDumpWriteDump

微软提供了专门的 API 来生成转储文件,这个 API 就是 MiniDumpWriteDump()

BOOL MiniDumpWriteDump(HANDLE                            hProcess,DWORD                             ProcessId,HANDLE                            hFile,MINIDUMP_TYPE                     DumpType,PMINIDUMP_EXCEPTION_INFORMATION   ExceptionParam,PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,PMINIDUMP_CALLBACK_INFORMATION    CallbackParam
);

简单介绍下每个参数:

  • hProcess:要转储的进程句柄。

    打开进程的时候,至少需要指定 PROCESS_QUERY_INFORMATIONPROCESS_VM_READ 权限。如果需要转储 句柄信息,还需要 PROCESS_DUP_HANDLE 权限。如果需要转储线程信息,需要 THREAD_ALL_ACCESS 权限。

  • ProcessId:要转储的进程ID

    有点不太理解为什么要额外传递一个进程 ID 的参数,调用GetProcessId() 就可以根据 hProcess 获取进程 ID 了,难道是为了效率?希望有知道的小伙伴儿指点一二。

  • hFile:通过 CreateFile()API 打开的,用来保存 dump 的文件句柄。

  • DumpType:转储类型。此参数会直接影响转储文件的大小,如果想自己写一个手动收集 dump 的工具,了解下这个参数会很有用。稍后介绍。

  • ExceptionParam:指向异常信息结构 MINIDUMP_EXCEPTION_INFORMATION 的指针。如果本参数为 NULL,则转储文件中不会包含异常信息。

typedefstruct _MINIDUMP_EXCEPTION_INFORMATION {DWORD               ThreadId;PEXCEPTION_POINTERS ExceptionPointers;BOOL                ClientPointers;} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
  • UserStreamParam:指向用户自定义信息结构 MINIDUMP_USER_STREAM_INFORMATION 的指针。如果本参数为 NULL,则转储文件中不会包含用户定义的信息。

typedefstruct _MINIDUMP_USER_STREAM_INFORMATION {ULONG                 UserStreamCount;PMINIDUMP_USER_STREAM UserStreamArray;} MINIDUMP_USER_STREAM_INFORMATION, *PMINIDUMP_USER_STREAM_INFORMATION;
  • CallbackParam:指向回调例程 MINIDUMP_CALLBACK_INFORMATION 的指针。如果此参数为NULL,转储过程中不会执行任何回调例程。

typedefstruct _MINIDUMP_CALLBACK_INFORMATION {MINIDUMP_CALLBACK_ROUTINE CallbackRoutine;PVOID                     CallbackParam;} MINIDUMP_CALLBACK_INFORMATION, *PMINIDUMP_CALLBACK_INFORMATION;

本结构中的CallbackParam 是传递给回调函数的参数,用户可以指定一些自己需要传递的参数,很常见的做法。CallbackRoutine 是回调函数,类型为 MINIDUMP_CALLBACK_ROUTINE 。原型如下:

typedefBOOL(WINAPI * MINIDUMP_CALLBACK_ROUTINE) (_Inout_ PVOID CallbackParam,_In_    PMINIDUMP_CALLBACK_INPUT CallbackInput,_Inout_ PMINIDUMP_CALLBACK_OUTPUT CallbackOutput);

因为 DumpType会影响最后生成的转储文件的大小,这里介绍下这个参数。

DumpType 参数

DumpType 类型为 MINIDUMP_TYPE。以下定义摘自 10.0.18362.0 版本的 minidumpapiset.h,应该是比较全的了。如果你问我:你怎么知道 MINIDUMP_TYPE 定义在这个文件里?答案很简单:用 File Locator 搜的呗。

搜索结果
typedefenum _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,MiniDumpWithoutAuxiliaryState          = 0x00004000,MiniDumpWithFullAuxiliaryState         = 0x00008000,MiniDumpWithPrivateWriteCopyMemory     = 0x00010000,MiniDumpIgnoreInaccessibleMemory       = 0x00020000,MiniDumpWithTokenInformation           = 0x00040000,MiniDumpWithModuleHeaders              = 0x00080000,MiniDumpFilterTriage                   = 0x00100000,MiniDumpWithAvxXStateContext           = 0x00200000,MiniDumpWithIptTrace                   = 0x00400000,MiniDumpScanInaccessiblePartialPages   = 0x00800000,MiniDumpValidTypeFlags                 = 0x00ffffff,
} MINIDUMP_TYPE;

下面是每个选项的意义,主要翻译自官方帮助文档。

名称描述
MiniDumpNormal只包含调用栈相关信息
MiniDumpWithDataSegs包含已加载的模块的数据段信息,比如全局变量
MiniDumpWithFullMemory包含全部可访问的内存
MiniDumpWithHandleData包含句柄信息
MiniDumpFilterMemory过滤一些敏感信息,保护重建调用栈需要的信息
MiniDumpScanMemory扫描,以包含引用内存
MiniDumpWithUnloadedModules包含最近被卸载的模块信息
MiniDumpWithIndirectlyReferencedMemory包含未直接引用的内存
MiniDumpFilterModulePaths过滤某块的路径信息
MiniDumpWithProcessThreadData包含完整的进程和线程信息
MiniDumpWithPrivateReadWriteMemory包含页面属性为 PAGE_READWRITE 的页面
MiniDumpWithoutOptionalData不包含可选数据
MiniDumpWithFullMemoryInfo包含内存区信息
MiniDumpWithThreadInfo包含线程状态信息
MiniDumpWithCodeSegs包含所有代码和有关的内存段
MiniDumpWithoutAuxiliaryState关闭辅助内存收集
MiniDumpWithFullAuxiliaryState使用所有的内存收集器
MiniDumpWithPrivateWriteCopyMemory包含页面属性为 PAGE_WRITECOPY 的页面
MiniDumpIgnoreInaccessibleMemory忽略不可访问的页面
MiniDumpWithTokenInformation包含安全令牌相关信息。可以在调试的时候使用 "!token" 命令
MiniDumpWithModuleHeaders包含模块头相关信息
MiniDumpFilterTriage添加与筛选器分类相关的数据
MiniDumpWithAvxXStateContext
MiniDumpWithIptTrace
MiniDumpValidTypeFlags设置所有标志位

说明:

MINIDUMP_TYPE 的值是不断发展变化的(向后兼容),旧版本的 DbgHelp.dll 可能不支持某些值,具体可以参考 微软官方介绍 MINIDUMP_TYPE 的文档[1]

关于MINIDUMP_TYPE 每一项的作用更为详细的介绍请参考 Effective minidumps (Part 1)[2] 和 Effective minidumps (Part 2)[3]。真的是超级详细,强烈建议大家点开看一看!唯一的遗憾是作者写的比较早,很多新出现的标志没总结进来,但仍然是非常好的参考资料!

MiniDumpWriteDump() 可以用来生成转储文件,我们应该怎么使用呢?

使用场景

通常,我们希望在自己的程序发生异常的时候,能自动保存一份转储文件,供我们事后分析。我们需要做的大概是:捕获各种异常,在异常处理函数中判断发生的异常是否能恢复,如果不能恢复就保存转储文件和其它一些关键文件(比如,程序的配置文件,出现问题时的屏幕截图等),并一起打包并保存,然后提示用户,让用户上传我们打包好的文件供我们分析。当程序发生异常的时候,我们很难确定发生异常的进程的运行状态。所以我们需要启动一个新的进程,并在新进程中调用 MiniDumpWriteDump() 来保存异常进程的信息。

以上的操作看起来比较简单,但是处理起来,有很多细节需要考虑。比如,我们需要捕获哪些异常?怎么捕获这些异常?保存好的文件怎么上传?通过什么形式上传?等等等等…… 如果有可以直接拿来使用的框架,就太好了!别说,还真有!

相关开源库

以上的这些功能,早已经有开源软件可以用了。作为一个谦卑的程序员,尽量复用现有的轮子吧。给大家推荐一些不错的开源库。

  1. CrashRpt[4] (之前在项目里用过,如果你想自己手动实现一个类似的,可以参考此项目的代码)。

CrashRpt 官方介绍
  1. 除了 CrashRptchromium 里使用的 crashpad[5] 也是一个非常好的选择。google 出品,质量有保障。

    crashpad 的前身是 breakpad[6](根据 google 官方介绍,breakpad 使用的是进程内报告机制,不再建议大家使用)。

  2. 适用于.NET程序的 CrashReporter[7]

  3. 其它平台(AndroidiOS等)也有类似的开源库。可以在 github 上搜索 crash report。我就不截图了。

说明:

本文介绍的方法只适用于我们自己的程序。如果我们想转储其它进程,我们需要借助现有工具(当然,如果有时间和精力,也可以自己写一个)。关于抓取转储的工具的介绍,可以参考之前的文章—— 你需要知道的 N 种抓取 dump 的工具。

总结

  • 我们可以通过 MiniDumpWriteDump() 来保存转储文件。

  • 我们可以借助 CrashRpt, CrashPad 等开源框架来为我们的程序添加崩溃转储功能。

参考资料

  • 《软件调试》

  • MiniDumpWriteDump 文档[8]

  • Effective minidumps (Part 1)[2]

  • Effective minidumps (Part 2)[3]

References:

[1]

微软官方介绍 MINIDUMP_TYPE 的文档: https://docs.microsoft.com/zh-cn/windows/win32/api/minidumpapiset/ne-minidumpapiset-minidump_type

[2]

Effective minidumps (Part 1): http://www.debuginfo.com/articles/effminidumps.html

[3]

Effective minidumps (Part 2): http://www.debuginfo.com/articles/effminidumps2.html

[4]

CrashRpt: http://crashrpt.sourceforge.net/

[5]

crashpad: https://github.com/chromium/crashpad

[6]

breakpad: https://github.com/google/breakpad

[7]

CrashReporter: https://github.com/ravibpatel/CrashReporter.NET

[8]

MiniDumpWriteDump 文档: https://docs.microsoft.com/zh-cn/windows/win32/api/minidumpapiset/nf-minidumpapiset-minidumpwritedump


猜你喜欢:

转储文件系列:

转储文件知多少

你需要知道的 N 种抓取 dump 的工具

你生成的转储文件有问题吗?

JIT Debug Info 简介

调试系列:

调试实战——你知道怎么使用DebugView查看调试信息吗?

调试实战——程序CPU占用率飙升,你知道如何快速定位吗?

调试实战——崩溃在ComFriendlyWaitMtaThreadProc

调试实战——使用windbg调试崩溃在ole32!CStdMarshal::DisconnectSrvIPIDs

调试实战——调试PInvoke导致的内存破坏

调试实战——调试excel启动时死锁

调试实战——调试DLL卸载时的死锁

调试实战——调试TerminateThread导致的死锁

排错系列:

排错实战——VS清空最近打开的工程记录

排错实战——拯救加载调试符号失败的IDA

排错实战——你知道拖动窗口时只显示虚框怎么设置吗?

排错实战——解决Tekla通过.tsep安装插件失败的问题

排错实战——使用process explorer替换任务管理器

排错实战——通过对比分析sysinternals事件修复程序功能异常

欢迎留言交流

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/311987.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sqlite3 c++ VS编译生成静态库

官网 https://www.sqlite.org/download.html 下载sqlite-amalgamation和x86版本下载sqlite-dll-win32-x86、x64位版本sqlite-dll-win64-x64 解压 SQLITE-AMALGAMATION包含 shell.csqlite3.csqlite3.hsqlite3ext.hsqlite-dll-win32-x86包含 sqlite3.def sqlite3.dll建立一个空…

数据结构----单源最短路径Dijkstra

数据结构----单源最短路径Dijkstra 原理&#xff1a;参考趣学数据结构 代码&#xff1a; stack.h 栈代码 #pragma once #include<stdio.h> #define maxSize 100 typedef struct stack {int * base;int * top; }stack; bool init(stack & Stack) {//栈的初始化Stack.b…

在.NET中执行Async/Await的两种错误方法

微信公众号&#xff1a;架构师高级俱乐部关注可了解更多的编程&#xff0c;架构知识。问题或建议&#xff0c;请公众号留言;如果你觉得此文对你有帮助&#xff0c;欢迎转发在.NET中执行异步/等待的两种错误方法 在应用开发中&#xff0c;我们为了提高应用程序的吞吐能力或者异步…

C#如何安全、高效地玩转任何种类的内存之Span的秉性特点(二)

前言读完上篇《C#如何安全、高效地玩转任何种类的内存之Span的本质(一)》&#xff0c;相信大家对span的本质应该非常清楚了。含着金钥匙出生的它&#xff0c;从小就被寄予厚望要成为.NET下编写高性能应用程序的重要积木&#xff0c;而且很多老前辈为了接纳它&#xff0c;都纷纷…

word List 05

word List 05 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

《ASP.NET Core 微服务实战》-- 读书笔记(第6章)

第 6 章 事件溯源与 CQRS在本章&#xff0c;我们来了解一下随着云平台一同出现的设计模式我们先探讨事件溯源和命令查询职责分离&#xff08;CQRS&#xff09;背后的动机与哲学事件溯源简介事实由事件溯源而来我们大脑就是一种事件溯源系统&#xff0c;接收感官多种形式刺激&am…

数据结构----快速排序

数据结构----快速排序 原理&#xff1a;参考趣学数据结构 代码&#xff1a; #include<stdio.h> #include<stdlib.h> int quickSort(int a[], int l, int h) {//快速排序int i l, j h, p a[l];while (i < j) {while (i<j&&a[j]>p) {//从右往左…

编译调试 .NET Core 5.0 Preview 并分析 Span 的实现原理

很久没有写过 .NET Core 相关的文章了&#xff0c;目前关店在家休息所以有些时间写一篇新的????。这次的文章主要介绍如何在 Linux 上编译调试最新的 .NET Core 5.0 Preview 与简单分析 Span 的实现原理。微软从 .NET Core 5.0 开始把 GIT 仓库 coreclr 与 corefx 合并移动…

数据结构----归并排序

数据结构----归并排序 原理&#xff1a;参考趣学数据结构 代码&#xff1a; #include<stdio.h> #include<stdlib.h> #define N 100 void guiBingSort(int a[], int l, int h,int length) {//归并排序int mid (l h) / 2;int* b (int *)malloc(N*sizeof(int));i…

利用obfuscar对.NET应用进行混淆

背景发布客户端程序产品时&#xff0c;免不了会遇到一些怀有恶意或有强烈学习欲望的用户尝试对程序进行反编译。对于一些编译成本地指令的程序&#xff08;如C、C&#xff09;&#xff0c;编译后可读性低&#xff0c;反编译和破解成本较高&#xff0c;不需要对代码进行太高强度…

数据结构---基数排序

数据结构—基数排序 原理&#xff1a;参考趣学数据结构 代码&#xff1a; #include<stdio.h> #include<stdlib.h> int getNumberBit(int number) {//获取数字的位数int x number,count0;if (x 0)return 1;while (x ! 0) {count;x / 10;}return count; } int g…

C# 版本 疫情传播仿真程序

前言前一阵子看到有人制作了《疫情传播仿真程序》&#xff0c;是用 Java做的。里面根据多种实际情况&#xff0c;如居民移动意愿、医护能力、病毒传播能力&#xff0c;来模拟疫情的发展。看完之后&#xff0c;我暗暗称奇&#xff0c;特别是结合一些视频和照片&#xff0c;确实做…

jmeter 加密解密_使用Jmeter对SHA1加密接口进行性能测试

机会只留给那些有准备的人改变能改变的&#xff0c;接受不能改变的&#xff0c;就是进步性能测试过程中&#xff0c;有时候会遇到需要对信息头进行加密鉴权&#xff0c;下面我就来介绍如何针对SHA1加密鉴权开发性能测试脚本 1、首先了解原理&#xff0c;就是需要对如下三个参数…

word List 06

word List 06 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

面对疫情,在家办公的程序员如何突围

作者&#xff1a;陌北有棵树&#xff0c;架构师社区合伙人很多程序员朋友都已经开始了在家办公的生活&#xff0c;第一天办公&#xff0c;你的远程工具还流畅吗&#xff0c;视频会议换了几个软件&#xff1f;当然这些都是外在因素&#xff0c;尤其对于程序员来说&#xff0c;解…

[蓝桥杯2018初赛]全球变暖-dfs,bfs,连通块

解题思路: bfs:遍历所有未遍历过的陆地&#xff0c;通过bfs计算出当前位置连通陆地的数量cnt&#xff0c;以及被淹没陆地的数量bound,若cnt bound表示完整淹没的一个岛屿 dfs:将连通块全部标记&#xff0c;如果这个连通块全部都会淹没&#xff0c;则答案1&#xff0c;如果这个…

latex 参考文献显示问号_回「LaTeX 的罪与罚」

原文链接&#xff1a;LaTeX 的罪与罚 - 朴素的贝叶斯的文章 - 知乎作为 LaTeX 开发者&#xff0c;看到这种嘲讽自然是非常 angry 的。本来并不想趟这个混水&#xff0c;然而眼见着赞数一天天涨上去&#xff0c;还居然进了精华区&#xff0c;实在忍不住只好注册了贵乎来说几句。…

疫情之下,使用FRP实现内网穿透,远程连接公司电脑进行办公

当前情况下&#xff0c;经常会有需要到公司电脑进行一些操作&#xff0c;比如连接内网OA&#xff0c;数据库或者提交文档。为了减少外出&#xff0c;将使用frp进行内网穿透的方法进行一个说明。前提条件1. 一台拥有公网 IP 的设备(如果没有&#xff0c;服务器可以使用https://d…

ad中电容用什么封装_二极管在电路中到底做什么用的

所有的电子电路中基本上都会用到二极管&#xff0c;它的特性也是非常之多&#xff0c;最主要就是单方向导电性&#xff0c;(单向导电性的两根引脚之间的电阻分为正向电阻和反向电阻两种)。人们利用这些不同特性构成各种具体的应用电路&#xff0c;分析不同电路中的二极管工作原…

数据结构---邻接矩阵的DFS

数据结构—邻接矩阵的DFS 原理&#xff1a;参考趣学数据结构 代码&#xff1a; #include<stdio.h> #include<stdlib.h> #define N 100 #define elemType int //const int MAX_INT (1 << 31) - 1; //const int MAX_INT 0X7fffffff; #define INF (((uns…