您的位置:首页 > 其它

程序异常崩溃捕捉-dmp文件及Windbg分析

2014-06-29 22:09 405 查看
客户端程序发布之后,程序会出现突然崩溃的情况,客户机器上并不一样安装有Windbg工具, 所以最好是自己程序里面能够捕捉exception/crash,并且生成crash dump,然后通过网络传回到自己服务器。

 

捕捉exception 可以用API 函数
SetUnhandledExceptionFilter 。生成crash dump 可以用DbgHelp.dll 里面的MiniDumpWriteDump函数。

 

LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter( __in LPTOP_LEVEL_EXCEPTION_FILTERlpTopLevelExceptionFilter );

 

BOOL WINAPI MiniDumpWriteDump( __in HANDLE hProcess, __in DWORDProcessId, __in HANDLEhFile, __in MINIDUMP_TYPE
DumpType, __in PMINIDUMP_EXCEPTION_INFORMATIONExceptionParam, __in PMINIDUMP_USER_STREAM_INFORMATIONUserStreamParam, __in PMINIDUMP_CALLBACK_INFORMATIONCallbackParam );

 代码示例:

#include <dbghelp.h>  

    #include <shellapi.h>  

    #include <shlobj.h>  

      

      

    // 自定义的exectpion filter  

    LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *pExceptionPointers)  

    {  

      

        SetErrorMode( SEM_NOGPFAULTERRORBOX );  

      

        //收集信息  

         CStringW strBuild;  

        strBuild.Format(L"Build: %s %s", __DATE__, __TIME__);  

        CStringW strError;  

        HMODULE hModule;  

        WCHAR szModuleName[MAX_PATH] = L"";  

        GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)pExceptionPointers->ExceptionRecord->ExceptionAddress, &hModule);  

        GetModuleFileName(hModule, szModuleName, ARRAYSIZE(szModuleName));  

        strError.AppenedFormat(L"%s %d , %d ,%d.", szModuleName,pExceptionPointers->ExceptionRecord->ExceptionCode,       pExceptionPointers->ExceptionRecord->ExceptionFlags, pExceptionPointers->ExceptionRecord->ExceptionAddress);  

      

        //生成 mini crash dump  

        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);  

      

        MINIDUMP_USER_STREAM UserStream[2];  

        MINIDUMP_USER_STREAM_INFORMATION UserInfo;  

        UserInfo.UserStreamCount = 1;  

        UserInfo.UserStreamArray = UserStream;  

        UserStream[0].Type = CommentStreamW;  

        UserStream[0].BufferSize = strBuild.GetLength()*sizeof(WCHAR);  

        UserStream[0].Buffer = strBuild.GetBuffer();  

        UserStream[1].Type = CommentStreamW;  

        UserStream[1].BufferSize = strError.GetLength()*sizeof(WCHAR);  

        UserStream[1].Buffer = strError.GetBuffer();  

      

        ExpParam.ThreadId = GetCurrentThreadId();  

        ExpParam.ExceptionPointers = pExceptionPointers;  

        ExpParam.ClientPointers = TRUE;  

          

        MINIDUMP_TYPE MiniDumpWithDataSegs = MiniDumpNormal   

                | MiniDumpWithHandleData   

                | MiniDumpWithUnloadedModules   

                | MiniDumpWithIndirectlyReferencedMemory   

                | MiniDumpScanMemory   

                | MiniDumpWithProcessThreadData   

                | MiniDumpWithThreadInfo;  

        bMiniDumpSuccessful = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),   

                        hDumpFile, MiniDumpWithDataSegs, &ExpParam, NULL, NULL);  

      // 上传mini dump 到自己服务器(略)  

      ...  

      

      return EXCEPTION_CONTINUE_SEARCH; //或者 EXCEPTION_EXECUTE_HANDLER 关闭程序  

    }  

       

    int _tmain()  

    {  

      // 设置 execption filter  

      SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);  

      ....  

      return 0;  

    }

 

在程序异常生成dmp文件后,我们需要使用该文件对应的pdb文件来定位程序崩溃的源文件:

建立PDB文件基本上是这几个选项,a)在project setting的C++属性中,选择生成program database,或者直接手动加入/Zi选项,如果有/Z7,把它替换成/Zi。b)在link选项中选择Generate debug info,或者直接加入/debug选项,另外注意/pdb应该是类似/PDB:".\Release/yourproj.pdb"这样的,如果不是手动修改。

