目的
利用dump和pdb文件来快速定位程序崩溃的地方
步骤
这里写一个简单的崩溃程序。
sample.cpp
class Test{
public:
void say(){
int a = 0;
int b = 10 / a;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
Test t;
t.say();
return 0;
}
int b=10/a这句代码会导致程序崩溃。
1.dump文件生成相关配置
添加两个文件。
CCreateDump.h
#pragma once
#include <string>
using namespace std;
class CCreateDump
{
public:
CCreateDump();
~CCreateDump(void);
static CCreateDump* Instance();
static long __stdcall UnhandleExceptionFilter(_EXCEPTION_POINTERS* ExceptionInfo);
//声明Dump文件,异常时会自动生成。会自动加入.dmp文件名后缀
void DeclarDumpFile(std::string dmpFileName = "");
private:
static std::string strDumpFile;
static CCreateDump* __instance;
};
CCreateDump.cpp文件
#include "stdafx.h"
#include <Windows.h>
#include "CCreateDump.h"
#include <DbgHelp.h>
#pragma comment(lib, "dbghelp.lib")
CCreateDump* CCreateDump::__instance = NULL;
std::string CCreateDump::strDumpFile = "";
CCreateDump::CCreateDump()
{
}
CCreateDump::~CCreateDump(void)
{
}
long CCreateDump::UnhandleExceptionFilter(_EXCEPTION_POINTERS* ExceptionInfo)
{
HANDLE hFile = CreateFile(strDumpFile.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
MINIDUMP_EXCEPTION_INFORMATION ExInfo;
ExInfo.ThreadId = ::GetCurrentThreadId();
ExInfo.ExceptionPointers = ExceptionInfo;
ExInfo.ClientPointers = FALSE;
// write the dump
BOOL bOK = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL);
CloseHandle(hFile);
if (!bOK)
{
DWORD dw = GetLastError();
//写dump文件出错处理,异常交给windows处理
return EXCEPTION_CONTINUE_SEARCH;
}
else
{ //在异常处结束
return EXCEPTION_EXECUTE_HANDLER;
}
}
else
{
return EXCEPTION_CONTINUE_SEARCH;
}
}
void CCreateDump::DeclarDumpFile(std::string dmpFileName)
{
SYSTEMTIME syt;
GetLocalTime(&syt);
char c[MAX_PATH];
sprintf_s(c, MAX_PATH, "[%04d-%02d-%02d %02d:%02d:%02d]", syt.wYear, syt.wMonth, syt.wDay, syt.wHour, syt.wMinute, syt.wSecond);
strDumpFile = std::string(c);
if (!dmpFileName.empty())
{
strDumpFile += dmpFileName;
}
strDumpFile += std::string(".dmp");
SetUnhandledExceptionFilter(UnhandleExceptionFilter);
}
CCreateDump* CCreateDump::Instance()
{
if (__instance == NULL)
{
__instance = new CCreateDump;
}
return __instance;
}
在sample.cpp添加一句:
int _tmain(int argc, _TCHAR* argv[])
{
CCreateDump::Instance()->DeclarDumpFile("dumpfile");//新加代码
Test t;
t.say();
return 0;
}
对项目属性进行设置。
属性-》配置属性-》常规-》字符集-》使用多字节字符集。
2.pdb文件生成相关配置
对项目属性进行配置:
属性–》链接器-》调试–》生成调试信息–》是
3.生成dump和pdb文件
对项目进行编译生成sample.exe文件,可以同时生成了sample.pdb文件。
双击运行.exe程序,程序一闪而过,崩溃了,同时生成了dump文件。
4.调试
用vs打开dump文件,点击使用本机进行调试。
可以发现自动定位到崩溃的代码了。
当然这是最理想的状态。工程项目的目录结构跟生成pdb文件的时候一样没发生改变,不需要设置源码的路径,直接把exe、pdb、dump文件放到同一个文件夹下就行了,因为pdb文件中保存了源代码的绝对路径。否则需要手动添加源码路径,然后在进行本机调试。
属性-》调试源文件
当然也可以不管,直接本机调试,根据调用堆栈也能知道是哪句代码出问题,只是看不到具体代码。
5.注意
前面的部分有个大前提,就是得保证exe、dump和pdb文件的一致性,就是说exe和pdb都是由一份代码在同一时间生成的,而dump又是由这个exe生成的。这三个文件有GUID的效验码,假如不一致的话就会没法定位。代码稍微改动了没关系,只是定位会不是很准确。这个GUID貌似是跟时间戳相关的,就算代码没变,重新生成了的话也会改变GUID。
可以用dumpbin查看guid。打开VS2013 开发人员命令提示,输入
dumpbin /headers D:\opgl\sample\Debug\sample.exe
,这部分就是GUID
前面还有一段,这是时间戳信息,也就是生成exe的时间,可以用它来找相应的代码。
6.用Windbg定位
对WIndbg不是很熟悉,据说:
Windbg是一款功能十分强大的调试工具,它设计了极其丰富的功能来支持各种调试任务,包括用户态调试、内核态调试、调试转储文件、远程调试等等。
.1设置pdb文件路径
file-》Symbol Search Path。我的pdb是放在这个路径下。
.2设置源码位置
file-》Source Search Path
.3打开dump文件
把dump文件直接拖进去。
输入自动分析命令
!analyze -v
此时左边变成了BUSY,就是在工作中,等它变回去就结束了。
分析结果关键部分是这个
STACK_TEXT表示堆栈信息,后面表示崩溃的代码。
不输入源代码路径的话它就会自动读取pdb中的源码路径,假如找不到的话就没后面部分,只能知道哪个函数出错了。
我用的Windbg是这个:
链接:https://pan.baidu.com/s/1eNMibUvA1xgIgM6GIM9ePA
提取码:z221
外面的windbg是汉化版,但用起来有问题,我用的是x64里面的。
参考:
1.Dump文件的生成和使用
2.Dump调试
3.vs2010下使用dmp文件和pdb文件调试时dump、exe和pdb三个文件要保持版本一致的原因
4.VC++使用pdb和dump恢复“案发现场”
5.转储了dump后,如何用Windbg进行分析呢?