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