Effective minidump (下)

【轉http://blog.csdn.net/pkrobbie/article/details/6641081

 

MiniDumpCallback函數

如果MINIDUMP_TYPE不能滿足我們定製minidump內容的需要,我們可以使用MiniDumpCallback函數。這是一個用戶定義的回調函數,MiniDumpWriteDump會調用它,讓用戶來決定是否把某些數據放到minidump中。通過這個函數,我們可以完成這些功能:

  • 從minidump的模塊信息中移除一個可執行模塊信息(部分或者全部)
  • 從minidump的線程信息中移除一個線程信息(部分或者全部)
  • 在minidump中添加一段用戶指定範圍的內存的內容

讓我們先看一下MiniDumpCallback 的聲明(見Figure 7):

Figure 7:

BOOL CALLBACK MiniDumpCallback(

  PVOID CallbackParam,

  const PMINIDUMP_CALLBACK_INPUT CallbackInput,

  PMINIDUMP_CALLBACK_OUTPUT CallbackOutput

);

這個函數有四個參數。第一個參數CallbackParam是一個用戶爲回調函數定義的數據結構(例如,一個指向C++對象的指針)。第二個參數CallbackInput是MiniDumpWriteDump傳遞給回調函數的數據。第三個參數CallbackOutput包含了回調函數返回給MiniDumpWriteDump的數據。這個數據通常就是指定關於那些數據應該包含在minidump中。

現在,讓我們看一下MINIDUMP_CALLBACK_INPUT和MINIDUMP_CALLBACK_OUTPUT結構體的內容。

Figure 8:

typedef struct _MINIDUMP_CALLBACK_INPUT {

    ULONG ProcessId;

    HANDLE ProcessHandle;

    ULONG CallbackType;

    union {

        HRESULT Status;

        MINIDUMP_THREAD_CALLBACK Thread;

        MINIDUMP_THREAD_EX_CALLBACK ThreadEx;

        MINIDUMP_MODULE_CALLBACK Module;

        MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread;

        MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule;

    };

} MINIDUMP_CALLBACK_INPUT, *PMINIDUMP_CALLBACK_INPUT;

 

typedef struct _MINIDUMP_CALLBACK_OUTPUT {

    union {

        ULONG ModuleWriteFlags;

        ULONG ThreadWriteFlags;

        struct {

            ULONG64 MemoryBase;

            ULONG MemorySize;

        };

        struct {

            BOOL CheckCancel;

            BOOL Cancel;

        };

        HANDLE Handle;

    };

} MINIDUMP_CALLBACK_OUTPUT, *PMINIDUMP_CALLBACK_OUTPUT;

 

typedef enum _MINIDUMP_CALLBACK_TYPE {

    ModuleCallback,

    ThreadCallback,

    ThreadExCallback,

    IncludeThreadCallback,

    IncludeModuleCallback,

    MemoryCallback,

    CancelCallback,

    WriteKernelMinidumpCallback,

    KernelMinidumpStatusCallback,

} MINIDUMP_CALLBACK_TYPE;

MINIDUMP_CALLBACK_INPUT結構體包含MiniDumpWriteDump對回調函數的請求。前兩個成員意義很明顯-創建minidump的進程的id和句柄。第三個成員CallbackType是請求的類型,通常叫做回調類型。所有CallbackType的可能的值定義在MINIDUMP_CALLBACK_TYPE枚舉集合中(見Figure 8)。我們在後面會仔細看一下這些值。結構體的第四個參數是一個聯合,它的意義依賴於CallbackType的值。這個聯合包含了MiniDumpWriteDump請求的附加數據。

MINIDUMP_CALLBACK_OUTPUT結構體要簡單一點。它有一個聯合構成,聯合的意義依賴於MINIDUMP_CALLBACK_INPUT的值。聯合的CallbackType成員包含了回調對於MiniDumpWriteDump的反饋。

下面我們來過一下回調類型(callback type)對應的一些最終重要的請求,以及回調函數如何對他們做出響應。在開始之前,先看一下Figure 9。這個例子表示了怎麼樣告訴MiniDumpWriteDump有一個用戶自定的回調函數需要調用。

