WinDbg调试Dump:

用WinDbg调试Dump文件需要三样东西:

  • Dump文件
  • 源代码,必须与编译可执行文件时的代码一致
  • PDB文件,编译时生成的符号文件,一定是要与可执行文件同时生成的!(即便源代码一致,重新生成的PDB文件也不行。)

1)打开WinDbg工具,通过菜单“File”->“Open Crash Dump”打开dmp文件。
image.png
2)置符号路径(生成二进制文件时对应的PDB文件所在目录,版本要一致!多个二进制文件的pdb最好生在同一目录):
image.png

常用命令:

//加载pdb:
.reload /i /f
//查看pdb是否加载成功:
lm
//自动分析指令(通常分析出的是主线程的堆栈)
!analyze -v

此时通过分析出的堆栈去找有无kernel32!UnhandledExceptionFilter信息
如果有,则此堆栈(主线程堆栈)为异常堆栈;
如果没有,则需查看所有线程堆栈:
//查看所有线程堆栈:
~*kb

程序示例

.h

//reference: http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus
#pragma once


//need function "MiniDumpWriteDump" in Dbghelp.dll which is always existing on Windows XP and later version
//if you want to use it on Windows 2000, you can put Windows XP's Dbghelp.dll in your program dir
//dmp file name will end with ".dmp" in exe dir, see source file for details


namespace exception_catcher
{
    //some exception handlers work on per process, others work on per thread
    //per process: SEH exception, pure virtual function call, C++ new exception, runtime invalid parameter error, signal: SIGABRT, SIGINT, SIGTERM
    //per thread: terminate() call, unexpected() call, signal: SIGFPE, SIGILL, SIGSEGV
    void set_process_exception_handlers();
    void set_thread_exception_handlers();
}



