C++程序中dump文件生成方法详解

news2024/9/24 18:38:45

最近项目中新作成了一个动态链接库,长时间运行后,偶尔会崩溃。根据log分析,被调用的动态库函数最外层catch到了这个异常,但是不能定位哪里出了问题。另外虽然上层exe是有dump文件输出处理的,但是在C++中,如果异常被捕获并处理的情况下,系统就不会生成dump文件了。如果仍希望在try-catch块中捕获异常的同时生成dump文件,就必须在catch块中手动调用生成dump文件的函数。这样可以在异常被捕获后仍然生成dump文件以供后续分析。本文详细介绍下怎么生成dump文件。


链接库

Windows平台上用Windows调试帮助库中的函数MiniDumpWriteDump来生成dump文件。

dbghelp.h头文件

#include <dbghelp.h>

dbghelp.lib库

项目【属性】→【链接器】→【输入】→【附加依赖项】→ 添加dbghelp.lib
在这里插入图片描述


dump生成原理:

生成dump文件的原理是在未处理异常发生时,系统调用设置的未处理异常过滤器回调函数,并将异常信息传递给这个回调函数。回调函数可以利用这些异常信息,如异常指针和线程ID,来创建一个包含应用程序状态快照的dump文件。这个快照包括了应用程序的内存、寄存器状态、堆栈信息等,可以帮助开发人员在应用程序崩溃时进行调试和分析。

SetUnhandledExceptionFilter函数是Windows平台上用于设置未处理异常过滤器的函数。
MiniDumpWriteDump函数是Windows平台上用于输出dump文件的函数。


代码实例

dump文件名和输出路径设置

dump文件名和输出路径没有什么硬性要求,根据各自程序的需求来即可。

本文
dump文件名:当前模块名+当前时间
dump输出路径:当前模块所在同级目录

// 获取当前模块的路径
std::string GetCurrentModuleFilePath()
{
	char buffer[MAX_PATH];
	GetModuleFileName(nullptr, buffer, MAX_PATH);
	return std::string(buffer);
}

// 获取当前模块的名字(不含后缀)
std::string GetModuleName(const std::string& filePath)
{
	std::string fileName = "";

	size_t lastSlash = filePath.find_last_of("\\");
	size_t lastDot = filePath.find_last_of(".");
	if (lastSlash != std::string::npos 
		&& lastDot != std::string::npos
		&& lastDot > lastSlash)
	{
		fileName = filePath.substr(lastSlash + 1, lastDot - lastSlash - 1);
	}

	return fileName;
}

// 获取当前时间(2023-11-23_18-42-345形式)
std::string GetCurrentTimeWithFormat() 
{
	SYSTEMTIME st;
	GetLocalTime(&st);

	std::string fileName;
	fileName = std::to_string(st.wYear) + "-" + std::to_string(st.wMonth) + "-" + std::to_string(st.wDay) + "_" + std::to_string(st.wHour) + "-" + std::to_string(st.wMinute) + "-" + std::to_string(st.wSecond) + "-" + std::to_string(st.wMilliseconds);

	return fileName;
}

未处理异常过滤器回调函数

// 未处理异常过滤器回调函数																																		
LONG WINAPI UnhandledExceptionFilterCallback(EXCEPTION_POINTERS *pExceptionPointers)
{
	std::string moduleFilePath = GetCurrentModuleFilePath();
	std::string moduleFileName = GetModuleName(moduleFilePath);
	std::string currentTime = GetCurrentTimeWithFormat();

	// 获取dump文件名字
	std::string dumpFileName = moduleFileName + "_" + currentTime + ".dmp";

	// 获取dump文件输出路径
	std::string dumpFilePath = "";
	size_t pos = moduleFilePath.find_last_of("\\");
	if (pos != std::string::npos)
	{
		dumpFilePath = moduleFilePath.substr(0, pos + 1) + dumpFileName;
	}
	else
	{
		dumpFilePath = dumpFileName;
	}

	// 创建dump文件
	HANDLE hDumpFile = CreateFile(dumpFilePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hDumpFile != INVALID_HANDLE_VALUE) 
	{
		MINIDUMP_EXCEPTION_INFORMATION miniDumpExceptionInfo;
		miniDumpExceptionInfo.ThreadId = GetCurrentThreadId();           // 表示引发异常的线程的线程标识符
		miniDumpExceptionInfo.ExceptionPointers = pExceptionPointers;    // 指向包含有关异常上下文信息的指针的指针
		miniDumpExceptionInfo.ClientPointers = FALSE;                    // 指示是否包含有关客户端指针的信息。如果设置为 TRUE,则会包含客户端指针的信息;如果设置为 FALSE,则不会包含客户端指针的信息。
		
		/*
		Note
		在 Windows 编程中,客户端指针通常指的是指向客户端应用程序内存中的数据结构或对象的指针。
		当生成 dump 文件时,包含客户端指针的信息可能会暴露应用程序的内部结构和数据,可能包含敏感信息,因此在某些情况下可能需要禁用客户端指针的信息以保护隐私和安全。
		*/

		// 根据自己的需要指定dump文件的类型,一般MiniDumpNormal就可
		MINIDUMP_TYPE miniDumpType = (MINIDUMP_TYPE)(MiniDumpNormal
			| MiniDumpWithHandleData
			| MiniDumpScanMemory
			| MiniDumpWithProcessThreadData
			| MiniDumpWithThreadInfo);

		// 写入dump文件
		BOOL bMiniDumpWriteSuccessful = MiniDumpWriteDump(
			GetCurrentProcess(),     // 获取进程句柄
			GetCurrentProcessId(),   // 获取进程ID
			hDumpFile,               // 要写入的dump文件句柄
			miniDumpType,            // 指定dump文件的类型
			&miniDumpExceptionInfo,  // 指向包含异常信息的结构体指针
			NULL,                    // 指向用户自定义数据的结构体指针
			NULL                     // 指向回调函数的结构体指针
		);                      
		CloseHandle(hDumpFile);
	}

	return EXCEPTION_EXECUTE_HANDLER;
}

