灯火互联
管理员
管理员
  • 注册日期2011-07-27
  • 发帖数41778
  • QQ
  • 火币41290枚
  • 粉丝1086
  • 关注100
  • 终身成就奖
  • 最爱沙发
  • 忠实会员
  • 灌水天才奖
  • 贴图大师奖
  • 原创先锋奖
  • 特殊贡献奖
  • 宣传大使奖
  • 优秀斑竹奖
  • 社区明星
阅读:3423回复:0

[C++技术]C++记录程序崩溃时的dumpfile

楼主#
更多 发布于:2012-09-23 13:18
最近一段时间,新上线的软件在外场偶尔会出现异常崩溃的情况。由于试用范围比较分散,很难一一前往现场定位问题。而传统的log日志方法,在崩溃的情况下,并不能比较准确的表示出问题位置,这使得软件调试进程缓慢。
  后在公司前辈的指点下,我们想到了使用window自带的dumpfile来记录崩溃时刻的堆栈信息,这样配合log日志记录,能够快速的定位出问题点。大大提高了系统调试效率。
  经过一段时间的调试,现在项目已相对稳定了。想记录下此方法,以待后续类似情况下使用。
//使所有版本都可以捕获到异常
void DisableSetUnhandledExceptionFilter()
{
   void *addr = (void*)GetProcaddress(LoadLibrary(_T("kernel32.dll")), "SetUnhandledExceptionFilter");

   if (addr)
   {
       unsigned char code[16];
       int size = 0;
       code[size++] = 0x33;
       code[size++] = 0xC0;
       code[size++] = 0xC2;
       code[size++] = 0x04;
       code[size++] = 0x00;

       DWORD dwOldFlag, dwTempFlag;
       VirtualProtect(addr, size, PAGE_READWRITE, ;dwOldFlag);
       WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);
       VirtualProtect(addr, size, dwOldFlag, ;dwTempFlag);
   }
}

//程序未捕获的异常处理函数
LONG WINAPI ExceptionFilter(struct _EXCEPTION_POinterS *ExceptionInfo)
{
   ::AfxMessageBox("ExceptionFilter");

   HANDLE hFile = ::CreateFile( _T("C:\\dumpfile.dmp"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
   if( hFile != INVALID_HANDLE_VALUE)
   {
       MINIDUMP_EXCEPTION_INFORMATION einfo;
       einfo.ThreadId = ::GetCurrentThreadId();
       einfo.ExceptionPointers = ExceptionInfo;
       einfo.ClientPointers = FALSE;

       ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpWithFullMemory, ;einfo, NULL, NULL);
       ::CloseHandle(hFile);
   }

   return 0;
}

//把当前时刻的线程栈记录到DUMP文件中
int RecordCurStack()
{
   HANDLE hFile = ::CreateFile( _T("C:\\dumpfile.dmp"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
   if( hFile != INVALID_HANDLE_VALUE)
   {
       ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpWithFullMemory  ,NULL, NULL, NULL);

       ::CloseHandle(hFile);
       return 1;
   }

   return 0;
}


bool bCreateDumpThrd = true;
//循环检测线程
//查看到有ADTV2_TEMP.TXT文件,则记录下当前时刻的堆栈
void CreateDumpThrd(void* pv)
{
   HANDLE hFile;
   string strPath = FileAssist::GetExePath() + "\\ADTV2_TEMP.TXT";
   while(bCreateDumpThrd)
   {
       //每5秒检测一次
       Sleep(5000);
       hFile = CreateFileA(strPath.c_str(),    // file to open
           GENERIC_READ,          // open for reading
           FILE_SHARE_READ,       // share for reading
           NULL,                  // default security
           OPEN_EXISTING,         // existing file only
           FILE_ATTRIBUTE_NORMAL, // normal file
           NULL);                 // no attr. template

       if (hFile != INVALID_HANDLE_VALUE)
       {
           //防止多次记录当前堆栈信息,删除文件
           ::CloseHandle(hFile);
           ::DeleteFile(strPath.c_str());
           RecordCurStack();
       }
   }
}
然后在程序入口将异常处理接口声明即可。
//调试信息
::SetUnhandledExceptionFilter(ExceptionFilter); //设置异常处理函数
DisableSetUnhandledExceptionFilter();           //获取未处理的异常
这样,在程序异常时,就可以在C盘根目录下记录一个dumpfile.dmp的文件。这个文件会比较大,一般有100多M,其中信息比log形式的日志丰富很多,包括了异常时的堆栈调用关系以及各对象的值。,在VS中可以直接打开。如果保留了和当时编译软件一致的代码备份的话,可以直接使用VS的debug功能定位到问题代码行,否则,debug定位是到汇编代码行,看起来比较麻烦。

喜欢0 评分0
游客

返回顶部