最近一段時間,新上線的軟件在外場偶爾會出現(xiàn)異常崩潰的情況。由于試用范圍比較分散,很難一一前往現(xiàn)場定位問題。而傳統(tǒng)的log日志方法,在崩潰的情況下,并不能比較準(zhǔn)確的表示出問題位置,這使得軟件調(diào)試進(jìn)程緩慢。 后在公司前輩的指點(diǎn)下,我們想到了使用window自帶的dumpfile來記錄崩潰時刻的堆棧信息,這樣配合log日志記錄,能夠快速的定位出問題點(diǎn)。大大提高了系統(tǒng)調(diào)試效率。 經(jīng)過一段時間的調(diào)試,現(xiàn)在項(xiàng)目已相對穩(wěn)定了。想記錄下此方法,以待后續(xù)類似情況下使用。 //使所有版本都可以捕獲到異常 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); } } //程序未捕獲的異常處理函數(shù) 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; } //把當(dāng)前時刻的線程棧記錄到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; //循環(huán)檢測線程 //查看到有ADTV2_TEMP.TXT文件,則記錄下當(dāng)前時刻的堆棧 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) { //防止多次記錄當(dāng)前堆棧信息,刪除文件 ::CloseHandle(hFile); ::DeleteFile(strPath.c_str()); RecordCurStack(); } } } 然后在程序入口將異常處理接口聲明即可。 //調(diào)試信息 ::SetUnhandledExceptionFilter(ExceptionFilter); //設(shè)置異常處理函數(shù) DisableSetUnhandledExceptionFilter(); //獲取未處理的異常 這樣,在程序異常時,就可以在C盤根目錄下記錄一個dumpfile.dmp的文件。這個文件會比較大,一般有100多M,其中信息比log形式的日志豐富很多,包括了異常時的堆棧調(diào)用關(guān)系以及各對象的值。,在VS中可以直接打開。如果保留了和當(dāng)時編譯軟件一致的代碼備份的話,可以直接使用VS的debug功能定位到問題代碼行,否則,debug定位是到匯編代碼行,看起來比較麻 煩
|