Dump与Win Program Crash
2019獨角獸企業重金招聘Python工程師標準>>>
本文轉自:http://www.cnblogs.com/rainbowzc/archive/2013/04/03/2997426.html
(尋找崩潰原因的好方法,比map更好用,也方便去掉os彈出異常窗口從而阻礙了守護進程執行職能。)
一、?? 綜述
總算講到MiniDump了。
Dump有多有用我都無法盡數,基本上屬于定位錯誤修復BUG的倚天劍。(日志可以算是屠龍刀)這些都是對于那些不是必出的BUG,放在外面運行的時候出現的BUG而言的,那些能夠通過簡單調試就能發現的BUG,一般都不足為懼。
?
二、?? 基本應用
MiniDump之所以叫MiniDump,自然是有其Mini之處。。。(廢話),呵呵,MS提供了一個API函數,MiniDumpWriteDump,(在Dbghelp.h中聲明,需要導入DbgHelp.lib使用)所以我才將其稱為MiniDump,其實Dump也能表達同樣的意思。。。。
MiniDump最簡單的應用在于程序崩潰的時候,將崩潰時那一刻的信息寫進一個文件,以方便以后查找錯誤。使用方法說簡單就簡單,說難也難。
1.????? ?????? 怎么感知到程序的崩潰?
Window提供了較為方便的方法去感知到程序的幾種崩潰情況。
在《Breakpad在進程中完成dump的流程描述》一文中,我描述了一下Breakpad獲取到程序崩潰的方法,事實上,這也是典型的Windows下感知程序崩潰的方法,那篇文章是剛開始工作的時候,完成公司自己的ExceptionHandle庫的時候寫的工作筆記,現在看起來也還是有一定的參考價值。
Windows下感知程序崩潰(其實就是運行時的嚴重錯誤)的方法有3個核心的函數,分別如下:
SetUnhandledExceptionFilter(HandleException)確定出現沒有控制的異常發生時調用的函數為HandleException.
_set_invalid_parameter_handler(HandleInvalidParameter)確定出現無效參數調用發生時調用的函數為HandleInvalidParameter.
_set_purecall_handler(HandlePureVirtualCall)確定純虛函數調用發生時調用的函數為HandlePureVirtualCall.
3個函數的使用方法一致,都是在發生自己關心的(見上面的描述)異常時,調用參數傳進來回調函數,Windows會將崩潰信息通過參數傳入回調函數,這時候就是進行Dump的絕佳時機。詳細的信息可以查閱MSDN,我這里就不復制資料了,那樣有copy文檔之嫌,這里以SetUnhandledExceptionFilter為例,演示實際與MiniDumpWriteDump配合使用的情況。像這些比較復雜的API,MSDN中連個Example都沒有,說實話,當時掌握花了一點時間。
ExceptionExample:
#include <windows.h>
#include <Dbghelp.h>
using namespace std;
?
#pragma auto_inline (off)
#pragma comment( lib, "DbgHelp" )
?
// 為了程序的簡潔和集中關注關心的東西,按示例程序的慣例忽略錯誤檢查,實際使用時請注意
LONG WINAPI MyUnhandledExceptionFilter(
struct _EXCEPTION_POINTERS* ExceptionInfo
??? )
{
??? HANDLE lhDumpFile = CreateFile(_T("DumpFile.dmp"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL ,NULL);
?
??? MINIDUMP_EXCEPTION_INFORMATION loExceptionInfo;
??? loExceptionInfo.ExceptionPointers = ExceptionInfo;
??? loExceptionInfo.ThreadId = GetCurrentThreadId();
??? loExceptionInfo.ClientPointers = TRUE;
??? MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),lhDumpFile, MiniDumpNormal, &loExceptionInfo, NULL, NULL);
?
??? CloseHandle(lhDumpFile);
?
??? return EXCEPTION_EXECUTE_HANDLER;
}
?
?
void Fun2()
{
??? int *p = NULL;
??? *p = 0;
}
?
void Fun()
{
??? Fun2();
}
?
int main()
{
??? SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
?
??? Fun();
?
??? return 1;
}
?
API的調用僅僅作為釋放,查看下MSDN就知道使用方法了,
#pragma auto_inline (off)
#pragma comment( lib, "DbgHelp" )
兩句講一下,第一句是取消掉自動內聯效果,這樣才能達到更好的演示效果,不然,Fun,Fun2這種簡單的函數會被自動內聯,那么也就沒有堆棧,不好看到實際中Dump的作用。效果與VS2005編譯選項的,C/C++->優化->內聯函數展開->only _inline一樣。
第二句是標志導入DbgHelp庫,以使用MiniDumpWriteMiniDump API。與VS2005編譯選項的鏈接器->輸入->附加依賴項中添加dbgHelp.lib效果一樣。
?
實際運行程序,(不能在VS中調試運行,不然異??刂茩嗫偸菚籚S掌握,那么,總是沒有辦法讓MyUnhandledExceptionFilter獲得控制權,詳細的描述見參考2),可以獲得一個名叫DumpFile.dmp的文件,此文件就是我們折騰了半天所謂的dump文件了。其他兩個函數與MiniDumpWriteMiniDump的配合使用方式也類似,就不多說了。
?
2.????? Dump文件的使用
Dump文件的在Windows下的使用非常簡單,但是就是因為太過于簡單,所以網上的描述也是非常簡單,想起來,那時候折騰出Dump文件時非常興奮,解決發現拿dump文件沒有辦法,網上簡單的描述用VS打開調試的方法總是沒有頭緒。。。。呵呵
正確的使用方法是,將崩潰程序的dmp, pdb,exe文件都放在同一個目錄下,然后雙擊運行dmp,(或者用VS打開),然后就會出現一個名為dumpfile的解決方案并且包含一個dumpfile的工程,此時右鍵點擊此工程,選擇調試->啟動新實例(或者啟動并進入單步調試新實例)都行,此時程序會自動的調到源碼中崩潰的那一行,并且在call stack中有完整的堆棧信息,并且臨時變量的值也可以通過VS顯示出來,還有模塊信息,線程信息,
在上例中,堆棧信息是:
>???? Exception.exe!Fun2()? 行36?????? C++
????? Exception.exe!main()? 行50 C++
????? Exception.exe!__tmainCRTStartup()? 行597 + 0x17 字節?????? C
????? kernel32.dll!7c817077() ??????
????? [下面的框架可能不正確和/或缺失,沒有為 kernel32.dll 加載符號]???
????? ntdll.dll!7c93005d() ?
?
然后,寄存器的值為:
EAX = 00000000 EBX = 00000000 ECX = 0000B623
EDX = 7C92E514 ESI = 00000001 EDI = 00403384
EIP = 00401072 ESP = 0013FF7C EBP = 0013FFC0
EFL = 00010246
?
在normal模式下,dump文件速度較快,但是沒有內存信息,你甚至可以通過調整MiniDumpWriteMiniDump的參數來將運行時的整個內存都dump下來,這些都非常簡單,查看一下MSDN MiniDumpWriteMiniDump的信息即可。
有了這些信息,程序的錯誤定位(C++下一般是空指針的訪問比較多)已經是非常明朗的了,再配合日志,一般的錯誤不難發現。這里順帶說明一下,當運行的程序被改名或者糅合進其他地方后運行,用這樣的方式,一開始堆棧信息中是沒有完整的信息的,這時候可以在堆棧信息中,用右鍵菜單中的加載符號,選擇合適的文件pdb,這樣信息就出來了。。。。。(以前這個問題困擾了我們一天)
?
三、?? 高級應用
程序崩潰的問題解決了,問題是,有很多時候,很多程序是不允許隨便崩潰的,這樣,在程序崩潰后再去發現問題就有些晚了,那么,有沒有程序不崩潰時也能發現問題的方法呢?前面描述的SEH就是一種讓程序不崩潰的方法,不過在那種方式下,按以前描述的方法,崩潰是不崩潰了,但是實際上,掩蓋了很多問題,對于問題的發現有些不利的地方。本文前面描述過了,MiniDump是一種快速發現問題的好方法,但是卻沒有辦法避免程序崩潰,那么終極辦法是啥呢?我們的目的既然是程序不崩潰+快速發現問題,那么終極辦法自然就是SEH+MiniDump了:)SEH和MiniDump都是Windows的特性,MS也的確提供了結合的方式。見下面的例子,呵呵,別太激動了。。。。這也是我們公司的服務器從內測時一天多次無任何通知,預告,警告的崩潰(總監甚至還曾因為我的問題,半夜3點爬起來解決服務器崩潰問題)到現在服務器基本做到永不崩潰,即便出現問題了也有充足的時間從容的解決,然后在服務器中發通告,告訴文件服務器需要臨時維護。。。。呵呵,都依賴于此終極解決方案。。。。。
SEH的用法和特性講解這里不重復了,見前面的文章?!?strong>異常處理與MiniDump詳解(3) SEH(Structured Exception Handling)》
要想利用MiniDumpWriteMiniDump,需要獲取的是MINIDUMP_EXCEPTION_INFORMATION結構的信息,這個結構中最重要的信息來源于PEXCEPTION_POINTERS的信息,這個信息在上述的例子中是在程序崩潰的時候,由Windows作為參數傳入我們設定好的異常處理函數的,現在最主要的問題就是從哪里獲取到這個異常信息了,通過MSDN,我們查到了GetExceptionInformation的函數,返回的就是這個信息,見MSDN:
LPEXCEPTION_POINTERS GetExceptionInformation(void);
不過,這里MS給出了一個notice:
The Microsoft C/C++ Optimizing Compiler interprets this function as a keyword, and its use outside the appropriate exception-handling syntax generates a compiler error.
事實上,剛開始我使用的時候,哪個地方都試遍了,果然都是報編譯錯誤。因為此函數使用方式如此奇怪,并且沒有example。。。。。最后在絕望中。。。看到了Platform Builder for Microsoft Windows CE 5.0的詞函數的說明,里面有個說明,然后我吐血了。。。。
try
{
??? // try block
}
except (FilterFunction(GetExceptionInformation())
{
??? // exception handler block
}?
原來是這樣使用的啊。。。。。。。。。。暈
HandleWithoutCrash例子:
#include <windows.h>
#include <Dbghelp.h>
using namespace std;
?
#pragma auto_inline (off)
#pragma comment( lib, "DbgHelp" )?
// 為了程序的簡潔和集中關注關心的東西,按示例程序的慣例忽略錯誤檢查,實際使用時請注意
LONG WINAPI MyUnhandledExceptionFilter(
struct _EXCEPTION_POINTERS* ExceptionInfo
??? )
{
??? HANDLE lhDumpFile = CreateFile(_T("DumpFile.dmp"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL ,NULL);
?
??? MINIDUMP_EXCEPTION_INFORMATION loExceptionInfo;
??? loExceptionInfo.ExceptionPointers = ExceptionInfo;
??? loExceptionInfo.ThreadId = GetCurrentThreadId();
??? loExceptionInfo.ClientPointers = TRUE;
??? MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),lhDumpFile, MiniDumpNormal, &loExceptionInfo, NULL, NULL);
??? CloseHandle(lhDumpFile);?
??? return EXCEPTION_EXECUTE_HANDLER;
}
?
void Fun2()
{
??? __try
??? {
?????? static bool b = false;
??????
?????? if(!b)
?????? {
?????????? b = true;
?????????? int *p = NULL;
?????????? *p = 0;
??? ??? }
?????? else
?????? {
?????????? MessageBox(NULL, _T("Here"), _T(""), MB_OK);
?????? }
?
??? }
??? __except(MyUnhandledExceptionFilter(GetExceptionInformation()))
??? {
??? }
}?
void Fun()
{
??? Fun2();
}
?int main()
{
??? Fun();
??? Fun();
?
??? return 1;
}
?
這里例子中,你可以調試程序了,因為程序不會崩潰,這樣VS不會和你搶異常的控制。同時,看到dump文件的同時,也可以看到,程序實際上是繼續運行了下去,因為MessageBox還是彈出來了。這。。。就是我們想要的。。。。。
這里有幾個要點,GetExceptionInformation()僅僅只能在__except的MS所謂的Filter中調用,其他地方會報編譯錯誤,其次,返回的值和一般的__except的意義是一樣的,要想程序運行,需要返回EXCEPTION_EXECUTE_HANDLER表示異常得到了控制。其他幾個值的含義見前篇的SEH。
?
轉載于:https://my.oschina.net/u/262868/blog/135849
總結
以上是生活随笔為你收集整理的Dump与Win Program Crash的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android:数据库增删改查、SQLi
- 下一篇: 使用 Label 类在 XNA 中显示文