Figure 9:

void CreateMiniDump( EXCEPTION_POINTERS* pep )

{

  // Open the file

 

  HANDLE hFile = CreateFile( _T("MiniDump.dmp"), GENERIC_READ | GENERIC_WRITE,

    0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );

 

  if( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) )

  {

    // Create the minidump

 

    MINIDUMP_EXCEPTION_INFORMATION mdei;

 

    mdei.ThreadId           = GetCurrentThreadId();

    mdei.ExceptionPointers  = pep;

    mdei.ClientPointers     = FALSE;

 

    MINIDUMP_CALLBACK_INFORMATION mci;

 

    mci.CallbackRoutine     = (MINIDUMP_CALLBACK_ROUTINE)MyMiniDumpCallback;

    mci.CallbackParam       = 0;     // this example does not use the context

 

    MINIDUMP_TYPE mdt       = MiniDumpNormal;

 

    BOOL rv = MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(),

      hFile, mdt, (pep != 0) ? &mdei : 0, 0, &mci );

 

    if( !rv )

      _tprintf( _T("MiniDumpWriteDump failed. Error: %u \n"), GetLastError() );

    else

      _tprintf( _T("Minidump created.\n") );

 

    // Close the file

 

    CloseHandle( hFile );

 

  }

  else

  {

    _tprintf( _T("CreateFile failed. Error: %u \n"), GetLastError() );

  }

 

}

 

BOOL CALLBACK MyMiniDumpCallback(

  PVOID                            pParam,

  const PMINIDUMP_CALLBACK_INPUT   pInput,

  PMINIDUMP_CALLBACK_OUTPUT        pOutput

)

{

    // Callback implementation

    …

}

IncludeModuleCallback

當回調類型被設成IncludeModuleCallback,MiniDumpWriteDump詢問回調函數是否要把特定可執行模塊的信息存到minidump中。回調函數根據MINIDUMP_CALLBACK_INPUT的內容做出決定。此時,聯合成員應該是MINIDUMP_INCLUDE_MODULE_CALLBACK:

typedef struct _MINIDUMP_INCLUDE_MODULE_CALLBACK {

    ULONG64 BaseOfImage;

} MINIDUMP_INCLUDE_MODULE_CALLBACK, *PMINIDUMP_INCLUDE_MODULE_CALLBACK;

這裏,BaseOfImage是模塊在內存中的基地址。利用這個地址,可以獲得模塊更多的信息,以便決定是否需要存到minidump中。

回調函數利用返回值來把決定返回給MiniDumpWriteDump。如果回調返回值是TRUE,關於模塊的信息會被包含進minidump中。通過後續的回調調用可以更精確的定義那些信息需要保存。如果返回值是FALSE,模塊的所有信息會被丟棄。Minidump中看不到任何模塊存在的痕跡。

對於這個回調類型,MINIDUMP_CALLBACK_OUTPUT沒有用處。

ModuleCallback

一個模塊通過了IncludeModuleCallback的測試之後,它會面臨在通往minidump之路上的另外一個障礙。這個障礙是ModuleCallback。這個回調函數會決定關於這個模塊的哪些信息需要保存。

這一次回調函數必須返回TRUE,來保證MiniDumpWriteDump繼續工作。回調函數使用MINIDUMP_CALLBACK_OUTPUT結構體通知MiniDumpWriteDump的關於數據的決定。這個結構體中的聯合包括一個ModuleWriteFlags成員。MiniDumpWriteDump會初始化它的值。它的值代表了可以保存在minidump中的各種模塊信息。MODULE_WRITE_FLAGS枚舉包含了所有可用的標誌。

Figure 10:

typedef enum _MODULE_WRITE_FLAGS {

    ModuleWriteModule        = 0x0001,

    ModuleWriteDataSeg       = 0x0002,

    ModuleWriteMiscRecord    = 0x0004,

    ModuleWriteCvRecord      = 0x0008,

    ModuleReferencedByMemory = 0x0010,

    ModuleWriteTlsData       = 0x0020,

    ModuleWriteCodeSegs      = 0x0040,

} MODULE_WRITE_FLAGS;

