Dump與Win Program Crash

本文轉自:http://www.cnblogs.com/rainbowzc/archive/2013/04/03/2997426.html html

(尋找崩潰緣由的好方法,比map更好用,也方便去掉os彈出異常窗口從而阻礙了守護進程執行職能。) windows

1、   綜述

總算講到MiniDump了。 服務器

Dump有多有用我都沒法盡數,基本上屬於定位錯誤修復BUG的倚天劍。(日誌能夠算是屠龍刀)這些都是對於那些不是必出的BUG,放在外面運行的時候出現的BUG而言的,那些可以經過簡單調試就能發現的BUG,通常都不足爲懼。 app

 

2、   基本應用

MiniDump之因此叫MiniDump,天然是有其Mini之處。。。(廢話),呵呵,MS提供了一個API函數,MiniDumpWriteDump,(在Dbghelp.h中聲明,須要導入DbgHelp.lib使用)因此我纔將其稱爲MiniDump,其實Dump也能表達一樣的意思。。。。 框架

MiniDump最簡單的應用在於程序崩潰的時候,將崩潰時那一刻的信息寫進一個文件,以方便之後查找錯誤。使用方法說簡單就簡單,說難也難。 ide

1.             怎麼感知到程序的崩潰?

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的配合使用方式也相似,就很少說了。

 

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,這樣信息就出來了。。。。。(之前這個問題困擾了咱們一天)

 

3、   高級應用

程序崩潰的問題解決了,問題是,有不少時候,不少程序是不容許隨便崩潰的,這樣,在程序崩潰後再去發現問題就有些晚了,那麼,有沒有程序不崩潰時也能發現問題的方法呢?前面描述的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。

相關文章
相關標籤/搜索