未处理异常过滤器函数的调用

未处理异常过滤器函数的调用应该在main函数(MFC程序的话是InitInstance函数)入口或者dll函数的入口。

int main(){
	// 设置未处理异常过滤器
	SetUnhandledExceptionFilter(UnhandledExceptionFilterCallback);
	
    // 程序逻辑
    //......
}

注意事项

在生成dump文件时,有一些注意事项需要考虑:

  • 文件大小:生成的dump文件可能会很大,特别是在应用程序的内存占用较大时。确保生成dump文件的目标位置有足够的磁盘空间。
  • 调试符号:为了更好地分析dump文件,通常需要应用程序的调试符号(PDB文件)。确保在生成dump文件时,同时保存了相关的调试符号信息。
  • 版本一致性:在分析dump文件时,确保使用与生成dump文件时相同版本的调试符号和源代码。否则,可能会导致分析结果不准确。

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

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

相关文章

专用设备上的SD卡插入电脑想读取数据,提示要格式化?

环境&#xff1a; Win10 专业版 车载感应数据专用SD卡 问题描述&#xff1a; 专用设备上的SD&#xff0c;现在把SD卡从设备取出&#xff0c;用读卡器插入电脑提示要格式化&#xff1f; 解决方案&#xff1a; 1.先进入PE查看SD分区情况&#xff0c;SD格式为ext4 查看文件…

爱上C语言:scanf、gets以及getchar输入字符串你真的懂了吗

&#x1f680; 作者&#xff1a;阿辉不一般 &#x1f680; 你说呢&#xff1a;不服输的你&#xff0c;他们拿什么赢 &#x1f680; 专栏&#xff1a;爱上C语言 &#x1f680;作图工具&#xff1a;draw.io(免费开源的作图网站) 如果觉得文章对你有帮助的话&#xff0c;还请点赞…

计算机组成原理2

1.浮点数 2.IEEE 754 3.存储器的性能指标 4.存储器的层次化结构 主存类似手机运行内存8g &#xff0c;辅存类似手机内存128g.... 辅存必须先通过主存才能被cpu接收&#xff0c;就例如微信打开那个月亮小人界面两三秒就是主存在读取辅存的程序然后被cpu接收运行。 5.主存储…

EMG肌肉电信号处理合集(三)

本文主要展示常见的肌电信号预处理的实现&#xff0c;开发环境为matlab。 目录 1 肌电信号低通&#xff0c;高通&#xff0c;带通滤波 2 去除DC 0阶偏置&#xff0c;1阶偏置 3 全波整流 4 信号降采样 5 linear envolope / butterworth 低通滤波器 1 肌电信号低通&#xf…

(一)C语言之入门:使用Visual Studio Community 2022运行hello world

使用Visual Studio Community 2022运行c语言的hello world 一、下载安装Visual Studio Community 2022 与 新建项目二、编写c helloworld三、编译、链接、运行 c helloworld1. 问题记录&#xff1a;无法打开源文件"stdio.h"2. 问题记录&#xff1a;调试和执行按钮是灰…

【LeetCode】挑战100天 Day13(热题+面试经典150题)

【LeetCode】挑战100天 Day13&#xff08;热题面试经典150题&#xff09; 一、LeetCode介绍二、LeetCode 热题 HOT 100-152.1 题目2.2 题解 三、面试经典 150 题-153.1 题目3.2 题解 一、LeetCode介绍 LeetCode是一个在线编程网站&#xff0c;提供各种算法和数据结构的题目&…

Node使用Nvm安装双版本切换(node两个版本同时用怎么办?不同的项目Node版本要求不一样怎么办?)

