【转载请注明出处】:http://blog.csdn.net/longlong530
一. 背景
没人能保证自己的软件在各种未知环境运行下,会木有任何问题。那么如果程序崩溃了怎么办?看日志?日志不全又怎么办?日志能帮你定位的多细致呢?如果能有种方法记录程序最后工作的状态,比如堆栈调用情况等,那么我们就可以获悉“它是如何挂掉的~”
二. 调研
我们对程序bug引起的程序崩溃的五种定位方法进行了调研,并最终选择方案5为我所在项目使用的程序崩溃定位方案。
方案1: 崩溃地址 + MAP文件
方案2: 崩溃地址 + MAP文件 + COD文件
这个方案主要是为了解决方案1的缺陷。由于VC8以后的版本都不再支持MAP文件中产生代码行信息,所以增加了COD文件的方法来定位问题。
方案3: 崩溃地址 + PDB文件 + CrashFinder
说明:前三种方案,其实只需要用户告知崩溃地址,然后在本地查找crash地址就可以了,但是定位crash的过程非常不方便,如果crash的情况比
较多,前三种方案都不合适。而且,前三种方案均不能生成堆栈调用信息,对于debug的作用有限。
方案4:SetUnhandledExceptionFilter + StackWalker
此方法需要pdb文件才能够正确生成堆栈调用的函数行号及代码行号,因此只适合本地release版的调试。
方案5:SetUnhandledExceptionFilter + Minidump
该方法是我们使用的捕获dump文件的工具,所以这里对其重点介绍一下。
三. 代码共享
核心代码如下:
#include <stdio.h> #include <time.h> #include <windows.h> #include <DbgHelp.h> #pragma comment(lib, "DbgHelp.lib") LONG WINAPI TopLevelFilter( struct _EXCEPTION_POINTERS *pExceptionInfo ) { // 返回EXCEPTION_CONTINUE_SEARCH,让程序停止运行 LONG ret = EXCEPTION_CONTINUE_SEARCH; time_t nowtime; time(&nowtime); struct tm *pTime = localtime(&nowtime); char szFile[128] = {0}; // 设置core文件生成目录和文件名 sprintf(szFile, "c:\\%4d.%02d.%02d_%02d.%02d.%02d.dmp", pTime->tm_year+1900, pTime->tm_mon+1, pTime->tm_mday, pTime->tm_hour, pTime->tm_min, pTime->tm_sec); HANDLE hFile = ::CreateFile(szFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { MINIDUMP_EXCEPTION_INFORMATION ExInfo; ExInfo.ThreadId = ::GetCurrentThreadId(); ExInfo.ExceptionPointers = pExceptionInfo; ExInfo.ClientPointers = NULL; // write the dump BOOL bOK = MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL ); ret = EXCEPTION_EXECUTE_HANDLER; ::CloseHandle(hFile); } return ret; }
代码移植方法:
1 将下面的头文件拷贝到你的代码里
#include <time.h>
#include <windows.h>
#include <DbgHelp.h>
2 引入gdbhelp的静态库
#pragma comment(lib, "DbgHelp.lib")
3 将TopLevelFilter函数拷贝到你的代码里
4 在mian函数里,加入下面的代码
::SetUnhandledExceptionFilter(TopLevelFilter);
5 修改工程属性->配置属性->常规->项目默认值, 将字符集配置修改为使用多字节字符集
6 编译运行即可。
四. 代码解释
a)SetUnhandledExceptionFilter
SetUnhandledExceptionFilter 函数声明如下:
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter( __in LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter );
b) Minidump
Minidump:minidump(小存储器转储)可以理解为一个dump文件,里面记录了能够帮助调试crash的最小有用信息。实际上,如果你在 系统属性 ->径下生成一个.dmp后缀的文件,这个文件就是minidump文件,只不过这个是内核态的minidump。生成minidump文件的API函数:MiniDumpWriteDump,该函数需要dbghelp.lib支持。
BOOL WINAPI MiniDumpWriteDump( __in HANDLE hProcess, __in DWORD ProcessId, __in HANDLE hFile, __in MINIDUMP_TYPE DumpType, __in PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, __in PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, __in PMINIDUMP_CALLBACK_INFORMATION CallbackParam );
五. 注意事项
对于release版的程序来说,很多代码是经过编译器优化过的,因此定位的时候可能会有所偏差,大家可以考虑设置选项去掉代码优化。使用Minidump的详细方法可参见:http://vicchina.51.net/research/other/seh/minidumps/intro.htm
【转载请注明出处】:http://blog.csdn.net/longlong530