zoukankan      html  css  js  c++  java
  • 使用API自己实现Crash时Dump

    程序在运行时,难免会有一些异常情况发生,特别是在条件不容许去挂调试器的时候,如何快速的定位错误的方法就显得很重要。

    日志一直都是一种很重要的定位错误的方法,出得好的日志可以方便程序员快速的定位问题所在。但日志有时也显不足:

    • 日志有时只能定位大体错误范围,却无法确认问题所在,比如程序抓到一个未知的异常。
    • 程序有时没有机会来出日志,或者能出日志的时候已经无法获得和错误相关的信息,比如程序崩溃的时候。

    在日志明显不足的时候,把进程中相关数据DUMP下来分析就是一个比较实用方便的方法。很多应用都会提供这类功能,以便在程序出现问题时可以把相关的数据发给开发者,方便开发者分析问题。类似Office这样的应用都会有这个功能,当应用崩溃时会弹出对话框,提示是否发送错误相关的数据。

    如何在自己程序中也添加类似的功能呢?其实这方面的代码很多的,在网上可以搜到很多,也有许多都是弄好的,只要你在自己程序中加几行代码就可以实现。我以前开发的程序就参用过BugTrap来实现这种功能。在CodeProject中有篇文章“Catch All Bugs with BugTrap!”,可以下载它的代码。

    也可以使用WheatyExceptionReport,它的代码定义了一个WheatyExceptionReport类型的全局变量g_WheatyExceptionReport。在WheatyExceptionReport的构造函数中,代码调用SetUnhandledExceptionFilter,并且设置异常处理器为WheatyExceptionReport::WheatyUnhandledExceptionFilter。在网上可以搜到代码,只有把它加到工程中就可以使用了(针对非托管C++代码)

    其实要实现这个功能并不是很难,自己也可以调用MS DbgHelp.dll中MiniDumpWriteDump来实现。自己实现的好处在于不用增加许多用不到的代码。

    MiniDumpWriteDump可以导出程序内部的内存、堆栈、句柄、线程、模块等程序运行相关的信息,该函数的原型如下(具体细节参考 DbgHelp.h ):

    代码
    BOOL WINAPI
    MiniDumpWriteDump(
    IN HANDLE hProcess,
    IN DWORD ProcessId,
    IN HANDLE hFile,
    IN MINIDUMP_TYPE DumpType,
    IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
    IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
    IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
    );

    使用这个函数我们可以自己写个比较简单的函数,在出现问题时候的Dump进程数据,再用WinDbg来分析问题所在。

    代码
    1 /******************************************************************
    2 Routine Description:
    3
    4 Arguments:
    5
    6 Return:
    7
    8 Remark:
    9
    10 *******************************************************************/
    11 int GenerateMiniDump(HANDLE hFile, PEXCEPTION_POINTERS pExceptionPointers)
    12 {
    13 BOOL bOwnDumpFile = FALSE;
    14 HANDLE hDumpFile = hFile;
    15 MINIDUMP_EXCEPTION_INFORMATION ExpParam;
    16
    17 typedef BOOL (WINAPI * MiniDumpWriteDumpT)(
    18 HANDLE,
    19 DWORD ,
    20 HANDLE ,
    21 MINIDUMP_TYPE ,
    22 PMINIDUMP_EXCEPTION_INFORMATION ,
    23 PMINIDUMP_USER_STREAM_INFORMATION ,
    24 PMINIDUMP_CALLBACK_INFORMATION
    25 );
    26
    27 MiniDumpWriteDumpT pfnMiniDumpWriteDump = NULL;
    28 HMODULE hDbgHelp = LoadLibrary(_T("DbgHelp.dll"));
    29 if (hDbgHelp)
    30 pfnMiniDumpWriteDump = (MiniDumpWriteDumpT)GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
    31
    32 if (pfnMiniDumpWriteDump)
    33 {
    34 if (hDumpFile==NULL || hDumpFile==INVALID_HANDLE_VALUE)
    35 {
    36 TCHAR szPath[MAX_PATH] = {0};
    37 TCHAR szFileName[MAX_PATH] = {0};
    38 TCHAR* szAppName = L"XXXXXXXXXX";
    39 TCHAR* szVersion = L"v2.0";
    40 TCHAR dwBufferSize = MAX_PATH;
    41 SYSTEMTIME stLocalTime;
    42
    43 GetLocalTime( &stLocalTime );
    44 GetTempPath( dwBufferSize, szPath );
    45
    46 StringCchPrintf( szFileName, MAX_PATH, L"%s%s", szPath, szAppName );
    47 CreateDirectory( szFileName, NULL );
    48
    49 StringCchPrintf( szFileName, MAX_PATH, L"%s%s\\%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",
    50 szPath, szAppName, szVersion,
    51 stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
    52 stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
    53 GetCurrentProcessId(), GetCurrentThreadId());
    54 hDumpFile = CreateFile(szFileName, GENERIC_READ|GENERIC_WRITE,
    55 FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
    56
    57 bOwnDumpFile = TRUE;
    58 }
    59
    60 if (hDumpFile!=INVALID_HANDLE_VALUE)
    61 {
    62 ExpParam.ThreadId = GetCurrentThreadId();
    63 ExpParam.ExceptionPointers = pExceptionPointers;
    64 ExpParam.ClientPointers = FALSE;
    65
    66 pfnMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
    67 hDumpFile, MiniDumpWithDataSegs, (pExceptionPointers ? &ExpParam : NULL), NULL, NULL);
    68
    69 if (bOwnDumpFile)
    70 CloseHandle(hDumpFile);
    71 }
    72 }
    73
    74 if (hDbgHelp!=NULL)
    75 FreeLibrary(hDbgHelp);
    76
    77 return EXCEPTION_EXECUTE_HANDLER;
    78 }

    参数PEXCEPTION_POINTERS pExceptionPointers可以通过两种方式取得:

    1、结构化异常的__except内通过GetExceptionInformation()取得。

    __try
    {
    ...
    }
    __except(GenerateMiniDump(hFile, GetExceptionInformation()),EXCEPTION_CONTINUE_EXECUTION)
    {
    }

    2、设置的UnhandledExceptionFilter传人的参数。

    SetUnhandledExceptionFilter(UnhandledExceptionFilter);

    LONG WINAPI UnhandledExceptionFilter(LPEXCEPTION_POINTERS lpExceptionInfo)
    {
    ...
    if(IsDebuggerPresent())
    {
    return EXCEPTION_CONTINUE_SEARCH;
    }

    return GenerateMiniDump(hFile,lpExceptionInfo);
    }

    其实在使用GenerateMiniDump也可以不用传入LPEXCEPTION_POINTERS值,有它只是方便WinDbg使用命令.ecxr 直接显示出现问题的位置,比如在C++异常Catch中抓到一个未知的异常,这时虽然不能取得EXCEPTION_POINTERS指针,但是出现异常时的CONTEX指针已经在内存中了,只要手工找到它,同样可以知道出现异常时的环境。

    使用WinDbg找异常CONTEXT位置的方法有很多,我平时使用的使用过WinDbg的搜索内存命令来找,查看WinNt.h中的x86 CONTEXT定义:

    代码
    1 typedef struct _CONTEXT {
    2
    3 //
    4 // The flags values within this flag control the contents of
    5 // a CONTEXT record.
    6 //
    7 // If the context record is used as an input parameter, then
    8 // for each portion of the context record controlled by a flag
    9 // whose value is set, it is assumed that that portion of the
    10 // context record contains valid context. If the context record
    11 // is being used to modify a threads context, then only that
    12 // portion of the threads context will be modified.
    13 //
    14 // If the context record is used as an IN OUT parameter to capture
    15 // the context of a thread, then only those portions of the thread's
    16 // context corresponding to set flags will be returned.
    17 //
    18 // The context record is never used as an OUT only parameter.
    19 //
    20  
    21 DWORD ContextFlags;
    22
    23 //
    24 // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
    25 // set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT
    26 // included in CONTEXT_FULL.
    27 //
    28  
    29 DWORD Dr0;
    30 DWORD Dr1;
    31 DWORD Dr2;
    32 DWORD Dr3;
    33 DWORD Dr6;
    34 DWORD Dr7;
    35
    36 //
    37 // This section is specified/returned if the
    38 // ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
    39 //
    40  
    41 FLOATING_SAVE_AREA FloatSave;
    42
    43 //
    44 // This section is specified/returned if the
    45 // ContextFlags word contians the flag CONTEXT_SEGMENTS.
    46 //
    47  
    48 DWORD SegGs;
    49 DWORD SegFs;
    50 DWORD SegEs;
    51 DWORD SegDs;
    52
    53 //
    54 // This section is specified/returned if the
    55 // ContextFlags word contians the flag CONTEXT_INTEGER.
    56 //
    57  
    58 DWORD Edi;
    59 DWORD Esi;
    60 DWORD Ebx;
    61 DWORD Edx;
    62 DWORD Ecx;
    63 DWORD Eax;
    64
    65 //
    66 // This section is specified/returned if the
    67 // ContextFlags word contians the flag CONTEXT_CONTROL.
    68 //
    69  
    70 DWORD Ebp;
    71 DWORD Eip;
    72 DWORD SegCs; // MUST BE SANITIZED
    73   DWORD EFlags; // MUST BE SANITIZED
    74   DWORD Esp;
    75 DWORD SegSs;
    76
    77 //
    78 // This section is specified/returned if the ContextFlags word
    79 // contains the flag CONTEXT_EXTENDED_REGISTERS.
    80 // The format and contexts are processor specific
    81 //
    82  
    83 BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
    84
    85 } CONTEXT;
    86
    87
    88
    89 typedef CONTEXT *PCONTEXT;

    其中ContextFlags的值一般都是CONTEXT_ALL,这个宏的值为0x1003f(如果是其它值也可以算出值),通过在WinDbg中搜索0x1003f就能快速找到异常CONTEXT指针值,如下:

    s -d 0 L?FFFFFFFF 0x1003f,从搜索出的结果中可以比较方便的找到所要的异常CONTEXT指针值,如果找到后使用命令.cxr就可以看到异常发生时的寄存器值、堆栈等信息。

  • 相关阅读:
    linux: convmv =-======pkgs.org
    ETL__pentaho__SPOON_PDI
    jira-source--plugin
    MAC OSX
    android gradle,groovy--https://blog.csdn.net/hebbely/article/details/79074460
    CentOS下Redis服务器安装配置
    mysql db imported into mongodb
    mongdb ---shard
    高精度整数
    CentOS 配置 Hadoop 2.6.5
  • 原文地址:https://www.cnblogs.com/Quincy/p/1701404.html
Copyright © 2011-2022 走看看