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里面的。

 

posted @ 2023-05-11 10:20  Citrusliu  阅读(1305)  评论(0编辑  收藏  举报