/*

//
//
//代码修改自http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus
//有两处大修改:
//      1. 原代码中GetExceptionPointers的实现是复制自vc8.0,我这里是复制自vc9.0
//      2. 原代码中是动态获取MiniDumpWriteDump函数的地址,我这里是静态链接了Dbghelp.dll
//  因为经我测试,虽然Windows2000的Dbghelp.dll里没有MiniDumpWriteDump函数,但是是可以用WindowsXPSP3的Dbghelp.dll而正常生成dump文件的
//  所以,你最好将WindowsXPSP3的Dbghelp.dll和你的应用程序一同发布。我没有测试其它版本的Dbghelp.dll是否能在Windows2000上用
//
//

//对于示例中的SEH异常,一次会有两个dmp文件,第一个是SIGSEGV的,第二个是SEH的。两个都指示到了正确的异常语句
//对于示例中的terminate钩子,一次会产生两个dmp文件,第一个是terminate的,第二个是SIGABRT的。但是两个都指示到了unexpected()的调用位置???
//对于示例中的unexpected钩子,一次会产生三个dmp文件,第一个是unexpected的,第二个是terminate的,第三个是SIGABRT的。都指示到了unexpected()的调用位置
//对于示例中的纯虚函数调用异常,一次会产生两个dmp文件,第一个是纯虚函数调用的,第二个是SIGABRT的。都指示到了纯虚函数调用的位置
//对于示例中的非法参数调用,一次会产生一个dmp文件,指示位置正确
//对于示例中的new异常,一次会产生两个dmp文件,第一个是new的,指示位置正确,第二个是SEH的,调用栈:AVbadcast->_onexit->_unlockexit->_unlock->LeaveCriticalSection
//对于示例中的SIGABRT信号钩子,一次会产生一个dmp文件,指示位置正确
//对于示例中的SIGFPE信号钩子,我测试的时候除零语句并没有发生除零异常(Release版),可能还要设置什么东西,所以无法验证是否能够捕获到除零异常
//对于示例中的SIGILL信号钩子,一次会产生一个dmp文件,指示位置正确
//对于示例中的SIGINT信号钩子,一次会产生一个dmp文件,指示位置正确
//对于示例中的SIGSEGV信号钩子,一次会产生一个dmp文件,指示位置正确
//对于示例中的SIGTERM信号钩子,一次会产生一个dmp文件,指示位置正确
//对于示例中的RaiseException异常,在不同的code下可能产生不同个数的dmp文件,不过调用栈都是:_onexit->printf
//对于示例中的C++类型异常,一次会产生一个dmp文件,是SEH的,根据类型的不同调用栈不同,但是都没指向正确的代码上???


//
//下面是原代码中的示例程序(稍加修改)
//


#include <float.h>
#include <stdio.h>
#include <signal.h>
#include <conio.h>
#include <stdlib.h>
#include <exception>
#include <Windows.h>
#include "logger.h"
#include "exception_catcher.h"



void sigfpe_test()
{ 
    // Code taken from http://www.devx.com/cplus/Article/34993/1954

    //Set the x86 floating-point control word according to what
    //exceptions you want to trap. 
    _clearfp(); //Always call _clearfp before setting the control
    //word
    //Because the second parameter in the following call is 0, it
    //only returns the floating-point control word
    unsigned int cw; 
    _controlfp_s(&cw, 0, 0); //Get the default control
    //word
    //Set the exception masks off for exceptions that you want to
    //trap.  When a mask bit is set, the corresponding floating-point
    //exception is //blocked from being generating.
    cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|
        EM_DENORMAL|EM_INVALID);
    //For any bit in the second parameter (mask) that is 1, the 
    //corresponding bit in the first parameter is used to update
    //the control word.  
    unsigned int cwOriginal;
    _controlfp_s(&cwOriginal, cw, MCW_EM); //Set it.
    //MCW_EM is defined in float.h.
    //Restore the original value when done:
    //_controlfp(cwOriginal, MCW_EM);

    // Divide by zero

    float a = 1;
    float b = 0;
    float c = a/b;
    c; 
}

#define BIG_NUMBER 0x1fffffff
#pragma warning(disable: 4717) // avoid C4717 warning
int RecurseAlloc() 
{
    int *pi = new int[BIG_NUMBER];
    pi;
    RecurseAlloc();
    return 0;
}

class CDerived;
class CBase
{
public:
    CBase(CDerived *derived): m_pDerived(derived) {};
    ~CBase();
    virtual void function(void) = 0;

    CDerived * m_pDerived;
};

#pragma warning(disable:4355)
class CDerived : public CBase
{
public:
    CDerived() : CBase(this) {};   // C4355
    virtual void function(void) {};
};

CBase::~CBase()
{
    m_pDerived -> function();
}

int main()
{
    InitLog("", 0, LOG_DEBUG);

    exception_catcher::SetProcessExceptionHandlers();
    exception_catcher::SetThreadExceptionHandlers();

    printf("Choose an exception type:\n");
    printf("0 - SEH exception\n");
    printf("1 - terminate\n");
    printf("2 - unexpected\n");
    printf("3 - pure virtual method call\n");
    printf("4 - invalid parameter\n");
    printf("5 - new operator fault\n");    
    printf("6 - SIGABRT\n");
    printf("7 - SIGFPE\n");
    printf("8 - SIGILL\n");
    printf("9 - SIGINT\n");
    printf("10 - SIGSEGV\n");
    printf("11 - SIGTERM\n");
    printf("12 - RaiseException\n");
    printf("13 - throw C++ typed exception\n");
    printf("Your choice >  ");

    int ExceptionType = 0;
    scanf_s("%d", &ExceptionType);

    switch(ExceptionType)
    {
    case 0: // SEH
        {
            // Access violation
            int *p = 0;
#pragma warning(disable : 6011)   // warning C6011: Dereferencing NULL pointer 'p'
            *p = 0;
#pragma warning(default : 6011)   
        }
        break;
    case 1: // terminate
        {
            // Call terminate
            terminate();
        }
        break;
    case 2: // unexpected
        {
            // Call unexpected
            unexpected();
        }
        break;
    case 3: // pure virtual method call
        {
            // pure virtual method call
            CDerived derived;
        }
        break;
    case 4: // invalid parameter
        {      
            char* formatString;
            // Call printf_s with invalid parameters.
            formatString = NULL;
#pragma warning(disable : 6387)   // warning C6387: 'argument 1' might be '0': this does not adhere to the specification for the function 'printf'
            printf(formatString);
#pragma warning(default : 6387)   

        }
        break;
    case 5: // new operator fault
        {
            // Cause memory allocation error
            RecurseAlloc();
        }
        break;
    case 6: // SIGABRT 
        {
            // Call abort
            abort();
        }
        break;
    case 7: // SIGFPE
        {
            // floating point exception ( /fp:except compiler option)
            sigfpe_test();            
        }    
        break;
    case 8: // SIGILL 
        {
            raise(SIGILL);              
        }    
        break;
    case 9: // SIGINT 
        {
            raise(SIGINT);              
        }    
        break;
    case 10: // SIGSEGV 
        {
            raise(SIGSEGV);              
        }    
        break;
    case 11: // SIGTERM
        {
            raise(SIGTERM);            
        }
        break;
    case 12: // RaiseException 
        {
            // Raise noncontinuable software exception
            RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL);        
        }
        break;
    case 13: // throw 
        {
            // Throw typed C++ exception.
            throw std::runtime_error("123");
        }
        break;
    default:
        {
            printf("Unknown exception type specified.");       
            _getch();
        }
        break;
    }

    system("pause");
    return 0;
}

*/

