需求背景
用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文件调试分析
- 打开Visual Studio 2017开发工具
- 将前面生成的dump文件拖入开发工具中进行打开(或通过文件---打开文件的方式)
- 打开 右侧的“设置符号路径”选项选择 pdb和dump文件所在目录,点击确定
- 点击 “使用 仅限本机 进行调试”
- 结果
执行调试后,会自动定位到程序异常的位置,具体信息还可以通过查看堆栈信息来定位分析。
总结
- 程序打包时将pdb文件一起打包
- 程序出现崩溃时 让客户讲dump文件和pdb文件一起发送过来 调试分析即可