1. 程式人生 > >[Windows程式設計] 如何捕捉程式異常 crash 並生成 dump 檔案(轉)

[Windows程式設計] 如何捕捉程式異常 crash 並生成 dump 檔案(轉)

程式崩潰(crash)的時候, 為了以後能夠除錯分析問題, 可以使用WinDBG要把當時程式記憶體空間資料都儲存下來,生成的檔案稱為dump 檔案。

步驟:

1) 開啟WinDBG並將之Attach 到crash的程式程序

2) 輸入產生dump 檔案的命令

WinDBG產生dump 檔案的命令是 .dump ,可以選擇不同的引數來生成不同型別的dump檔案。

選項(1): /m

命令列示例:.dump /m C:\dumps\myapp.dmp

註解: 預設選項,生成標準的minidump, 轉儲檔案通常較小,便於在網路上通過郵件或其他方式傳輸。 這種檔案的資訊量較少,只包含系統資訊、載入的模組(DLL)資訊、 程序資訊和執行緒資訊。

選項(2): /ma

命令列示例:.dump /ma C:\dumps\myapp.dmp

註解: 帶有儘量多選項的minidump(包括完整的記憶體內容、控制代碼、未載入的模組,等等),檔案很大,但如果條件允許(本機除錯,區域網環境), 推薦使用這中dump。

選項(3):/mFhutwd

命令列示例:.dump /mFhutwd C:\dumps\myapp.dmp

註解:帶有資料段、非共享的讀/寫記憶體頁和其他有用的資訊的minidump。包含了通過minidump能夠得到的最多的資訊。是一種折中方案。

   上面介紹如如何用WinDBG 生成crash dump 《WinDBG 技巧:如何生成Dump 檔案(.dump 命令) 》,但是使用者機器上通常不安裝WinDBG, 而且多數使用者也不知道怎麼使用WinDBG。 所以最好是自己程式裡面能夠捕捉exception/crash,並且生成crash dump,然後通過網路傳回到自己伺服器。

捕捉exception 可以用API 函式 SetUnhandledExceptionFilter 。

生成crash dump 可以用DbgHelp.dll 裡面的MiniDumpWriteDump 函式。

LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter( __in LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter );

BOOL WINAPI MiniDumpWriteDump( __in HANDLE hProcess, __in DWORD ProcessId, __in HANDLE hFile, __in MINIDUMP_TYPE DumpType, __in PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, __in PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, __in PMINIDUMP_CALLBACK_INFORMATION CallbackParam );

【程式碼例項】

#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; 
}

正常情況下不會生成dump,需要自己在程式中新增程式碼實現,一般是利用MS的dbghelp.dll提供的DbgHelpCreateUserDump來生成,用WinDbg來分析,期間需要編譯程式生成的.pdb檔案。可以參考BlackBox、google breakpad等

crash 檔案生成的路徑:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug]
"Auto"="0"
"Debugger"="\"D:\\WinDBG\\ntsd.exe\" -p %ld -e %ld -g -noio -c \".dump /ma /u d:\\dbgdmp\\dmp.dmp; q"
"UserDebuggerHotKey"=dword:00000000