如何定位Release程序崩溃原因
[轉]如何定位Release程序崩潰原因
Posted on 2011-08-19 10:44 單魚游弋 閱讀(2162) 評論(1) 編輯 收藏1?????? 案例描述
作為Windows程序員,平時最擔心見到的事情可能就是程序發生了崩潰(異常),這時Windows會提示該程序執行了非法操作,即將關閉。請與您的供應商聯系。呵呵,這句微軟的“名言”,恐怕是程序員最怕見也最常見的東西了。
在一個大型軟件的測試過程中,初期出現程序崩潰似乎成了不可避免的事。其實測試中出現程序崩潰并不可怕,反而是測試的成功。作為開發的我們更需要關心的是程序中的哪個函數或哪一行導致了系統崩潰,這樣才能有針對性的進行改正。
本文描述了自己總結的幾種定位崩潰的辦法。
2?????? 案例分析
以下是幾種常見的崩潰現象及對應的處理辦法:
1.??????? 對于Release版本必現的崩潰且在Debug版本上也崩潰的程序。
解決思路:去掉所有斷點,直接在Debug版本上運行程序,在程序崩潰時,VC會自動跳轉定位到崩潰代碼行, 這種方法最簡單也最常用。
2.??????? 對于在Debug版本上不崩潰但Release版本崩潰的程序,很有可能是Debug和Release版本的差異。例如Debug版本所有成員在構造時會被清0,而Release版本所有成員在構造時是內存里面的原始值,而且Debug有運行時庫做保護,這些都會導致某些程序在Debug正常而Release崩潰。
解決思路:1)在程序中加打印,通過程序崩潰之前的打印定位出錯位置; 2)逐段注釋代碼,直到程序不崩潰為止。這種方法耗時較長,對程序員要求較高,而且對于那種不是必現的bug或者很難搭建執行環境的情況就較難處理了。
3.??????? 對于在客戶現場崩潰的情況,顯然不適合直接帶一臺電腦去調試。
解決思路:應該有文件記錄下崩潰信息,客服人員可以將崩潰信息文件發送給程序員,以便程序員查詢崩潰原因,然后利用編譯時生成MAP文件(工程信息文件,存放在版本編譯機中)的信息來定位問題函數或問題代碼行。下面就這種方法展開討論一下:
3?????? 解決過程
對于上節第三種情況,也是最難解決的情況,解決過程如下:
1.????? 崩潰回調注冊,攔截Windows程序崩潰;
2.????? 在回調處理中,輸出崩潰原因,崩潰內存地址,崩潰堆棧;
3.????? 工程輸出map文件;
4.????? 通過崩潰內存地址以及map文件找出崩潰的函數。
5.????? 使用COD文件精確定位崩潰行
?
3.1???? 崩潰回調注冊
實際上,只靠Windows的錯誤消息對話框提供的信息量是很有限的。用SetUnhandledExceptionFilter注冊自定義錯誤處理回調函數,可以替換Win32默認的異常處理過濾器(top-level exception filter),而且能打印出崩潰堆棧,這對定位崩潰原因非常有用。
SetUnhandledExceptionFilter的函數原型:
| LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter( LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter );? |
功? 能:注冊和注銷異常處理回調;
用? 法:第一次調用注冊異常處理回調,第二次調用注銷;
返回值:返回當前的exception filter。需要保存這個函數指針,在注銷異常處理回調的時候,以此為參數再次調用SetUnhandledExceptionFilter。打印異常處理也需要此值。
參數: 異常處理的回調函數;
?
3.2???? 輸出崩潰信息
崩潰信息在異常回調函數中打印,輸出到程序執行目錄下的文件:
異常處理回調的函數原形:
| LONG WINAPI CallBackDebugInfo ( EXCEPTION_POINTERS *pException);? |
功? 能:異常處理回調處理,打印崩潰信息;
用? 法:注冊自定義錯誤處理回調:SetUnhandledExceptionFilter (CallBackDebugInfo);
返回值:EXCEPTION_CONTINUE_EXECUTION – ?錯誤已經被修復,從異常發生處繼續執行
EXCEPTION_CONTINUE_SEARCH ???– ?繼續查找異常過濾器
EXCEPTION_EXECUTE_HANDLER?? – ?正常返回
參數: 崩潰信息結構,包含崩潰原因、崩潰模塊、崩潰地址、崩潰堆棧等;
常見崩潰原因有:
EXCEPTION_ACCESS_VIOLATION = C0000005h?? 讀寫內存錯誤
EXCEPTION_INT_DIVIDE_BY_ZERO = C0000094h ?除0錯誤
EXCEPTION_STACK_OVERFLOW = C00000FDh? 堆棧溢出或者越界
EXCEPTION_GUARD_PAGE = 80000001h 由Virtual Alloc建立起來的屬性頁沖突
EXCEPTION_NONCONTINUABLE_EXCEPTION = C0000025h不可持續異常,程序無法恢復執行,異常處理例程不應處理這個異常
EXCEPTION_INVALID_DISPOSITION = C0000026h在異常處理過程中系統使用的代碼
EXCEPTION_BREAKPOINT = 80000003h? 調試時中斷(INT 3)
EXCEPTION_SINGLE_STEP = 80000004h? 單步調試狀態(INT 1)
?
3.3???? 輸出map文件
map文件記錄程序的全局符號、源文件和代碼行號信息,是整個程序工程信息的靜態文本。通過文本閱讀工具如Ultra Edit或記事本就可以打開Map文件。
在 VC 中,打開“Project Settings”選項頁,選擇 C/C++ 選項卡,并在最下面的 Project Options 里面輸入:/Zd ,然后選擇 Link 選項卡,選中“Generate mapfile”復選框。并在最下面的 Project Options 里面輸入:/mapinfo:lines,表示生成 map 文件時,加入行信息。
?
最后編譯就可以生成 MAP 文件,可以在工程的Debug或Release目錄下找到剛剛生成的MAP文件,文件名為“工程名.map”。
?
3.4???? 使用map文件找出崩潰函數
?? 通過上面的步驟,已經得到了 MAP 文件,那么我們該如何利用它呢?下面一步步演示使用MAP文件定位程序崩潰行的過程。
????? 1.我們先在代碼中加入非法內存操作(最常見的異常)的代碼:
| BOOL CMainFrameDlg::OnInitDialog() { ???????? ::SetProp(m_hWnd,AfxGetApp()->m_pszExeName, (HANDLE)1); ???????? s32 *p=NULL; ???????? *p= 123; |
?
2.執行程序,程序在開始就異常,在異常打印文件中打印了如下信息:
| ======================== 崩潰信息 ========================== 崩潰時間: 2009/06/02 16:58:22 崩潰原因:非法內存操作 異常代碼 = c0000005 異常地址 = 0x0045a76f 異常模塊: E:\ccroot\liuxiaojing_Enterprise\Enterprise_VOB\70-nms1\pcmt2\prj_win32\Release\pcmt2.exe Section name: .text - offset(rva) : 0x0005976f ---------------------- Trips of Stack ---------------------- E:\ccroot\liuxiaojing_Enterprise\Enterprise_VOB\70-nms1\pcmt2\prj_win32\Release\pcmt2.exe name : pcmtver - location: 2bef |
3.確定崩潰地址是:0x0005976f,在Map文件中定位函數:
| 0001:00059420 ?OnCreate@CMainFrameDlg@@IAEHPAUtagCREATESTRUCTA@@@Z 0045a420 f?? MainFrameDlg.obj ?0001:00059460?????? ?SetTooltips@CMainFrameDlg@@AAEXXZ 0045a460 f?? MainFrameDlg.obj ?0001:00059700?????? ?OnTranslate@CMainFrameDlg@@IAEJIJ@Z 0045a700 f?? MainFrameDlg.obj ?0001:00059730?????? ?OnInitDialog@CMainFrameDlg@@MAEHXZ 0045a730 f?? MainFrameDlg.obj ?0001:00059a10? ?OnSysCommand@CMainFrameDlg@@IAEXIJ@Z 0045aa10 f?? MainFrameDlg.obj ?0001:00059c20?????? ?OnPaint@CMainFrameDlg@@IAEXXZ 0045ac20 f?? MainFrameDlg.obj |
根據00059730< 0005976f < 00059a10 ,確定是在CMainFrameDlg 的OnInitDialog函數中的某一行產生了異常。
3.5???? 使用map代碼行定位崩潰行區間
?
| Line numbers for .\Release\MainFrameDlg.obj(E:\ccroot\liuxiaojing_Enterprise\Enterprise_VOB\70-nms1\pcmt2\source\MainFrameDlg.cpp) segment .text ?? 498 0001:00059647?? 499 0001:00059667?? 501 0001:0005966e?? 502 0001:000596af ?? 503 0001:000596ed?? 506 0001:00059700?? 507 0001:00059703?? 508 0001:00059708 ?? 510 0001:0005970f?? 511 0001:00059720?? 512 0001:00059723?? 515 0001:00059730 ?? 516 0001:0005974e?? 521 0001:0005976d?? 524 0001:0005977e?? 526 0001:0005978b |
我們在map文件的代碼行信息里查找不超過計算結果0x0005976f,但可以找最接近的數。發現是 MainFrameDlg.cpp 文件中的:521 0001:0005976d,而程序實際崩潰行在519(注釋行和空行也要計算在內),非常接近實際崩潰行了,考慮到程序實際執行的是匯編指令,我們可以在(516 ~524)行區間內尋找到實際崩潰行。
3.6???? 無法定位崩潰的情況
但是這種輸出文件的方法也不能定位所有崩潰問題,俗話說得好:沒有萬能的救世主。
例如我們有時會碰到下層編解碼器崩潰,崩潰打印如下表:
| ======================== 崩潰信息 ========================== 崩潰時間: 2009/05/07 09:48:17 崩潰原因:非法內存操作 異常代碼 = c0000005 異常地址 = 0x02163b32 異常模塊: C:\WINDOWS\system32\kdg7221.acm Section name: .text - offset(rva) : 0x00002b32 ---------------------- Trips of Stack ---------------------- C:\WINDOWS\system32\kdg7221.acm |
總結
以上是生活随笔為你收集整理的如何定位Release程序崩溃原因的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: forceinline关键字
- 下一篇: extern用法详解