Bootstrap

C++ 程序崩溃定位分析解决方案

需求背景

用Visual Studio 2017开发的MFC程序发布后,客户在使用时出现了挂机的情况,受限于使用场景和复现条件等因素,分析解决这类概率性的问题遇到了一些困难。

解决方案

利用PDB和dump文件定位问题并进行调试。

  • 关于pdb文件

什么是pdb文件,pdb文件是“程序数据库”Program Data Base的简称,包含了程序的代码信息。

通过这个文件,可以查看对应断点的位置,堆栈信息等内容;

使用pdb使得我们无需代码也可以掌握程序的堆栈运行状态;

Pdb文件是链接器自动生成;

文件由2个部分构成,私有符号数据和公共符号表;

  • dump文件

Dump文件是附加堆栈信息的存储文件的简称,文件扩展名是“.dmp”,通过dump文件我们可以得到程序运行某一时刻的堆栈数据。

当我们程序意外崩溃后,通常程序后立即中断运行,此时我们生成这一时刻的dump文件就可以通过此时的堆栈进行分析,找到崩溃的代码并分析原因。

 

  • 通过C++程序创建dump文件

已VC++ 程序为例来说明,在项目文件中,添加头文件引用和相关代码,

#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, "%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);
}

在程序初始化函数中,加入创建 崩溃dump文件功能,

SetUnhandledExceptionFilter(ExceptionFilter);

在程序入口创建后,程序一旦崩溃会在当前目录下自动生成dump文件

  • Dump文件调试分析

  1. 打开Visual Studio 2017开发工具
  2. 将前面生成的dump文件拖入开发工具中进行打开(或通过文件---打开文件的方式)  
  3. 打开 右侧的“设置符号路径”选项选择 pdb和dump文件所在目录,点击确定
  4. 点击 “使用 仅限本机 进行调试”
  5. 结果

 

执行调试后,会自动定位到程序异常的位置,具体信息还可以通过查看堆栈信息来定位分析。

总结

  1. 程序打包时将pdb文件一起打包
  2. 程序出现崩溃时 让客户讲dump文件和pdb文件一起发送过来 调试分析即可
;