程式異常崩潰捕捉-dmp檔案及Windbg分析
#include <dbghelp.h>
#include <shellapi.h>
#include <shlobj.h>
// 自定義的exectpion filter
LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *pExceptionPointers)
{
SetErrorMode( SEM_NOGPFAULTERRORBOX );
//收集資訊
CStringW strBuild;
strBuild.Format(L"Build: %s %s", __DATE__, __TIME__);
CStringW strError;
HMODULE hModule;
WCHAR szModuleName[MAX_PATH] = L"";
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)pExceptionPointers->ExceptionRecord->ExceptionAddress, &hModule);
GetModuleFileName(hModule, szModuleName, ARRAYSIZE(szModuleName));
strError.AppenedFormat(L"%s %d , %d ,%d.", szModuleName,pExceptionPointers->ExceptionRecord->ExceptionCode, pExceptionPointers->ExceptionRecord->ExceptionFlags, pExceptionPointers->ExceptionRecord->ExceptionAddress);
//生成 mini crash dump
BOOL bMiniDumpSuccessful;
WCHAR szPath[MAX_PATH];
WCHAR szFileName[MAX_PATH];
WCHAR* szAppName = L"AppName";
WCHAR* szVersion = L"v1.0";
DWORD dwBufferSize = MAX_PATH;
HANDLE hDumpFile;
SYSTEMTIME stLocalTime;
MINIDUMP_EXCEPTION_INFORMATION ExpParam;
GetLocalTime( &stLocalTime );
GetTempPath( dwBufferSize, szPath );
StringCchPrintf( szFileName, MAX_PATH, L"%s%s", szPath, szAppName );
CreateDirectory( szFileName, NULL );
StringCchPrintf( szFileName, MAX_PATH, L"%s%s//%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",
szPath, szAppName, szVersion,
stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
GetCurrentProcessId(), GetCurrentThreadId());
hDumpFile = CreateFile(szFileName, GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
MINIDUMP_USER_STREAM UserStream[2];
MINIDUMP_USER_STREAM_INFORMATION UserInfo;
UserInfo.UserStreamCount = 1;
UserInfo.UserStreamArray = UserStream;
UserStream[0].Type = CommentStreamW;
UserStream[0].BufferSize = strBuild.GetLength()*sizeof(WCHAR);
UserStream[0].Buffer = strBuild.GetBuffer();
UserStream[1].Type = CommentStreamW;
UserStream[1].BufferSize = strError.GetLength()*sizeof(WCHAR);
UserStream[1].Buffer = strError.GetBuffer();
ExpParam.ThreadId = GetCurrentThreadId();
ExpParam.ExceptionPointers = pExceptionPointers;
ExpParam.ClientPointers = TRUE;
MINIDUMP_TYPE MiniDumpWithDataSegs = MiniDumpNormal
| MiniDumpWithHandleData
| MiniDumpWithUnloadedModules
| MiniDumpWithIndirectlyReferencedMemory
| MiniDumpScanMemory
| MiniDumpWithProcessThreadData
| MiniDumpWithThreadInfo;
bMiniDumpSuccessful = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
hDumpFile, MiniDumpWithDataSegs, &ExpParam, NULL, NULL);
// 上傳mini dump 到自己伺服器(略)
...
return EXCEPTION_CONTINUE_SEARCH; //或者 EXCEPTION_EXECUTE_HANDLER 關閉程式
}
int _tmain()
{
// 設定 execption filter
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
....
return 0;
}
在程式異常生成dmp檔案後,我們需要使用該檔案對應的pdb檔案來定位程式崩潰的原始檔:
建立PDB檔案基本上是這幾個選項,a)在project setting的C++屬性中,選擇生成program database,或者直接手動加入/Zi選項,如果有/Z7,把它替換成/Zi。b)在link選項中選擇Generate debug info,或者直接加入/debug選項,另外注意/pdb應該是類似/PDB:".\Release/yourproj.pdb"這樣的,如果不是手動修改。
有的人會擔心包含debug資訊以後檔案變大,修改link中這兩個選項/OPT:REF和/OPT:ICF會減小最終生成的檔案大小。在這裡借用一下John Robbins的截圖。
windbg 除錯崩潰
1、程式崩潰發生過程
這是一個對檔案進行處理的模組,而處理模組在處理之前,需查詢被處理的檔案是否值得處理。這個任務執行過程中發生了崩潰,問題就發生在查詢模組。
2、提取dump檔案
3、分析dump:
1)啟動windbg,file--open crash dump 配置符號庫,reload完成 。
2)使用命令 :.ecxr獲得程序崩潰時暫存器的內容
0:021> .ecxr (意指恢復崩潰時所有暫存器的內容,包括堆疊等)
eax=0532d414 ebx=00000fec ecx=000003fb edx=00000000 esi=0532c428 edi=00000000
eip=750f53ea esp=0490f22c ebp=0490f234 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
msvcr80!memmove+0x5a:
750f53ea f3a5 rep movs dword ptr es:[edi],dword ptr [esi] es:002b:00000000=???????? ds:002b:0532c428=00000000
3)使用命令:k 顯示堆疊
0:021> k (.ecxr不能直接顯示堆疊,需使用k顯示堆疊)
*** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
0490f234 03ba4806 msvcr80!memmove+0x5a(kxewfsys呼叫memmove,memmove是個字串操作函式,能把字串的一部分複製到另一部分,這裡出問題,可能是複製時傳遞的指標有問題,或者字串的大小有問題)
0490f25c 03ba2547 kxewfsys!__ovfl_get+0xa6 (呼叫memmove之前內部的一些處理)
0490f27c 03ba4a74 kxewfsys!__bt_cmp+0x77
0490f2a8 03ba2679 kxewfsys!__bt_search+0x74
0490f2c4 03ba1409 kxewfsys!__bt_get+0x49
0490f2d8 03ba7448 kxewfsys!IKBDBImpl::Get+0x19
0490f2f8 03ba750d kxewfsys!CFdbFileInfo::Search+0x28 [e:eingsoft_eubauilduild_srckicekice_kxewhitesrckxewfssysfdbfileinfo.cpp @ 483]
0490f338 03ba9c63 kxewfsys!CFdbFileInfo::QueryFileInfo+0x4d [e:eingsoft_eubauilduild_srckicekice_kxewhitesrckxewfssysfdbfileinfo.cpp @ 546]
0490f380 028210d1 kxewfsys!CFdbManager::QueryFileInfo+0xa3 [e:eingsoft_eubauilduild_srckicekice_kxewhitesrckxewfssysfdbmanager.cpp @ 370] (kxewfsys 是處理查詢模組)
*** ERROR: Symbol file could not be found. Defaulted to export symbols for kspfeng.dll -
0490f390 03b1323c kxewhite!kxe_white_query_file_info+0x21 [e:eingsoft_eubauilduild_srckicekice_kxewhitesrckxewfssdkkxewfs.cpp @ 103] (將檔案提交進行查詢.)
0490f45c 03b13442 kspfeng!KSEGetAddonEntries+0x21fbc (kspfeng.dll是檔案處理模組用到的公共功能的封裝檔案)
*** ERROR: Symbol file could not be found. Defaulted to export symbols for ksecore.dll -
0490f498 03abcaa4 kspfeng!KSEGetAddonEntries+0x221c2
0490f4c8 03b07fd5 ksecore+0x1caa4
0490f52c 03b1c52b kspfeng!KSEGetAddonEntries+0x16d55
0490f860 5019dd7c kspfeng!KSEGetAddonEntries+0x2b2ab
0490f864 01c95695 0x5019dd7c
0490f868 1a77217c 0x1c95695
0490f86c 01c95693 0x1a77217c
0490f870 1a7982dc 0x1c95693
0490f874 01c95693 0x1a7982dc
4、總結崩潰原因
查詢模組,對檔案路徑的處理存在bug。