當MiniDumpWriteDump帶着ModuleCallback參數調用回調函數,它會設置一些標誌,告訴回調函數哪些模塊信息可以包含在minidump中。回調函數可以分析這些標誌,然後決定清除其中的一部分和還是全部。這樣就可以告訴MiniDumpWriteDump哪些信息不需要保存。Figure 11中的表格列出了目前可用的所有標誌,並且解釋了他們所代表的信息。

Figure 11:

標誌

描述

ModuleWriteModule

這個標誌允許從minidump中排除模塊的所有信息。如果回調函數清除了這個標誌,minidump中就不會包含這個模塊的任何信息。
默認條件下,這個標誌總是被設置的。

ModuleWriteCvRecord, ModuleWriteMiscRecord

這些標誌可以用來從minidump中排除模塊的調試信息記錄。如果清除這個標誌,只有在開發機器是有這個模塊的時候,調試器才能裝載模塊的調試信息。
通常,只有在模塊包含調試信息記錄的時候,這些標誌纔會被設置,也就是,模塊是帶着調試信息進行編譯的時候。
可以在這裏面找到關於調試信息的更詳細說明,http://www.debuginfo.com/articles/debuginfomatch.html

ModuleWriteDataSeg

這個標誌可以用來從minidump中排除模塊的數據段的內容。如果我們在MiniDumpWriteDump使用了MiniDumpWithDataSegs 標誌,又希望選擇哪些模塊的數據段需要包含進來,這個標記就非常有用了。通常,我們希望看到所有我們自己模塊的數據段(以便在調試器中看到全局變量),以及一小部分系統模塊(比如,ntdll.dll)。其他第三方模塊或者系統模塊的數據段沒有用處。由於可執行模塊的數據段在minidump中佔用了很大的空間。這個標記給我們提供一個很好的優化文件尺寸的機會。
只有MiniDumpWithDataSegs 標誌被傳給MiniDumpWriteDump 的時候,這個標誌纔會被設置。

ModuleWriteCodeSegs

這個標記可以用來從minidump中排除模塊的代碼段。只有MiniDumpWithCodeSegs 傳給MiniDumpWriteDump 函數的時候,這個標誌纔可用。這個標誌可以用來選擇哪些模塊的代碼段可以包含在minidump中。一定不要包含所有模塊的代碼段,這會顯著增加minidump的大小。

ModuleReferencedByMemory

這個標誌需要和MINIDUMP_TYPE中的MiniDumpScanMemory一起使用。如果MiniDumpScanMemory被傳給MiniDumpWriteDump,函數會遍歷進程中的所有線程棧,查找指向可執行模塊的地址空間的所有指針。搜索完成後,MiniDumpWriteDump就知道了哪些模塊被引用了,哪些模塊沒有被引用。
如果一個模塊沒有被任何一個線程棧引用,那麼重建調用棧可能不會用到這個模塊。Minidump就可以不包括這個模塊,來節省空間。爲了讓回調函數作最終決定,如果一個模塊被棧引用了,MiniDumpWriteDump會設置ModuleReferencedByMemory標誌,沒有被引用的模塊的標誌會被清除。.
接着,回調函數可以檢查這個模塊是否被引用過。然後,可以通過清除ModuleWriteModule標誌,來把模塊排除到minidump之外。

ModuleWriteTlsData

這個標誌可能是用來控制模塊的TLS數據(通過__declspec(thread)分配)是否要包括在mindump中。但是,到寫這篇文章爲止,還不能工作。

注意ModuleCallback只允許我們排除一些模塊信息,但是不允許添加新的數據。這意味着,如果MiniDumpWriteDump沒有設置相應的標誌,在回調函數中設置相應的標誌沒有用處。例如,如果沒有給MiniDumpWriteDump設置MiniDumpWithDataSegs標誌,MiniDumpWriteDump函數就不會給任何模塊設置ModuleWriteDataSeg標誌。進一步,即使回調函數設置一個模塊的ModuleWriteDataSeg標誌,minidump中也不會真的包含模塊數據段的內容。