.cpp

//if you want to use this class separately,
//you should comment log sentences in "CreateMiniDump" and all handlers,
//and replace CSelfPath with your exe path getting function in "PrepareDumpFilePath"
#include "stdafx.h"
#include <string>
#include <new.h>
#include <signal.h>
#include <Windows.h>
#include <ShellApi.h>
#include <Dbghelp.h>
#include <intrin.h>
#include "exception_catcher.h"


#pragma comment(lib, "Dbghelp.lib")



//copy from invarg.c of VC9.0
#if defined (_AMD64_)

PRUNTIME_FUNCTION
RtlLookupFunctionEntry (
                        IN ULONG64 ControlPc,
                        OUT PULONG64 ImageBase,
                        IN OUT PVOID HistoryTable OPTIONAL
                        );

PVOID
RtlVirtualUnwind (
                  IN ULONG HandlerType,
                  IN ULONG64 ImageBase,
                  IN ULONG64 ControlPc,
                  IN PRUNTIME_FUNCTION FunctionEntry,
                  IN OUT PCONTEXT ContextRecord,
                  OUT PVOID *HandlerData,
                  OUT PULONG64 EstablisherFrame,
                  IN OUT PVOID ContextPointers OPTIONAL
                  );

#endif



namespace
{
  std::string prepare_dump_file_path()
  {
      char path[MAX_PATH] = { 0 };
      ::GetModuleFileNameA(NULL, path, MAX_PATH);
      for (size_t i = strlen(path) - 1; i >= 0; i--)
      {
          if (path[i] == '\\' || path[i] == ':')
          {
              path[i + 1] = 0;
              break;
          }
      }
    std::string file_path = path;

    file_path += "Log\\";

    SYSTEMTIME systime = {0};
    GetLocalTime(&systime);

    const size_t name_buf_size = 100;
    char name_buf[name_buf_size] = {0};
    //PS: pid 5 numbers
    sprintf_s(name_buf, "SyncDbHelper_%04d%02d%02d%02d%02d%02d%03d_%05d.dmp",
      systime.wYear, systime.wMonth, systime.wDay,
      systime.wHour, systime.wMinute, systime.wSecond,
      systime.wMilliseconds, GetCurrentProcessId());
    file_path += name_buf;//buf is large enough to hold string and a null-terminated ch

    return file_path;
  }

  bool create_minidump(EXCEPTION_POINTERS* pExceptionPtrs)
  {
    bool ret = false;

    MINIDUMP_EXCEPTION_INFORMATION dumpinfo;
    dumpinfo.ThreadId = GetCurrentThreadId();
    dumpinfo.ExceptionPointers = pExceptionPtrs;
    dumpinfo.ClientPointers = 0;

    std::string file_path = prepare_dump_file_path();
    HANDLE hFile = CreateFileA(file_path.c_str(),
      GENERIC_WRITE,
      0,
      NULL,
      CREATE_ALWAYS,
      FILE_ATTRIBUTE_NORMAL,
      NULL);
    if (INVALID_HANDLE_VALUE == hFile)
    {
      //E_LOG(LOGLEVEL_VERBOSE,"CreateFile(for create dump) fail, file path: %s", file_path.c_str());
    }
    else
    {
      if (!MiniDumpWriteDump(GetCurrentProcess(),
        GetCurrentProcessId(),
        hFile,
        MiniDumpNormal,
        (NULL == pExceptionPtrs) ? NULL : &dumpinfo,
        NULL,
        NULL))
      {
       // E_LOG(LOGLEVEL_VERBOSE,"MiniDumpWriteDump fail, file path: %s", file_path.c_str());
      }
      else
      {
        ret = true;
      }

      CloseHandle(hFile);
      hFile = INVALID_HANDLE_VALUE;

      //string p = kutil::pwd();
      //p += "elin_crash_report.exe --type=crash_report";
      //ShellExecuteA(NULL, "open", p.c_str(), NULL, NULL, SW_SHOW);
    }

    return ret;
  }

