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

最近项目中新作成了一个动态链接库,长时间运行后,偶尔会崩溃。根据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);
	
    // 程序逻辑
    //......
}

try-catch模块场合

如果异常发生在try快中,且被catch捕捉到,系统就不会生成dump文件了。如果仍希望在try-catch块中捕获异常的同时生成dump文件,就必须在catch块中手动调用生成dump文件的函数。

catch
{
	// 其他处理......
	UnhandledExceptionFilterCallback(nullptr)}

注意事项

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

  • 文件大小:生成的dump文件可能会很大,特别是在应用程序的内存占用较大时。确保生成dump文件的目标位置有足够的磁盘空间。
  • 调试符号:为了更好地分析dump文件,通常需要应用程序的调试符号(PDB文件)。确保在生成dump文件时,同时保存了相关的调试符号信息。
  • 版本一致性:在分析dump文件时,确保使用与生成dump文件时相同版本的调试符号和源代码。否则,可能会导致分析结果不准确。
  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++代码控制程序生成dump文件,可以通过以下步骤实现: 1. 在代码添加以下头文件: ```cpp #include <Windows.h> #include <DbgHelp.h> ``` 2. 在程序初始化时,设置dump文件生成路径和名称: ```cpp // 设置dump文件生成路径和名称 SetUnhandledExceptionFilter(ExceptionHandler); ``` 3. 实现异常处理函数,并在其生成dump文件: ```cpp // 异常处理函数 LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo) { // 生成dump文件的路径和名称 const char* filePath = "C:\\dump\\myapp.dmp"; // 创建dump文件 HANDLE hFile = CreateFileA(filePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { // 写入dump文件头部 MINIDUMP_EXCEPTION_INFORMATION exceptionInfo; exceptionInfo.ThreadId = GetCurrentThreadId(); exceptionInfo.ExceptionPointers = ExceptionInfo; exceptionInfo.ClientPointers = FALSE; // 写入dump文件内容 MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &exceptionInfo, NULL, NULL); // 关闭dump文件 CloseHandle(hFile); } // 继续执行程序 return EXCEPTION_CONTINUE_EXECUTION; } ``` 以上代码会在程序发生异常时生成一个名为“myapp.dmp”的dump文件,路径为“C:\dump\”。你可以根据需要修改dump文件的名称和路径。注意,如果程序没有发生异常,dump文件不会生成。 另外,如果你使用的是Visual Studio,可以在项目属性的“Debugging”选项卡设置生成dump文件的路径和名称。这样,在程序发生异常时,Visual Studio会自动生成dump文件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值