Advertisement

windows客户端开发--让你的客户端崩溃之前生成dump文件

阅读量:

debug时候我们可以很快速、精确的定位问题所在。

但是对于release版本,我们往往无能为力。

尤其面对一群难缠的客户,情况就会更加糟糕。

而且对于release版本来说,crash的时候日志系统往往起不到任何作用。而且,我们也不可能捕获所有的异常,更何况,客户端崩溃的原因都是我们捕获不了的异常。

这就需要dump 文件了。

dump 文件是C++程序发生异常时,保存当时程序运行状态的文件,是调试异常程序重要的方法,所以程序崩溃时,除了日志文件,dump文件便成了我们查找错误的最后一根救命的稻草。

Not all bugs can be found prior to release, which means not all bugs that throw exceptions can be found before release. Fortunately, Microsoft has included in the Platform SDK a function to help developers collect information on exceptions that are discovered by users. The MiniDumpWriteDump function writes the necessary crash dump information to a file without saving the whole process space. This crash dump information file is called a minidump. This technical article provides info about how to write and use a minidump.

SetUnhandledExceptionFilter
调用SetUnhandledExceptionFilter注册一个自定义的异常处理回调函数,也就是在回调函数中进行写minidump文件。

MiniDumpWriteDump
这个函数就是用来写dump了:

复制代码
    #include <dbghelp.h>
    #include <shellapi.h>
    #include <shlobj.h>
    
    int GenerateDump(EXCEPTION_POINTERS* pExceptionPointers)
    {
    BOOL bMiniDumpSuccessful;
    WCHAR szPath[MAX_PATH]; 
    WCHAR szFileName[MAX_PATH]; 
    WCHAR* szAppName = L"AppName";
    WCHAR* szVersion = L"v1.0";
    DWORD dwBufferSize = MAX_PATH;
    HANDLE hDumpFile;
    SYSTEMTIME stLocalTime;
    MINIDUMP_EXCEPTION_INFORMATION ExpParam;
    
    GetLocalTime( &stLocalTime );
    GetTempPath( dwBufferSize, szPath );
    
    StringCchPrintf( szFileName, MAX_PATH, L"%s%s", szPath, szAppName );
    CreateDirectory( szFileName, NULL );
    
    StringCchPrintf( szFileName, MAX_PATH, L"%s%s\ %s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp", 
               szPath, szAppName, szVersion, 
               stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay, 
               stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond, 
               GetCurrentProcessId(), GetCurrentThreadId());
    hDumpFile = CreateFile(szFileName, GENERIC_READ|GENERIC_WRITE, 
                FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
    
    ExpParam.ThreadId = GetCurrentThreadId();
    ExpParam.ExceptionPointers = pExceptionPointers;
    ExpParam.ClientPointers = TRUE;
    
    bMiniDumpSuccessful = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), 
                    hDumpFile, MiniDumpWithDataSegs, &ExpParam, NULL, NULL);
    
    return EXCEPTION_EXECUTE_HANDLER;
    }
    
    
    void SomeFunction()
    {
    __try
    {
        int *pBadPtr = NULL;
        *pBadPtr = 0;
    }
    __except(GenerateDump(GetExceptionInformation()))
    {
    }
    }
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI写代码

最后,献上完整的代码:

新建一个nimidumo.h:

复制代码
    #pragma once  
    #include <windows.h>  
    #include <DbgHelp.h>  
    #include <stdlib.h>  
    #pragma comment(lib, "dbghelp.lib")  
    
    #ifndef _M_IX86  
    #error "The following code only works for x86!"  
    #endif  
    
    inline BOOL IsDataSectionNeeded(const WCHAR* pModuleName)
    {
    if (pModuleName == 0)
    {
        return FALSE;
    }
    
    WCHAR szFileName[_MAX_FNAME] = L"";
    _wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);
    
    if (_wcsicmp(szFileName, L"ntdll") == 0)
        return TRUE;
    
    return FALSE;
    }
    
    inline BOOL CALLBACK MiniDumpCallback(PVOID                            pParam,
    const PMINIDUMP_CALLBACK_INPUT   pInput,
    PMINIDUMP_CALLBACK_OUTPUT        pOutput)
    {
    if (pInput == 0 || pOutput == 0)
        return FALSE;
    
    switch (pInput->CallbackType)
    {
    case ModuleCallback:
        if (pOutput->ModuleWriteFlags & ModuleWriteDataSeg)
            if (!IsDataSectionNeeded(pInput->Module.FullPath))
                pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);
    case IncludeModuleCallback:
    case IncludeThreadCallback:
    case ThreadCallback:
    case ThreadExCallback:
        return TRUE;
    default:;
    }
    
    return FALSE;
    }
    
    inline void CreateMiniDump(PEXCEPTION_POINTERS pep, LPCTSTR strFileName)
    {
    HANDLE hFile = CreateFile(strFileName, GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    
    if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
    {
        MINIDUMP_EXCEPTION_INFORMATION mdei;
        mdei.ThreadId = GetCurrentThreadId();
        mdei.ExceptionPointers = pep;
        mdei.ClientPointers = NULL;
    
        MINIDUMP_CALLBACK_INFORMATION mci;
        mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
        mci.CallbackParam = 0;
    
        ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpNormal, (pep != 0) ? &mdei : 0, NULL, &mci);
    
        CloseHandle(hFile);
    }
    }
    
    LONG __stdcall MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
    {
    CreateMiniDump(pExceptionInfo, L"core.dmp");
    MessageBox(0, L"Error", L"error", MB_OK);
    /*printf("Error   address   %x/n", pExceptionInfo->ExceptionRecord->ExceptionAddress);
    printf("CPU   register:/n");
    printf("eax   %x   ebx   %x   ecx   %x   edx   %x/n", pExceptionInfo->ContextRecord->Eax,
        pExceptionInfo->ContextRecord->Ebx, pExceptionInfo->ContextRecord->Ecx,
        pExceptionInfo->ContextRecord->Edx);*/
    return   EXCEPTION_EXECUTE_HANDLER;
    }
    
    // 此函数一旦成功调用,之后对 SetUnhandledExceptionFilter 的调用将无效  
    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);
    }
    }
    
    void InitMinDump()
    {
    //注册异常处理函数  
    SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
    
    //使SetUnhandledExceptionFilter  
    DisableSetUnhandledExceptionFilter();
    }
    #pragma once
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI写代码

在我们的main.cc中使用:

复制代码
    #include <iostream>  
    #include "minidump.h"  
    int WINAPI WinMain(HINSTANCE hInstance,    HINSTANCE hPrevInstance,   char * lpCmdLine, int nCmdShow)
    {
    SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
    _asm   int   3   //只是为了让程序崩溃
    return 0;
    }
    
      
      
      
      
      
      
      
      
    
    AI写代码

这样,在程序崩溃的时候,我们就会得到一个core.dump文件。

接下来也是重点,如何根据dump文件定位代码:
在VS中 文件->打开->文件
这里写图片描述

然后就是进行调试:
点击 使用仅限本机进行调试
这里写图片描述

这里需要注意的是:
dump文件 pdb文件 源码 要保持版本一直,否则就无法准确定位了。

全部评论 (0)

还没有任何评论哟~