  EXCEPTION_POINTERS* get_exception_pointers(const DWORD dwExceptionCode)
  {
    //copy from function _invoke_watson in invarg.c of VC9.0
    EXCEPTION_RECORD   ExceptionRecord = {0};
    CONTEXT ContextRecord;

#ifdef _X86_

    __asm {
      mov dword ptr [ContextRecord.Eax], eax
        mov dword ptr [ContextRecord.Ecx], ecx
        mov dword ptr [ContextRecord.Edx], edx
        mov dword ptr [ContextRecord.Ebx], ebx
        mov dword ptr [ContextRecord.Esi], esi
        mov dword ptr [ContextRecord.Edi], edi
        mov word ptr [ContextRecord.SegSs], ss
        mov word ptr [ContextRecord.SegCs], cs
        mov word ptr [ContextRecord.SegDs], ds
        mov word ptr [ContextRecord.SegEs], es
        mov word ptr [ContextRecord.SegFs], fs
        mov word ptr [ContextRecord.SegGs], gs
        pushfd
        pop [ContextRecord.EFlags]
    }

    ContextRecord.ContextFlags = CONTEXT_CONTROL;
#pragma warning(push)
#pragma warning(disable:4311)
    ContextRecord.Eip = (ULONG)_ReturnAddress();
    ContextRecord.Esp = (ULONG)_AddressOfReturnAddress();
#pragma warning(pop)
    ContextRecord.Ebp = *((ULONG *)_AddressOfReturnAddress()-1);

#elif defined (_AMD64_)

    ULONG64 ControlPc;
    ULONG64 EstablisherFrame;
    PRUNTIME_FUNCTION FunctionEntry;
    PVOID HandlerData;
    ULONG64 ImageBase;

    RtlCaptureContext(&ContextRecord);
    ControlPc = ContextRecord.Rip;
    FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, NULL);
    if (FunctionEntry != NULL) {
      RtlVirtualUnwind(/*UNW_FLAG_NHANDLER*/0x00,
        ImageBase,
        ControlPc,
        FunctionEntry,
        &ContextRecord,
        &HandlerData,
        &EstablisherFrame,
        NULL);
    } else {
      ContextRecord.Rip = (ULONGLONG) _ReturnAddress();
      ContextRecord.Rsp = (ULONGLONG) _AddressOfReturnAddress();
    }

#elif defined (_IA64_)

    /* Need to fill up the Context in IA64. */
    RtlCaptureContext(&ContextRecord);

#else  /* defined (_IA64_) */

    ZeroMemory(&ContextRecord, sizeof(ContextRecord));

#endif  /* defined (_IA64_) */


    ExceptionRecord.ExceptionCode = dwExceptionCode;
    ExceptionRecord.ExceptionFlags    = EXCEPTION_NONCONTINUABLE;
    ExceptionRecord.ExceptionAddress = _ReturnAddress();

    //end copy

    EXCEPTION_RECORD* pExceptionRecord = new EXCEPTION_RECORD;
    memcpy(pExceptionRecord, &ExceptionRecord, sizeof(EXCEPTION_RECORD));
    CONTEXT* pContextRecord = new CONTEXT;
    memcpy(pContextRecord, &ContextRecord, sizeof(CONTEXT));

    EXCEPTION_POINTERS* pExceptionPointers = new EXCEPTION_POINTERS;
    pExceptionPointers->ContextRecord = pContextRecord;
    pExceptionPointers->ExceptionRecord = pExceptionRecord;