在討論很長時間MINIDUMP_CALLBACK_OUTPUT結構體之後,我們回頭來看MINIDUMP_CALLBACK_INPUT結構體。這時候,這個聯合會被解析成MINIDUMP_MODULE_CALLBACK結構體(Figure 12)。它裏面包括了關於模塊的豐富的信息,例如,名稱和路徑、大小、版本信息。

Figure 12:

typedef struct _MINIDUMP_MODULE_CALLBACK {

    PWCHAR FullPath;

    ULONG64 BaseOfImage;

    ULONG SizeOfImage;

    ULONG CheckSum;

    ULONG TimeDateStamp;

    VS_FIXEDFILEINFO VersionInfo;

    PVOID CvRecord;

    ULONG SizeOfCvRecord;

    PVOID MiscRecord;

    ULONG SizeOfMiscRecord;

} MINIDUMP_MODULE_CALLBACK, *PMINIDUMP_MODULE_CALLBACK;

IncludeThreadCallback

這個回調類型對於對於線程的作用,和IncludeModuleCallback對於模塊的作用一樣。這給我們一個機會來決定一個線程的哪些信息需要保存到minidump中。就像IncludeModuleCallback,回調函數返回TRUE表示要把線程信息保存到mindump,返回FASLE表示完全放棄這些信息。可以通過存儲在MINIDUMP_CALLBACK_INPUT的ID來區分線程。

typedef struct _MINIDUMP_INCLUDE_THREAD_CALLBACK {

    ULONG ThreadId;

} MINIDUMP_INCLUDE_THREAD_CALLBACK, *PMINIDUMP_INCLUDE_THREAD_CALLBACK;

MINIDUMP_CALLBACK_OUTPUT structure is not used.

ThreadCallback

這個回調類型的目的和ModuleCallback 對於模塊的作用一樣。回調類型的基本原則也一樣。MINIDUMP_CALLBACK_OUTPUT中的聯合包括了一系列的標誌(ThreadWriteFlags),回調函數可以清除部分或者全部標記,來從minidump清除相應的線程信息。

MINIDUMP_CALLBACK_INPUT提供了很多種關於線程的信息。這裏面的聯合可以解釋成MINIDUMP_THREAD_CALLBACK (Figure 13)。包括了線程ID和句柄、線程上下文、線程棧的邊界。爲了保證MiniDumpWriteDump繼續運行,回調函數必須返回TRUE.

Figure 13:

typedef struct _MINIDUMP_THREAD_CALLBACK {

    ULONG ThreadId;

    HANDLE ThreadHandle;

    CONTEXT Context;

    ULONG SizeOfContext;

    ULONG64 StackBase;

    ULONG64 StackEnd;

} MINIDUMP_THREAD_CALLBACK, *PMINIDUMP_THREAD_CALLBACK;

Figure 14種表格列出了所有常用標誌,以及他們所代表的信息。

Figure 14:

Flag

Description

ThreadWriteThread

通過這個標誌可以從minidump中清除一個線程的所有信息。如果回調函數清除了這個標誌,所有其他的標誌都會被忽略。Minidump就不保存任何關於這個線程的信息了。
通常,這個標誌總是被設置的。

ThreadWriteStack

這個標誌允許從minidump中清除線程棧的內容。因此,如果回調函數清除了這個標誌,調試器就沒辦法看到線程的調用棧了。線程棧通常有幾KB ,極少數情況可以達到幾MB。因此這個標誌會影響minidump的大小。
通常,這個標誌總是被設置的。

ThreadWriteContext

通過這個標誌可以清除線程上下文的內容(定義在winnt.h中的CONTEXT結構體)。如果回調清除了這個標誌,調試器就不能看到線程上下文和調用棧,所有寄存器會被置成0。
通常線程上下文不會佔據很大的minidump 空間(X86環境下是716字節),因此對於minidump大小的影響很小。
通常,這個標誌總是被設置的。

ThreadWriteInstructionWindow

