寫程式的人都有這個苦惱,使用者說你的程式總有時會莫名奇妙的崩潰,可是你怎麼也無法重現崩潰的情境,所以也無法找出程式中的Bug,難道就束手無策嗎? 前幾天在一本雜誌(《程式員-遊戲創造》)上看到一篇關於這個主題的文章,非常詳細的論述了怎樣通過代碼和工具實現Bug Report,使得你可以快速定位崩潰的原始碼行數。 要catch未處理的異常,該文中論述了使用全域的try-catch的優缺點,全域的try-catch導致了調試工作的麻煩,不可取。Win32異常的可以通過設定過濾函數來處理,而C++也有set_terminate函數可以設定一個函數來處理任何未catch的異常。 文章還告訴我們,main/WinMain是程式的入口處,但靜態初始化在這之前進行,作業系統裝載我們的程式後,先調用的是CRT提供函數WinMainCRTStartup/mainCRTStartup,可以通過VC開發工具設定程式的入口函數來擷取異常的優先控制權(/ENTRY:函數)(Linker->Advanced->Entry Point)。
1.用minidump進行調試 這個技術是改進Windows作業系統的錯誤報表機制的關鍵。產生的檔案非常小,適合於網路傳輸。
有三種方法建立一個minidump檔案:
a. 在你的應用程式中加入未處理的exception處理函數,函數中建立minidump檔案
b. Visual Studio .NET整合式開發環境中調試時,Debug菜單中按Save Dump
c. Windows XP 在一個程式遇到未處理的異常時會自動產生minidump檔案,但直接提交給微軟,你沒機會處理。 要使用該技術,build的時候要產生完全的調試資訊檔(PDB),發布每個版本時要儲存每個可執行檔(exe/dll)以及相對應的pdb檔案,以備將來debug用。為了更好的輔助檔案匹配,注意要正確設定每個可執行檔的組建號,每次發布要使用不同的版本號碼,然而debugger使用的是PE檔案頭中內部時間蹉來匹配的。在發布版本中產生調試資訊有副作用,佔用更多空間並且容易被逆向工程。 寫minidump的API是MiniDumpWriteDump,在PlatformSDK中的DBGHelp.dll,只有XP版本的沒有問題。為了調用這個API,你必須使用SetUnhandledExceptionFilter API來設定一個未處理異常處理器,用來catch崩潰,但在VS2005之前,仍然會無法catch純C++異常。 在你的函數中,注意要匯入正確的DbgHelp.dll,使用LoadLibrary會預設載入System32目錄下的dll,Win2000將會導致錯誤。 分析使用者傳回的minidump檔案,使用Visual Studio .Net開啟(*.dmp,*.mdmp)並建立預設的項目。按F5在Output視窗中顯示載入的模組資訊。重建所有進程狀態,但可能缺少symbols以及調試資訊。所以你需要所有匹配的exe和dll檔案以及pdb檔案,要查看所有的module開啟Modules視窗,找出所有(通常沒必要所有)匹配的dll(DLL Help database: http://support.microsoft.com/servicedesks/fileversion/dllinfo.asp.),或者你需要作業系統的安裝盤或者從報告者的系統中得到所有匹配的dll,拷貝到本地的一個目錄下,例如D:/Mudules。然後設定項目的Debugging頁中的Command參數為MODPATH=D:/Mudules,按F5重新載入minidump。注意你也需要每個dll匹配的pdb檔案,平台的可以在作業系統安裝盤或者http://www.microsoft.com/ddk/debugging中找到。
Operating system Files required Windows NT 4 DBGs
Windows 2000 DBGs, PDBs
Windows XP PDBs
3. CrashFinder John Robbins 寫好的CrashFinder能夠發現VC++/VB的應用程式的崩潰處(程式碼)。Release版本也要產生Debug資訊,在VC中使用/Zi 編譯選項(C/C++ tab中Program Database )和/DEBUG and /PDB:<pdb filename>連結選項(Link tab中打勾Debug info 並選擇Microsoft format),VB中在項目屬性打勾Compile to Native Code 和Create Symbolic Debug Info 選項。 如果你在Debugger的OUtput視窗中看到LDR: Dll xxx base 10000000 relocated due to collision with yyy資訊,可以通過REBASE.EXE(Platform SDK)程式使得你可以重新安排載入dll/ocx地址,因為不同的系統載入dll/ocx的地址不同,如果衝突會導致你找不出到底是哪個dll崩潰。 如果是beta版,你可以讓應用程式產生詳細Dr. Watson 資訊(要很大空間不適合傳輸),你可以在安裝應用程式時檢測系統是否已經安裝Dr. Watson(在註冊表HKEY_LOCAL_MACHINE/SOFTWARE/ Microsoft/Windows NT/CurrentVersion/AeDebug 處),有了這種資訊你可以得到詳細的堆棧空間並遊刃有餘地找出崩潰的地方。 CrashFinder使用IMAGEHLP.DLL符號引擎(Windows NT 4.0首次引入),Windows NT 5.0 SDK已經可以處理源碼行號。(CrashFinder2.1已經升級,使用最新的DBGHelp.dll) CrashFinder只儲存應用程式和調試資訊檔的目錄資訊,所以無需每次編譯都需要CrashFinder連編。你可以建立多個CrashFinder項目,每個項目針對一種作業系統。 原文:http://www.microsoft.com/msj/0498/bugslayer0498.aspx
下載:http://www.wintellect.com/about/instructors/robbins/code.aspx 解決方案: (To be continue)