    return pExceptionPointers;
  }


  //handlers
  LONG WINAPI catched_seh_handler(EXCEPTION_POINTERS* pExceptionPtrs)
  {
    //E_LOG(LOGLEVEL_VERBOSE,"SEH exception occurs");
    create_minidump(pExceptionPtrs);
    return EXCEPTION_CONTINUE_SEARCH;
  }

  void __cdecl catched_terminate_handler()
  {
    //E_LOG(LOGLEVEL_VERBOSE,"terminate call occurs");
    create_minidump(get_exception_pointers(0));
  }

  void __cdecl catched_unexpected_handler()
  {
    //E_LOG(LOGLEVEL_VERBOSE,"unexpected call occurs");
    create_minidump(get_exception_pointers(0));
  }

  void __cdecl catched_pure_call_handler()
  {
    //E_LOG(LOGLEVEL_VERBOSE,"pure virtual function call occurs");
    create_minidump(get_exception_pointers(0));
  }

  void __cdecl catched_invalid_parameter_handler(const wchar_t* expression,
    const wchar_t* function,
    const wchar_t* file,
    unsigned int line,
    uintptr_t pReserved)
  {
    //E_LOG(LOGLEVEL_VERBOSE,"invalid parameter call occurs, expression[%ls], function[%ls], file[%s], line[%d]",
    //  expression, function, file, line);
    create_minidump(get_exception_pointers(0));
  }

  int __cdecl catched_new_handler(size_t)
  {
    //E_LOG(LOGLEVEL_VERBOSE,"new operator memory allocation exception occurs");
    create_minidump(get_exception_pointers(0));
    return 0;
  }

  void catched_sigabrt_handler(int)
  {
    //E_LOG(LOGLEVEL_VERBOSE,"signal SIGABRT occurs");
    create_minidump(get_exception_pointers(0));
  }

  void catched_sigfpe_handler(int code, int subcode)
  {
    //E_LOG(LOGLEVEL_VERBOSE,"signal SIGFPE occurs, code[%d], subcode[%d]", code, subcode);
    create_minidump(reinterpret_cast<EXCEPTION_POINTERS *>(_pxcptinfoptrs));
  }

  void catched_sigint_handler(int)
  {
    //E_LOG(LOGLEVEL_VERBOSE,"signal SIGINT occurs");
    create_minidump(get_exception_pointers(0));
  }

  void catched_sigill_handler(int)
  {
   // E_LOG(LOGLEVEL_VERBOSE,"signal SIGILL occurs");
    create_minidump(get_exception_pointers(0));
  }

  void catched_sigsegv_handler(int)
  {
    //E_LOG(LOGLEVEL_VERBOSE,"signal SIGSEGV occurs");
    create_minidump(reinterpret_cast<EXCEPTION_POINTERS *>(_pxcptinfoptrs));
  }

  void catched_sigterm_handler(int)
  {
    //E_LOG(LOGLEVEL_VERBOSE,"signal SIGTERM occurs");
    create_minidump(get_exception_pointers(0));
  }
  //end handlers
}


void exception_catcher::set_process_exception_handlers()
{
  // Install top-level SEH handler
  SetUnhandledExceptionFilter(catched_seh_handler);

  // Catch pure virtual function calls.
  // Because there is one _purecall_handler for the whole process,
  // calling this function immediately impacts all threads. The last
  // caller on any thread sets the handler.
  // http://msdn.microsoft.com/en-us/library/t296ys27.aspx
  _set_purecall_handler(catched_pure_call_handler);

  // Catch new operator memory allocation exceptions
  _set_new_handler(catched_new_handler);

  // Catch invalid parameter exceptions.
  _set_invalid_parameter_handler(catched_invalid_parameter_handler);

  // Set up C++ signal handlers

  _set_abort_behavior(_CALL_REPORTFAULT, _CALL_REPORTFAULT);

  // Catch an abnormal program termination
  signal(SIGABRT, catched_sigabrt_handler);

  // Catch illegal instruction handler
  signal(SIGINT, catched_sigint_handler);

  // Catch a termination request
  signal(SIGTERM, catched_sigterm_handler);
}

void exception_catcher::set_thread_exception_handlers()
{
  // Catch terminate() calls.
  // In a multithreaded environment, terminate functions are maintained
  // separately for each thread. Each new thread needs to install its own
  // terminate function. Thus, each thread is in charge of its own termination handling
  // http://msdn.microsoft.com/en-us/library/t6fk7h29.aspx
  set_terminate(catched_terminate_handler);

  // Catch unexpected() calls.
  // In a multithreaded environment, unexpected functions are maintained
  // separately for each thread. Each new thread needs to install its own
  // unexpected function. Thus, each thread is in charge of its own unexpected handling.
  // http://msdn.microsoft.com/en-us/library/h46t5b69.aspx
  set_unexpected(catched_unexpected_handler);

  // Catch a floating point error
  typedef void (*sigh)(int);
  signal(SIGFPE, (sigh)catched_sigfpe_handler);

  // Catch an illegal instruction
  signal(SIGILL, catched_sigill_handler);

  // Catch illegal storage access errors
  signal(SIGSEGV, catched_sigsegv_handler);
}


.main

#include "exception_catcher.h"

void main()
{
    exception_catcher::set_process_exception_handlers();
    exception_catcher::set_thread_exception_handlers();
}

Simple
10 声望4 粉丝

引用和评论

0 条评论