通過這個標誌可以清除線程指令窗口(當前執行指針附近的256字節)。如果清除這個標誌,就沒有辦法直接看到出故障時的反彙編代碼。如果想看到,就必須在開發者的計算機上裝載相應的模塊。

ThreadWriteThreadInfo

只有給MiniDumpWriteDump 傳遞了MiniDumpWithThreadInfo 參數時,這個標誌才被設置。通過這個標誌,可以清除minidump中的額外線程信息。(參考本文中關於MiniDumpWithThreadInfo的解釋)

ThreadWriteThreadData

只有給MiniDumpWriteDump 傳遞了MiniDumpWithProcessThreadData參數時,這個標誌才被設置。通過這個標誌可以從minidump中清除線程的特別信息(TEB的內容、TLS存儲和一些附加信息)

MemoryCallback

有些時候,我們肯能希望在minidump中添加一些額外內存區域的內容。例如,我們可能在堆上分配了一些數據(也可能是通過VirtualAlloc),希望在調試minidump的時候能夠看到這些數據。我們可以通過MemoryCallback來完成這個功能。MiniDumpWriteDump會在通過回調調用處理完線程和模塊之後調用這個回調函數。

當使用MemoryCallback 作爲回調函數的回調參數時,MINIDUMP_CALLBACK_OUTPUT 中的聯合會被解析成:

struct {

  ULONG64 MemoryBase;

  ULONG MemorySize;

};

如果回調函數在這個結構體中寫入可讀內存塊的資質和大小,並且返回TRUE,這個內存塊的內容就會被放到minidump中。我們可以添加多個內存塊。當回調函數返回TRUE的時候,這個回調會被再次調用。MiniDumpWriteDump會一直等到返回FALSE才停止調用這個回調函數。

CancelCallback

MiniDumpWriteDump會定期調用這個回調類型。這個回調類型允許終止創建minidump的過程,這對於GUI應用程序很有用。MINIDUMP_CALLBACK_OUTPUT結構體被解析成兩個值,Cancel和 CheckCancel:

struct {

    BOOL CheckCancel;

    BOOL Cancel;

};

如果我們希望徹底取消創建minidump,我們應該把Cancel設成TRUE。如果我們不想取消minidump,而只是不想再接收CancelCallback的回調,就把CheckCancel設成TRUE。如果兩個成員都設置成FALSE,MiniDumpWriteDump就不再使用CancelCallback調用回調函數。

回調函數應該返回TRUE來確認MINIDUMP_CALLBACK_OUTPUT 的值被設置了。

回調的順序

在討論完回調的類型之後,我們可能會關心這些回調類型的順序。調用的順序如下:

  • IncludeThreadCallback – 進程中的每一個線程一次
  • IncludeModuleCallback –進程中每一個可執行模塊一次
  • ModuleCallback – 沒有被IncludeModuleCallback排除的模塊,每個調用一次
  • ThreadCallback –沒有被IncludeThreadCallback排除的線程,每個調用一次
  • MemoryCallback會調用一次或者多次,一直到回調函數返回FALSE

另外,CancelCallback 會在其他回調類型之間定期調用。這樣,保證在需要的時候可以中斷minidump的創建過程。

