使用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 ):

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
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来分析问题所在。

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
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定义:

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
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就可以看到异常发生时的寄存器值、堆栈等信息。

转载于:https://www.cnblogs.com/Quincy/archive/2010/04/11/1701404.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值