织梦CMS - 轻松建站从此开始!

罗索实验室

当前位置: 主页 > 杂项技术 > VC(MFC) >

VS2008调试Release程序--Dump文件方式

落鹤生 发布于 2014-07-11 14:44 点击:次 
因为release版本来就少了很多调试信息,更何况一般都是发布出去由用户使用,crash的现场很难保留和重现。目前有一些方法可以解决:崩溃地址 + MAP文件;MAP文件;SetUnhandledExceptionFilter + Minidump。本文重点解决Minidump方式。
TAG: 调试  Minidump  Debug  

Windows平台下用C++开发应用程序,最不想见到的情况恐怕就是程序崩溃,而要想解决引起问题的bug,最困难的应该就是调试release版本了。因为release版本来就少了很多调试信息,更何况一般都是发布出去由用户使用,crash的现场很难保留和重现。目前有一些方法可以解决:崩溃地址 + MAP文件;MAP文件;SetUnhandledExceptionFilter + Minidump。本文重点解决Minidump方式。

一、Minidump文件生成

  1、Minidump概念

    minidump(小存储器转储)可以理解为一个dump文件,里面记录了能够帮助调试crash的最小有用信息。实际上,如果你在系统属性 -> 高级 -> 启动和故障恢复 -> 设置 -> 写入调试信息中选择小内存转储(64 KB)”的话,当系统意外停止时都会在C:\Windows\Minidump\路径下生成一个.dmp后缀的文件,这个文件就是minidump文件,只不过这个是内核态的minidump

   我们要生成的是用户态的minidump,文件中包含了程序运行的模块信息、线程信息、堆栈调用信息等。而且为了符合其mini的特性,dump文件是压缩过的。

2、生成minidump文件

通过drwtsn32NTSDCDB等调试工具生成Dump文件, drwtsn32存在的缺点虽然NTSDCDB可以完全解决,但并不是所有的操作系统中都安装了NTSDCDB等调试工具。根据MiniDumpWriteDump接口,完全可以程序自动生成Dump文件。

3、  自动生成Minidump文件

当程序遇到未处理异常(主要指非指针造成)导致程序崩溃死,如果在异常发生之前调用了SetUnhandledExceptionFilter()函数,异常交给函数处理。MSDN中描述为:

Issuing SetUnhandledExceptionFilter replaces the existing top-level exception filter for all existing and all future threads in the calling process.

 因而,在程序开始处增加SetUnhandledExceptionFilter()函数,并在函数中利用适当的方法生成Dump文件,即可实现需要的功能。

