本文轉自:http://www.cnblogs.com/rainbowzc/archive/2013/04/03/2997426.html html
(尋找崩潰緣由的好方法,比map更好用,也方便去掉os彈出異常窗口從而阻礙了守護進程執行職能。) windows
總算講到MiniDump了。 服務器
Dump有多有用我都沒法盡數,基本上屬於定位錯誤修復BUG的倚天劍。(日誌能夠算是屠龍刀)這些都是對於那些不是必出的BUG,放在外面運行的時候出現的BUG而言的,那些可以經過簡單調試就能發現的BUG,通常都不足爲懼。 app
MiniDump之因此叫MiniDump,天然是有其Mini之處。。。(廢話),呵呵,MS提供了一個API函數,MiniDumpWriteDump,(在Dbghelp.h中聲明,須要導入DbgHelp.lib使用)因此我纔將其稱爲MiniDump,其實Dump也能表達一樣的意思。。。。 框架
MiniDump最簡單的應用在於程序崩潰的時候,將崩潰時那一刻的信息寫進一個文件,以方便之後查找錯誤。使用方法說簡單就簡單,說難也難。 ide
Window提供了較爲方便的方法去感知到程序的幾種崩潰狀況。 函數
在《Breakpad在進程中完成dump的流程描述》一文中,我描述了一下Breakpad獲取到程序崩潰的方法,事實上,這也是典型的Windows下感知程序崩潰的方法,那篇文章是剛開始工做的時候,完成公司本身的ExceptionHandle庫的時候寫的工做筆記,如今看起來也仍是有必定的參考價值。 優化
Windows下感知程序崩潰(其實就是運行時的嚴重錯誤)的方法有3個核心的函數,分別以下: ui
SetUnhandledExceptionFilter(HandleException)肯定出現沒有控制的異常發生時調用的函數爲HandleException. this
_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中調試運行,否則異常控制權老是會被VS掌握,那麼,老是沒有辦法讓MyUnhandledExceptionFilter得到控制權,詳細的描述見參考2),能夠得到一個名叫DumpFile.dmp的文件,此文件就是咱們折騰了半天所謂的dump文件了。其餘兩個函數與MiniDumpWriteMiniDump的配合使用方式也相似,就很少說了。
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的用法和特性講解這裏不重複了,見前面的文章。《異常處理與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。