最近一段時間,新上線的軟件在外場偶爾會出現異常崩潰的狀況。因爲試用範圍比較分散,很難一一前往現場定位問題。而傳統的log日誌方法,在崩潰的狀況下,並不能比較準確的表示出問題位置,這使得軟件調試進程緩慢。ide
後在公司前輩的指點下,咱們想到了使用window自帶的dumpfile來記錄崩潰時刻的堆棧信息,這樣配合log日誌記錄,可以快速的定位出問題點。大大提升了系統調試效率。函數
通過一段時間的調試,如今項目已相對穩定了。想記錄下此方法,以待後續相似狀況下使用。spa
- //使全部版本均可以捕獲到異常
- 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定位是到彙編代碼行,看起來比較麻煩。debug