先把node.js卸载 开始—>添加删除程序—>node npm -v node -v //检查是否还存在&#xff0c;卸载成功就行了NVM下载 github下载 百度网盘下载 打开安装包以管理员身份安装&#xff0c;要是记得这个路径并且必须全是英文 使用nvm安装两个使用的node版本 cmd以管理员…

手撕A*算法(详解A*算法)

A*算法原理 全局路径规划算法&#xff0c;根据给定的起点和终点在全局地图上进行总体路径规划。 导航中使用A*算法计算出机器人到目标位置的最优路线&#xff0c;一般作为规划的参考路线 // 定义地图上的点 struct Point {int x,y; // 栅格行列Point(int x, int y):x(x),y(y){…

【C++】拷贝构造函数,析构函数详解!

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

国家超级计算济南中心低代码平台应用实践

摘要&#xff1a;文章主要介绍了济南超算使用低代码平台明道云解决了一系列业务问题&#xff0c;包括资产管理、人员与机构管理、流程制度管理等。通过明道云平台&#xff0c;济南超算成功地将不同部门的业务信息进行整合&#xff0c;提高了工作效率和管理水平。文章还强调了明…

外部网关协议_边界网关协议BGP

一.边界网关协议BGP的基本概念 边界网关协议(Border Gateway Protocol&#xff0c;BGP&#xff09;属于外部网关协议EGP这个类别&#xff0c;用于自治系统AS之间的路由选择协议。由于在不同AS内度量路由的“代价”(距离、带宽、费用等&#xff09;可能不同&#xff0c;因此对于…

批量创建表空间数据文件(DM8:达梦数据库)

DM8:达梦数据库 - - 批量创建表空间数据文件 环境介绍1 批量创建表空间SQL2 达梦数据库学习使用列表 环境介绍 在某些场景(分区表子表)需要批量创建表空间,给不同的表使用,以下代码是批量创建表空间的SQL语句; 1 批量创建表空间SQL --创建 24个数据表空间,每个表空间有3个数…

LeetCode.283移动零(双指针)

LeetCode.283移动零 1.问题描述2.解题思路3.代码 1.问题描述 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1…

在一个页面里向两张表里插入内容时,有一些复杂的BUG简单化

向两张表里插入内容时&#xff0c;有一些复杂的BUG简单化 当在第一张表里的页面操作&#xff0c;在第一张表查询结果的页面进行编辑&#xff0c;在编辑的时候需要对第二张表里和第一张表都保存内容&#xff0c;而且插入之后两张表的id关联着&#xff0c;这个时候这张表的id就不…

【LeetCode】挑战100天 Day12(热题+面试经典150题)

【LeetCode】挑战100天 Day12&#xff08;热题面试经典150题&#xff09; 一、LeetCode介绍二、LeetCode 热题 HOT 100-142.1 题目2.2 题解 三、面试经典 150 题-143.1 题目3.2 题解 一、LeetCode介绍 LeetCode是一个在线编程网站&#xff0c;提供各种算法和数据结构的题目&…

2023年度全国国土变更调查工作相关技术文档、质检软件

1.资料清单&#xff1a; (1)01技术文件 2023年度国土变更调查数据库建设技术要求.pdf 2023年度全国国土变更调查实施方案.doc 附件1.国土调查数据库更新数据规范&#xff08;2023年度&#xff09;.pdf 附件2.国土调查数据库更新变更规则&#xff08;2023年度&#xff09;.pdf…

unity3d人物由静止到跑,出现人物漂移一段距离后再跑

减少动画切换过程中第一个动画的时间&#xff0c;交叉部分适当调小&#xff0c;动作切换就顺畅了

大模型生态新篇章:以AI Agent为引,助企业创新应用落地

文 | 智能相对论 作者 | 沈浪 以聊天机器人、虚拟助手、智能客服等为代表的对话式人工智能 (Conversational AI Agents ) 在具体服务场景中的应用已经十分普遍。今年以来&#xff0c;随着大模型技术的爆发与加持&#xff0c;对话式AI被市场赋予了更高的期望。 “所有行业都值…

C语言——指针(一)

&#x1f4dd;前言 这篇文章主要带大家初步认识一下指针&#xff0c;供大家理解参考。 主要归纳与讲解&#xff1a; 1&#xff0c;指针与指针变量 2&#xff0c;指针的基本使用&#xff08;如何定义&#xff0c;初始化&#xff0c;引用&#xff09; &#x1f3ac;个人简介&…

线性回归中的函数求导

在线性回归中&#xff0c;函数求导是一个重要的数学工具&#xff0c;用于计算损失函数关于模型参数的导数。通过求导&#xff0c;我们可以找到最优的参数值&#xff0c;以实现更好的线性回归拟合。 本文将介绍线性回归的基本原理&#xff0c;以及如何通过函数求导来优化线性回…