生成dump文件类(minidump.h)

  1. #pragma once 
  2.  
  3. #include <windows.h> 
  4. #include <imagehlp.h> 
  5. #include <stdlib.h> 
  6. #pragma comment(lib, "dbghelp.lib") 
  7.  
  8. inline BOOL IsDataSectionNeeded(const WCHAR* pModuleName) 
  9.     if(pModuleName == 0) 
  10.     { 
  11.        return FALSE; 
  12.     } 
  13.   
  14.     WCHAR szFileName[_MAX_FNAME] = L""
  15.     _wsplitpath(pModuleName, NULL, NULL, szFileName, NULL); 
  16.     if(wcsicmp(szFileName, L"ntdll") == 0) 
  17.        return TRUE; 
  18.   
  19.     return FALSE;  
  20.  
  21. inline BOOL CALLBACK MiniDumpCallback(PVOID                            pParam,  
  22.                                   const PMINIDUMP_CALLBACK_INPUT   pInput,  
  23.                                   PMINIDUMP_CALLBACK_OUTPUT        pOutput) 
  24.     if(pInput == 0 || pOutput == 0) 
  25.        return FALSE; 
  26.   
  27.     switch(pInput->CallbackType) 
  28.     { 
  29.     case ModuleCallback:  
  30.        if(pOutput->ModuleWriteFlags & ModuleWriteDataSeg)  
  31.            if(!IsDataSectionNeeded(pInput->Module.FullPath))  
  32.               pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);  
  33.     case IncludeModuleCallback: 
  34.     case IncludeThreadCallback: 
  35.     case ThreadCallback: 
  36.     case ThreadExCallback: 
  37.        return TRUE; 
  38.     default:; 
  39.     } 
  40.     return FALSE; 
  41.  
  42. //创建Dump文件 
  43. inline void CreateMiniDump(EXCEPTION_POINTERS* pep, LPCTSTR strFileName) 
  44.     HANDLE hFile = CreateFile(strFileName, GENERIC_WRITE, 0
  45. , NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 
  46.     if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) 
  47.     { 
  48.        MINIDUMP_EXCEPTION_INFORMATION mdei; 
  49.        mdei.ThreadId           = GetCurrentThreadId(); 
  50.        mdei.ExceptionPointers  = pep; 
  51.        mdei.ClientPointers     = FALSE; 
  52.        MINIDUMP_CALLBACK_INFORMATION mci; 
  53.        mci.CallbackRoutine     = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback; 
  54.        mci.CallbackParam       = 0; 
  55.        MINIDUMP_TYPE mdt       = (MINIDUMP_TYPE)0x0000ffff; 
  56.        MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId()
  57. , hFile, MiniDumpNormal, &mdei, NULL, &mci); 
  58.        CloseHandle(hFile);  
  59.     } 
  60.  
  61. LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter
  62. (LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) 
  63.     return NULL; 
  64.  
  65. BOOL PreventSetUnhandledExceptionFilter() 
  66.     HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll")); 
  67.     if (hKernel32 ==   NULL) 
  68.        return FALSE; 
  69.     void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter"); 
  70.     if(pOrgEntry == NULL) 
  71.        return FALSE; 
  72.     unsigned char newJump[ 100 ]; 
  73.     DWORD dwOrgEntryAddr = (DWORD) pOrgEntry; 
  74.     dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far 
  75.     void *pNewFunc = &MyDummySetUnhandledExceptionFilter; 
  76.     DWORD dwNewEntryAddr = (DWORD) pNewFunc; 
  77.     DWORD dwRelativeAddr = dwNewEntryAddr -  dwOrgEntryAddr; 
  78.     newJump[ 0 ] = 0xE9;  // JMP absolute 
  79.     memcpy(&newJump[ 1 ], &dwRelativeAddr, sizeof(pNewFunc)); 
  80.     SIZE_T bytesWritten; 
  81.     BOOL bRet = WriteProcessMemory(GetCurrentProcess(),
  82.     pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten); 
  83.     return bRet; 
  84.  
  85. LONG WINAPI UnhandledExceptionFilterEx(struct _EXCEPTION_POINTERS *pException) 
  86.     TCHAR szMbsFile[MAX_PATH] = { 0 }; 
  87.     ::GetModuleFileName(NULL, szMbsFile, MAX_PATH); 
  88.     TCHAR* pFind = _tcsrchr(szMbsFile, '\\'); 
  89.     if(pFind) 
  90.     { 
  91.        *(pFind+1) = 0; 
  92.        _tcscat(szMbsFile, _T("CreateMiniDump.dmp")); 
  93.        CreateMiniDump(pException,szMbsFile); 
  94.     } 
  95.   
  96.     // TODO: MiniDumpWriteDump 
  97.     FatalAppExit(-1,  _T("Fatal Error")); 
  98.     return EXCEPTION_CONTINUE_SEARCH; 
  99.  
  100. //运行异常处理 
  101. void RunCrashHandler() 
  102.     SetUnhandledExceptionFilter(UnhandledExceptionFilterEx); 
  103.     PreventSetUnhandledExceptionFilter(); 

//测试实现文件

// 一个有函数调用的类

//

class CrashTest

{

public:

    void Test()

    {

       Crash();

    }

 

private:

    void Crash()

    {

           strcpy(NULL,"adfadfg");

    }

};

 

int _tmain(int argc, _TCHAR* argv[])

{

    //设置异常处理函数

    RunCrashHandler();

 

    CrashTest test;

    test.Test();

    getchar();

    return 0;

}

 

注意事项

1、需要配置debug选项,在C/C++选项à常规à调试信息格式(设置为程序数据库(/Zi));在连接器选项—>调试à生成调试信息(设置为是)C/C++选项à优化à禁用。(参见下图)

2  可执行文件(exe)必须找到dbghelp.dll,才能生成Dump文件。这个DLL可以从调试工具包中找到。

3*.exe*.pdb*.dumpdbghelp.dll 这四个文件需要放在同一目录下才好调试,双击dump文件时,就可以自动关联到出错代码位置。

4、为了获取更多更深入的调试信息,需要把程序优化开关设置成禁用。

5 当异常代码定位成功以后,如果无法阻止异常的产生,可以用 __try 结构包装异常代码,__try try 不同,前者可以捕获非法指针产生的异常。

__try {

// 会异常的函数

}

__except( EXCEPTION_EXECUTE_HANDLER ){

// 异常处理

}

二、调试Minidump文件

  1. 双击minidump文件(*.dmp)。默认会启动vs2008。
  2. 菜单Tools/Options, Debugging/Symbols,增加PDB文件路径。注:如果minidump文件与pdb文件在同一目录,就不用设置这个了。
  3. 若调试的程序需要微软基础库的PDB信息,可以增加一个路径为:
  4. http://msdl.microsoft.com/download/symbols
  5. 在界面下方Cache Symbol From symbol…选择本地存储这些Symbols的路径。 注:如果本地已存储过微软基础库的pdb,就直接按照此步操作设置本地路径,不必执行上一步操作了。
  6. 设置代码路径:

设置代码路径:

刚打开的dmp工程,进入解决方案的属性。在这里输入源程序的代码路径。注:一定是sln所在的路径,而不是vcproj的路径!



 

按F5,debug吧。

(byxdaz)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www1.rosoo.net/a/201407/17009.html]
本文出处:CSDN博客 作者:byxdaz 原文
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容