windows系统利用vs 分析DMP文件、pdb文件定位异常崩溃

exe文件、dmp文件和pdb文件必须保持一致!exe文件和pdb文件同时生成,dmp文件是由当前exe生成的。

调试器是如何来判别EXE、DLL等是否和一个pdb文件匹配呢?

每次我们链接EXE或者DLL或者SYS的时候,链接器都将产生一个唯一的GUID,然后将其写入到PDB和可执行文件。调试器加载的时候将检查两者的GUID,如果一致就表示他们匹配。

如果我们需要调试,我们需要查dmp文件,那么请妥善保管好自己的代码和pdb。每次重新编译,即使所有代码均没有变化,他们的GUID也不同。

所以在发布版的可执行程序,保管备份好代码和pdb.这样就算程序崩溃了,也可以调试定位问题。
 

设置生成 pdb

  1. 选择 项目 -> 工程名+属性
    这里写图片描述

  2. 之后选择 配置属性 -> 连接器 -> 调试

上图中,“生成调试信息”为pdb文件生成与否的使能开关,“生成程序数据库文件”为该pdb文件的名字,默认即可。

PDB就是程序数据库文件并且包含了符号文件。符号文件包含的信息包括:函数,局部以及全局变量,以及用来把汇编代码和源代码关联起来的行号信息。


DUMP文件

生成dump文件的办法有下面4种,一般release使用第三种,debug使用第四种办法。

1.手动设置生成。

在代码调试过程中,知道会出现bug的情况下,手动点击工具栏的调试->将转储另存为,生成dump文件。这种办法太傻蛋了。

2.自己写封装一个函数,调用MiniDumpWriteDump函数实现。主动检查,主动生成。

调用时机:当异常发生时,且程序不处于调试模式,则首先调用该函数。即如果在调试模式下,不会调用该函数,也就不会生成dump文件。

MiniDumpWriteDump是MS DbgHelp.dll 中一个API, 用于导出当前运行的程序的Dump。具体见:设置C++崩溃时生成Dump文件_EnskDeCode-CSDN博客_c++ dump
 

MFC程序
#include "stdafx.h"
#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, L"%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);
}

int main()
{
    // 加入崩溃dump文件功能
    SetUnhandledExceptionFilter(ExceptionFilter);
    // 使程序崩溃产生 Dump 文件
    int *p = NULL;
    *p=1;
}
qt程序

在QtCreator中默认不支持生成dump文件,且发行版release模式不含调试信息,因此这里需要进行以下两步设置。

1.在.pro文件中添加如下语句,来产生调试信息以及pdb文件


win32{
    QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO
    QMAKE_LFLAGS_RELEASE = $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO
    LIBS += -lDbgHelp
}

2.程序文件中添加可以产生dump文件的代码

#include "dialog.h"
#include <QApplication>

#include <QDateTime>
#include <QDebug>
#include <QDir>

static QString g_strDumpPath = "";

#ifdef Q_OS_WIN
#include <Windows.h>          // Windows.h必须放在DbgHelp.h前,否则编译会报错
#include <DbgHelp.h>

LONG crashHandler(EXCEPTION_POINTERS *pException)
{
    QString curDataTime = QDateTime::currentDateTime().toString("yyyyMMddhhmmss");
    QString dumpName = g_strDumpPath + curDataTime + ".dmp";

    HANDLE dumpFile = CreateFile((LPCWSTR)QString(dumpName).utf16(),GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if(dumpFile != INVALID_HANDLE_VALUE)
    {
        MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
        dumpInfo.ExceptionPointers = pException;
        dumpInfo.ThreadId = GetCurrentThreadId();
        dumpInfo.ClientPointers = TRUE;

        MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),dumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);
        CloseHandle(dumpFile);
    }
    else
    {
        qDebug() << "dumpFile not vaild";
    }

    return EXCEPTION_EXECUTE_HANDLER;
}

//防止CRT(C runtime)函数报错可能捕捉不到
void DisableSetUnhandledExceptionFilter()
{
    void* addr = (void*)GetProcAddress(LoadLibrary(L"kernel32.dll"), "SetUnhandledExceptionFilter");
    if(addr)
    {
        unsigned char code[16];
        int size = 0;

        code[size++] = 0x33;
        code[size++] = 0xC0;
        code[size++] = 0xC2;
        code[size++] = 0x04;
        code[size++] = 0x00;

        DWORD dwOldFlag, dwTempFlag;
        VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);
        WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);
        VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);
    }
}

#endif






int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

#ifdef Q_OS_WIN  /// 创建dump
    QString strdmpFile =QApplication::applicationDirPath();
    strdmpFile = strdmpFile +"/testdump/";
    g_strDumpPath = strdmpFile;
    QDir dir(strdmpFile);
    if(!dir.exists())
    {
        if (!dir.mkpath(strdmpFile))
            qDebug() << QStringLiteral("创建dump文件目录失败");
    }
    SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)crashHandler);
    DisableSetUnhandledExceptionFilter();
#endif

    Dialog w;
    w.show();
    return a.exec();
}


3.通过任务管理器:

这种适用在exe程序挂了(crash)的时候进程还未退出,比如我运行程序,出现了下面的错:

 此时打开任务管理器,右击相应进程,点击"Create Dump File“:

 这种办法:dmp文件存放在系统目录。需要自己查找磁盘,可以使用everything 软件更快查找到。

4.改注册表

如果程序crash的时候没有框蹦出来,可以通过改注册表的设置让操作系统在程序crash的时候自动生成dump,并放到特定的目录。

增加注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps 

添加项如下图

 

其中Value Data=1代表的含义是:

设置完成后,crash发生时,操作系统生成dump,路径在dumpfolder设置的目录d:\dump文件下。

加载dump和pdb文件

1.配置源代码目录

 打开一个项目解决方案(可新建,可以利用现有的)。右键属性。选择 通用属性 调试源文件 添加程序源代码目录。注意一定是解决方案所在的路径

 test是随便一个工程。而memory才是出现崩溃的工程。就是出现崩溃的程序的源码导进来。

但是如果代码已经改过了,恢复不到当时的状态,vs显示不了源码怎么办?只要设置:Tools->Options->Debugging->General->Require source files to exactly match the original version 这个复选框钩掉就可以了.

2.配置pdb信息
 将生成的dump文件直接拖入到vs中。

拖入后可看到如上界面。右上方红色框选部分。设置符号路径。点击进入。配置pdb文件步骤如下:

a.在vs的工具菜单->选项->调试->符号 配置pdb文件路径。

pdb的路径就是前面生成exe时,同时生成pdb文件的存放的路径。

具体到某一个.pdb文件或者把所有的pdb文件集中存放到一个公共的目录文件都可以。

b.把运行的exe、dll路径也加载到符号文件的位置中

这样才能把exe关联起来(不能忽略)。
c.第一次调试pdb时必须也勾选microsotf符号服务器

(目的是加载windows本身的符号),后面在调试就不用勾选了。可以在当前的选项页面看到缓冲符号所在的目录。

3.仅限本机进行调试 。

都设置好了,点击 使用仅限本机进行调试 按钮。然后程序自动调到崩溃的代码

效果如下 

注意的地方:

1.要把可执行程序即exe放到 进程名称对应的路径。不然程序没法启动。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值