這個例子程序(http://www.debuginfo.com/examples/src/effminidumps/CallbackOrder.cpp)會顯示實際的調用順序。你也可以使用MiniDump Wizard來測試各種回調(http://www.debuginfo.com/tools/minidumpwizard.html)。

MiniDump Wizard

你可以使用MiniDump Wizard 來試驗各種minidump的選項並且看到他們會怎麼影響minidump的大小和內容。MiniDump Wizard可以創建任意進程的minidump。它也可以模擬異常來創建自己的mindump文件。你可以選擇把哪些類型標誌 傳遞給MiniDumpWriteDump ,然後通過一系列的對話框對回調請求做出響應。

當創建完minidump,可以在一個調試器中裝載它,然後查看包括了哪些信息。也可以使用MinDumpView(http://www.debuginfo.com/tools/minidumpview.html)應用來得到minidump中內容的清單。

用戶數據流

除了MiniDumpWriteDump已經捕獲的成功調試需要的所有應用程序狀態之外,我們經常需要程序運行環境的一些額外信息。例如,如果可以查看配置文件的內容或者應用程序相關的註冊表設置會很有幫助。Minidump允許把這些信息作爲額外數據流添加進來。

這個例子程序顯示瞭如何做到這一點(http://www.debuginfo.com/examples/src/effminidumps/WriteUserStream.cpp)。我們需要聲明一個MINIDUMP_USER_STREAM_INFORMATION變量,在裏面填充流的數量和用戶數據流的指針數組。每個用戶數據流用一個MINIDUMP_USER_STREAM結構體表示。結構體裏面包括流的類型、大小、以及一個指向流數據的指針。流類型是識別流的一個唯一標誌,必須是一個比LastReservedStream大的常數。

Figure 14:

typedef struct _MINIDUMP_USER_STREAM_INFORMATION {

    ULONG UserStreamCount;

    PMINIDUMP_USER_STREAM UserStreamArray;

} MINIDUMP_USER_STREAM_INFORMATION, *PMINIDUMP_USER_STREAM_INFORMATION;

 

typedef struct _MINIDUMP_USER_STREAM {

    ULONG32 Type;

    ULONG BufferSize;

    PVOID Buffer;

} MINIDUMP_USER_STREAM, *PMINIDUMP_USER_STREAM;

當我們向一個minidump 添加了用戶數據流,我們可以通過MiniDumpReadDumpStream 函數來讀出這些信息。這個例子程序(http://www.debuginfo.com/examples/src/effminidumps/WriteUserStream.cpp)顯示瞭如何從前一個例子(http://www.debuginfo.com/examples/src/effminidumps/WriteUserStream.cpp)寫入的例子數據。

策略

MiniDumpWriteDump有豐富功能和大量的可用選項。這使得找到一個所有應用都適用的策略會很困難。對於每一個特定的情況,應用程序的開發者必須決定哪些選項對他們的調試工作有用。在這我會試着描述一些基本策略,用來解釋如何把MiniDumpWriteDump的配置選項應用到真實場景中。我們會看到四種不同的MiniDumpWriteDump收集數據的策略。並且來了解他們會對minidump的大小和調試的可能性發生什麼影響。

TinyDump

這不是一個真實世界的場景。這個方法顯示了怎麼樣來創建一個最小可能數據集的minidump,來使它有一點用途。Figure 15總結了這種MiniDumpWriteDump配置選項。

Figure 15:

MINIDUMP_TYPE標誌

MiniDumpNormal

MiniDumpCallback

IncludeThreadCallback – exclude all threads
IncludeModuleCallback – exclude all modules

實現這種方式的例子程序在這個地址http://www.debuginfo.com/examples/src/effminidumps/TinyDump.cpp

結果minidump非常小,在我的系統上非常小。並不令人驚訝,我們去掉了所有線程和模塊的信息。如果你試着用WinDbg or VS.NET debugger來裝載,你會發現調試器沒有辦法裝載它。

但是,這個minidump還包含了異常的信息,所以不是完全無用,我們可以手工讀取這些信息(使用MiniDumpReadDumpStream函數),可以看到異常發生的地址、異常時刻的線程上下文、異常代碼甚至反彙編。你可以使用MinDumpView工具(http://www.debuginfo.com/tools/minidumpview.html)來查看其中的信息。爲了保持工具簡單,沒有提供返彙編。

MiniDump

不像TinyDump,這種方式對於真實世界場景是有用的。它收集了足夠的調試信息同時又保持minidump足夠小。Figure 16中的表格描述了相應的MiniDumpWriteDump配置項。

Figure 16:

MINIDUMP_TYPE

MiniDumpWithIndirectlyReferencedMemory,
MiniDumpScanMemory

MiniDumpCallback

IncludeThreadCallback – 包括所有線程
IncludeModuleCallback – 包括所有模塊
ThreadCallback – 包括所有線程
ModuleCallback – 檢查ModuleWriteFlags ,把所有ModuleReferencedByMemory 沒有設置的模塊排除掉(清除這些模塊的ModuleWriteModule標誌)

可以在這找到例子程序(http://www.debuginfo.com/examples/src/effminidumps/MiniDump.cpp)

結果的mindump文件仍然很小(在我的系統上大約40-50KB)。他比mindump的標準方式(MiniDumpNormal + no MiniDumpCallback))包含了更多的信息量。他允許查看棧上的引用的數據。爲了優化大小,我們把所有線程棧沒有引用的模塊從minidump中去掉了(在我的系統上,advapi32.dll 和rpcrt4.dll被去掉了)。

但是,這個minidump還缺少一些重要的信息。例如,我們看不到全局標量的值,不能查看堆和TLS中分配的數據(除非他們被線程棧引用了)。

MidiDump

下一個方式會產生一個信息量充足的minidump,同時保證文件不會過大。Figure 17的表格描述了配置。

Figure 17:

MINIDUMP_TYPE flags

MiniDumpWithPrivateReadWriteMemory,
MiniDumpWithDataSegs,
MiniDumpWithHandleData,
MiniDumpWithFullMemoryInfo,
MiniDumpWithThreadInfo,
MiniDumpWithUnloadedModules

MiniDumpCallback

IncludeThreadCallback –包括所有線程
IncludeModuleCallback – 包括所有模塊
ThreadCallback – 包括所有線程
ModuleCallback – 只保留主模塊和ntdll.dll的數據區

例子程序可以在這看到(http://www.debuginfo.com/examples/src/effminidumps/MidiDump.cpp)。minidump的大小在我的系統上大約1350KB。當在調試器中裝載的時候,我們可以得到應用程序的幾乎所有信息,包括全局變量的值、堆和TLS的內容、PEB、TEB。我們甚至可以得到句柄信息以及虛擬內存佈局。這是一個非常有用的dump,並且不是很大。下面的信息沒有包括在mindump中:

  • 所有模塊的代碼區(如果我們可以得到這些模塊,就不需要他們)
  • 某些模塊的數據區(我們只包括了我們希望看到全局變量的模塊的數據區)

MaxiDump

最後一個例子顯示瞭如何創建一個包含所有可能數據的minidump。Figure 18的表格顯示瞭如何做到這一點。

Figure 18:

MINIDUMP_TYPE flags

MiniDumpWithFullMemory,
MiniDumpWithFullMemoryInfo,
MiniDumpWithHandleData,
MiniDumpWithThreadInfo,
MiniDumpWithUnloadedModules

MiniDumpCallback

Not used

例子程序可以在這找到http://www.debuginfo.com/examples/src/effminidumps/MaxiDump.cpp。

這個minidump對於這樣一個簡單程序來說已經很大了(在我的系統上有8MB)。但是,它給了我們在一個mindump中包含所有信息的可能。

對比

Figure 19的表格比較和四種方式創建的minidump的大小。除了這個例子程序的數據之外(它和真實程序會有一定差距),我還添加了一個真實程序的數據。同樣也使用這四種不同的minidump。

Figure 19:

TinyDump

MiniDump

MidiDump

MaxiDump

例子程序

2 KB

40-50 KB

1,35 MB

8 MB

真實程序

2 KB

200 KB

14 MB

35 MB

補充

關於64位系統

這篇文章沒有討論MiniDumpWriteDump 中關於64位系統的選項。我的實驗室裏面沒有64位的機器,我沒有辦法提供關於他們的更有效信息。

關於 DbgHelp版本

DbgHelp.dll一直在不斷改進。新的特性會隨着Debugging Tools for Windows工具包的新版本推出。在寫這篇文章的時候,使用的版本是DbgHelp.dll 6.3。

例子程序

這篇文上涉及的所有例子程序(包括編譯指令)可以在這找到。(http://www.debuginfo.com/examples/effmdmpexamples.html)

聯繫方式

Have questions or comments? Free free to contact Oleg Starodumov at [email protected].

 

這份文件翻譯自

http://www.debuginfo.com/articles/effminidumps.html

http://www.debuginfo.com/articles/effminidumps2.html

發佈了8 篇原創文章 · 獲贊 1 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章