调试Release发布版程序的Crash错误(四)

来源:互联网 发布:轻量级c语言ide 编辑:程序博客网 时间:2024/04/30 12:11

前面几个方案都是直接定位crash的代码位置,但是在比较大型的程序中,只知道这个信息还是远远不够的,我们希望知道更多关于调用函数顺序及变量值等信息,也就是crash时调用堆栈信息。

 

   方案四:SetUnhandledExceptionFilter +StackWalker

   这个方案需要自己动手往工程里添加代码了。要实现上面的想法,需要做两件事情:1、需要在crash时有机会对程序堆栈进行处理;2、对堆栈信息进行收集。

   1、SetUnhandleExceptionFilter函数

   Windows平台下的C++程序异常通常可分为两种:结构化异常(StructuredException,可以理解为与操作系统相关的异常)和C++异常。对于结构化异常处理(SEH),可以找到很多资料,在此不细说。对于crash错误,一般由未被正常捕获的异常引起,Windows操作系统提供了一个API函数可以在程序crash之前有机会处理这些异常,就是SetUnhandleExceptionFilter函数。(C++也有一个类似函数set_terminate可以处理未被捕获的C++异常。)

   SetUnhandleExceptionFilter函数声明如下:

   LPTOP_LEVEL_EXCEPTION_FILTER WINAPISetUnhandledExceptionFilter(
     __in         LPTOP_LEVEL_EXCEPTION_FILTERlpTopLevelExceptionFilter
    );

    其中LPTOP_LEVEL_EXCEPTION_FILTER 定义如下:

    typedefLONG (WINAPI *PTOP_LEVEL_EXCEPTION_FILTER)(
       __in struct _EXCEPTION_POINTERS *ExceptionInfo
    );
    typedefPTOP_LEVEL_EXCEPTION_FILTER LPTOP_LEVEL_EXCEPTION_FILTER;

   简单来说,SetUnhandleExceptionFilter允许我们设置一个自己的函数作为全局SEH过滤函数,当程序crash前会调用我们的函数进行处理。我们可以利用的是_EXCEPTION_POINTERS结构类型的变量ExceptionInfo,它包含了对异常的描述以及发生异常的线程状态,过滤函数可以通过返回不同的值来让系统继续运行或退出应用程序。

    关于SetUnhandleExceptionFilter 函数的具体用法和示例请参考MSDN。

 

   2、StackWalker
   现在我们已经有机会可以在crash之前对程序状态信息进行处理了,只需要生成并保存堆栈信息就大功告成了。Windows的dbghelp.dll库提供了一个函数可以得到当前堆栈信息:StackWalk64(在Win2K以前版本中为StackWalk)。该函数声明如下:

    BOOLWINAPI StackWalk64(
     __in         DWORD MachineType,
     __in         HANDLE hProcess,
     __in         HANDLE hThread,
     __in_out     LPSTACKFRAME64 StackFrame,
     __in_out     PVOID ContextRecord,
     __in         PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
     __in         PFUNCTION_TABLE_ACCESS_ROUTINE64FunctionTableAccessRoutine,
     __in         PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
     __in         PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
    );
   该函数的具体用法可以参考MSDN。在这里推荐一个牛人写好的StackWalker,可以直接拿来用,开源的。StackWalker提供了一个基类,给出了几个简单的接口,可以方便地生成堆栈信息,并且支持一系列VC版本,非常好用。我们可以自己写一个子类,并重载虚函数OnOutput,就可以将堆栈信息输出为特定格式了。StackWalker的地址为:http://www.codeproject.com/KB/threads/StackWalker.aspx。

   不过对于Release版本来说,StackWalk64函数获得的堆栈信息有可能不完整。如果异常是由MFC的模块抛出,那么获得的堆栈可能缺少前面调用模块信息。另外,StackWalk64需要最新的dbghelp.dll文件支持才能工作;要正确输出crash的函数名和行号,需要要pdb文件支持。以上不足有可能影响输出信息的完整性和效果,而对于发布在外的程序,要带上pdb文件几乎不可能,因此这个方案还是有缺憾的,比较适用于本地的release版本调试。

   下一篇我们将介绍一个更加完善的解决方案。

原创粉丝点击