有的人会担心包含debug信息以后文件变大,修改link中这两个选项/OPT:REF和/OPT:ICF会减小最终生成的文件大小。在这里借用一下John Robbins的截图。





windbg 调试崩溃

1、程序崩溃发生过程

这是一个对文件进行处理的模块,而处理模块在处理之前,需查询被处理的文件是否值得处理。这个任务执行过程中发生了崩溃,问题就发生在查询模块。

2、提取dump文件

3、分析dump:

1)启动windbg,file--open crash dump 配置符号库,reload完成 。

2)使用命令 :.ecxr获得进程崩溃时寄存器的内容

0:021> .ecxr (意指恢复崩溃时所有寄存器的内容,包括堆栈等)

eax=0532d414 ebx=00000fec ecx=000003fb edx=00000000 esi=0532c428 edi=00000000

eip=750f53ea esp=0490f22c ebp=0490f234 iopl=0 nv up ei pl nz na pe nc

cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206

msvcr80!memmove+0x5a:

750f53ea f3a5 rep movs dword ptr es:[edi],dword ptr [esi] es:002b:00000000=???????? ds:002b:0532c428=00000000

3)使用命令:k 显示堆栈

0:021> k (.ecxr不能直接显示堆栈,需使用k显示堆栈)

*** Stack trace for last set context - .thread/.cxr resets it

ChildEBP RetAddr

WARNING: Stack unwind information not available. Following frames may be wrong.

0490f234 03ba4806 msvcr80!memmove+0x5a(kxewfsys调用memmove,memmove是个字符串操作函数,能把字符串的一部分复制到另一部分,这里出问题,可能是复制时传递的指针有问题,或者字符串的大小有问题)

0490f25c 03ba2547 kxewfsys!__ovfl_get+0xa6 (调用memmove之前内部的一些处理)

0490f27c 03ba4a74 kxewfsys!__bt_cmp+0x77

0490f2a8 03ba2679 kxewfsys!__bt_search+0x74

0490f2c4 03ba1409 kxewfsys!__bt_get+0x49

0490f2d8 03ba7448 kxewfsys!IKBDBImpl::Get+0x19

0490f2f8 03ba750d kxewfsys!CFdbFileInfo::Search+0x28 [e:eingsoft_eubauilduild_srckicekice_kxewhitesrckxewfssysfdbfileinfo.cpp @ 483]

0490f338 03ba9c63 kxewfsys!CFdbFileInfo::QueryFileInfo+0x4d [e:eingsoft_eubauilduild_srckicekice_kxewhitesrckxewfssysfdbfileinfo.cpp @ 546]

0490f380 028210d1 kxewfsys!CFdbManager::QueryFileInfo+0xa3 [e:eingsoft_eubauilduild_srckicekice_kxewhitesrckxewfssysfdbmanager.cpp @ 370] (kxewfsys 是处理查询模块)

*** ERROR: Symbol file could not be found. Defaulted to export symbols for kspfeng.dll -

0490f390 03b1323c kxewhite!kxe_white_query_file_info+0x21 [e:eingsoft_eubauilduild_srckicekice_kxewhitesrckxewfssdkkxewfs.cpp @ 103] (将文件提交进行查询.)

0490f45c 03b13442 kspfeng!KSEGetAddonEntries+0x21fbc (kspfeng.dll是文件处理模块用到的公共功能的封装文件)

*** ERROR: Symbol file could not be found. Defaulted to export symbols for ksecore.dll -

0490f498 03abcaa4 kspfeng!KSEGetAddonEntries+0x221c2

0490f4c8 03b07fd5 ksecore+0x1caa4

0490f52c 03b1c52b kspfeng!KSEGetAddonEntries+0x16d55

0490f860 5019dd7c kspfeng!KSEGetAddonEntries+0x2b2ab

0490f864 01c95695 0x5019dd7c

0490f868 1a77217c 0x1c956
bcea
95

0490f86c 01c95693 0x1a77217c

0490f870 1a7982dc 0x1c95693

0490f874 01c95693 0x1a7982dc

4、总结崩溃原因

查询模块,对文件路径的处理存在bug。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: