Release 版無調試資訊,定位崩潰比 Debug 版困難。本文介紹三種進階定位技術:MAP 檔案、自定義異常過濾器、COD 檔案混合分析。
/Zd。/MAPINFO:LINES。.map 檔。; 範例 MAP 片段
Preferred load address is 00400000 ; 基址
Address Publics by Value Rva+Base Lib:Object
0001:00000000 ?_GetBaseMessageMap@CMyApp@@... 00401000 MyApp.obj
Line numbers for MyDlg.obj (MyDlg.cpp)
24 0001:00003f90
28 0001:00003fce
29 0001:00003fd1
定位步驟:
0x00405FD1)。Publics by Value 區找 最後一個 Rva+Base < 崩潰位址 的函數,即所在函數。崩潰位址 - 函數 Rva+Base。Line numbers for ... 區,找 最接近該偏移 的行號。使用 SetUnhandledExceptionFilter 註冊回調,可在崩潰瞬間捕獲完整上下文。
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord; // 異常代碼、位址、參數
PCONTEXT ContextRecord; // 所有暫存器值 (EIP, ESP, EAX...)
} EXCEPTION_POINTERS;
| 代碼 | 符號 | 說明 |
|---|---|---|
| 0xC0000005 | EXCEPTION_ACCESS_VIOLATION | 記憶體讀寫違規 |
| 0xC0000094 | EXCEPTION_INT_DIVIDE_BY_ZERO | 整數除零 |
| 0xC00000FD | EXCEPTION_STACK_OVERFLOW | 堆疊溢位 |
| 0x80000003 | EXCEPTION_BREAKPOINT | 硬體中斷點 (INT 3) |
LPTOP_LEVEL_EXCEPTION_FILTER g_prevFilter = NULL;
LONG WINAPI MyExceptionFilter(PEXCEPTION_POINTERS pExpInfo)
{
// 1. 記錄詳細資訊到檔案
WriteCrashLog(pExpInfo);
// 2. 交還系統預設處理 (彈出錯誤對話框)
if (g_prevFilter) return g_prevFilter(pExpInfo);
return EXCEPTION_CONTINUE_SEARCH;
}
// 程式啟動時安裝
g_prevFilter = SetUnhandledExceptionFilter(MyExceptionFilter);
EXCEPTION_RECORD.ExceptionInformation[0] 為 0 表讀取違規,1 表寫入違規;[1] 為違規記憶體位址。
專案屬性 -> C/C++ -> 輸出檔案 -> 清單檔類型:含機器碼的組合語言 (/FAcs)。產生 .cod 檔。
?OnPaint@CMyDlg@@IAEXXZ PROC NEAR ; CMyDlg::OnPaint
; 81 : {
00000 83 ec 64 sub esp, 100 ; 00000064H
00003 56 push esi
; 82 : if (IsIconic())
00004 e8 xx xx xx xx call ?IsIconic@CWnd@@QAEHXZ
步驟:
.cod 檔找函數標籤 (如 ?OnPaint@CMyDlg@@IAEXXZ)。00000, 00003...) 找到崩潰指令。; 82 :) 精確定位到 C++ 行。/Zd 與 /MAPINFO:LINES,體積增加微小但獲益巨大。