Dieser Artikel wurde maschinell übersetzt.

Sysinternals ProcDump v4.0

Schreiben eines Plug-Ins für Sysinternals ProcDump v4.0

Andrew Richards

Sie haben wurden ganze Nacht installieren die neueste Aktualisierung Ihrer geschäftskritischen Anwendung und alles gegangen ist perfekt.Und dann passiert es — die Anwendung hängt, wie jeder beginnt am Arbeitsplatz kommen.Manchmal müssen Sie wie folgt, schneiden Sie Ihre Verluste, akzeptieren, dass die Version ein Fehler ist, relevante Beweise so schnell wie möglich zu sammeln, und starten Sie die immer wichtige Rollbackplan.

Ein Speicherabbild des Antrags in Zeiten wie diese Aufnahme ist eine gemeinsame Taktik zur Problembehandlung, ob Gründen hängen, Absturz oder Leistung.Die meisten Schirmsicherungs-Müllkippe-Tools eine-Chance Ansatz: sie geben Ihnen alles (vollständige Deponien) oder sehr wenig (Mini-Dumps).Die Mini-Dumps sind in der Regel so klein, dass fruchtbare Debug Analyse nicht möglich, weil die Haufen fehlen.Vollständige Deponien sind seit jeher bevorzugt, aber sie sind selten eine Option mehr.Immer mehr Speicher bedeutet, dass ein voller Dump 15, 30 oder sogar 60 Minuten in Anspruch nehmen kann.Darüber hinaus zunehmend die Dump-Dateien so groß, dass sie leicht zur Analyse, verschoben werden können um selbst wenn komprimiert.

Im vergangenen Jahr Sysinternals ProcDump v3. 0 eingeführt, den MiniPlus-Schalter (-mp) zum Beheben des Problems Größe für systemeigene Anwendungen.Dies erstellt ein Abbild, die irgendwo zwischen ein Miniabbild und Dumps voller Größe ist.Schalters MiniPlus Speicher Entscheidungen basieren auf einer Vielzahl von heuristischen Algorithmen, die halten Typ des Speichers, Speicherschutz, Zuordnungsgröße Gebiet Größe und Stapel Inhalt.Die Dump-Datei kann abhängig vom Layout der Zielanwendung 50 Prozent bis 95 Prozent kleiner als einen vollständigen Dump.Noch wichtiger ist, ist der Dump so funktionell wie einen vollständigen Dump für die meisten Analyseaufgaben.Wenn der Schalter MiniPlus auf der Microsoft Exchange 2010 Information Store mit 48 GB Speicher angewendet wird, ist das Ergebnis einer 1GB–2GB Dump-Datei (eine 95 Prozent Verminderung).

Mark Russinovich und ich haben auf eine neue Version des ProcDump gearbeitet, die jetzt den Speicher Entscheidungen werden können.Sysinternals ProcDump v4. 0 macht die gleiche-API, die MiniPlus intern als eine externe verwendet DLL-basierte Plug-in über die -d-Schalter.

In diesem Artikel werde ich sezieren wie Sysinternals ProcDump v4. 0 arbeiten durch den Bau einer Reihe von Beispiel-Applikationen, die auf einander, implementieren mehr und mehr der ProcDump Funktionalität erweitern.Durch Eintauchen in die Funktionsweise von ProcDump unter der Decke, werde ich Ihnen zeigen, wie Sie ein Plug-in schreiben können, die mit ProcDump und der zugrunde liegenden DbgHelp-API interagiert.

Der Codedownload enthält Beispielanwendungen und auch eine Sammlung von Anwendungen, die auf verschiedene Weise abstürzen (so Sie den Code testen können).Im MiniDump05 Beispiel hat alle die APIs als Standalone-Anwendung implementiert.Das MiniDump06-Beispiel implementiert das Beispiel MiniDump05 als plug-in für Sysinternals ProcDump v4. 0.

Terminologie

Es ist leicht zu erreichen alle zugeordneten Müllkippe Auflistung verwirrt Begriffe – der Begriff "Mini" viel benutzt.Gibt das Dateiformat MiniDump, Mini und MiniPlus Müllkippe Inhalt und die Funktionen MiniDumpWriteDump und MiniDumpCallback.

Windows unterstützt das Dateiformat MiniDump über DbgHelp.dll.Eine MiniDump-Dump-Datei (*.dmp) ist ein Container, der teilweise oder vollständige Erfassung des Speichers in einem Benutzer-Modus oder im Kernel-Modus-Ziel unterstützt.Das Dateiformat unterstützt die Verwendung von "Streams" um zusätzlichen Metadaten (Kommentare, Prozess-Statistiken usw.) zu speichern.Das Dateiformat Name leitet sich von der Verpflichtung zur Unterstützung der Einnahme von eine minimale Menge an Daten.Die DbgHelp API MiniDumpWriteDump und MiniDumpCallback Funktionen sind vorangestellt MiniDump das Dateiformat übereinstimmen, die, das Sie produzieren.

Mini, sind MiniPlus und voller zur Beschreibung die verschiedenen Mengen von Inhalten in die Dump-Dateien verwendet.Mini ist die kleinste (mindestens) und umfasst der Prozess Umgebungsblock (PEB) Thread Umwelt blockiert (TEBs), teilweise Stacks, geladene Module und Datensegmente.Mark und ich prägte MiniPlus zur Beschreibung der Inhalte der ein Sysinternals ProcDump -mp zu erfassen; Es beinhaltet den Inhalt über eine Mini-Dump, plus Speicher heuristisch als wichtig erachtet.Und einen vollständigen Dump (procdump.exe-Ma) umfasst den gesamten virtuellen Adressraum des Prozesses, unabhängig davon, ob der Speicher im RAM ausgelagert wird.

MiniDumpWriteDump-Funktion

Um einen Prozess in MiniDump-Datei-Format in einer Datei erfassen, rufen Sie die Funktion DbgHelp MiniDumpWriteDump.Die Funktion erfordert einen Handle für den Zielprozess (mit PROCESS_QUERY_INFORMATION und PROCESS_VM_READ), die PID des Zielprozesses, ein Handle für eine Datei (mit FILE_GENERIC_WRITE), eine Bitmaske aus Flags MINIDUMP_TYPE und drei optionale Parameter: eine Ausnahme Informationsstruktur (verwendet, um einen Ausnahmedatensatz Kontext enthalten); eine Stream Benutzerinformationen-Struktur (häufig verwendet, um einen Kommentar in der Dump über die CommentStreamA/W MINIDUMP_STREAM_TYPE-Typen enthalten); und ein Rückruf Informationsstruktur (zum ändern, was während des Aufrufs erfasst ist):

BOOL WINAPI MiniDumpWriteDump(
  __in  HANDLE hProcess,
  __in  DWORD ProcessId,
  __in  HANDLE hFile,
  __in  MINIDUMP_TYPE DumpType,
  __in  PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
  __in  PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
  __in  PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);

Der MiniDump01-Beispielanwendung (finden Sie unter Abbildung 1) gezeigt, wie Sie MiniDumpWriteDump zu einen Mini-Dump ohne die optionalen Parameter aufrufen. Es beginnt mit der Überprüfung die Befehlszeilenargumente für eine PID und ruft dann OpenProcess einen Prozesshandle des Ziels ab. Es wird dann CreateFile aufgerufen, ein Dateihandle abzurufen. (Beachten Sie, dass MiniDumpWriteDump I/O-Ziel unterstützt.) Die Datei ist ein ISO Datum/Zeit-basierte Dateiname für Einzigartigkeit und Sortierung nach: C:\dumps\minidump_YYYY-MM-DD_HH-MM-SS-MS.dmp. Das Verzeichnis ist hartcodiert, C:\dumps, schreiben Zugang zu gewährleisten. Dies ist erforderlich, wenn Sie post-mortem Debuggen, da der aktuelle Ordner (z. B. System32) möglicherweise nicht beschreibbar.

Abbildung 1 MiniDump01.cpp

// MiniDump01.cpp : Capture a hang dump.
//
#include "stdafx.h"
#include <windows.h>
#include <dbghelp.h>
int WriteDump(HANDLE hProcess, DWORD dwProcessId, HANDLE hFile, MINIDUMP_TYPE miniDumpType);
int _tmain(int argc, TCHAR* argv[])
{
  int nResult = -1;
  HANDLE hProcess = INVALID_HANDLE_VALUE;
  DWORD dwProcessId = 0;
  HANDLE hFile = INVALID_HANDLE_VALUE;
  MINIDUMP_TYPE miniDumpType;
  // DbgHelp v5.2
  miniDumpType = (MINIDUMP_TYPE) (MiniDumpNormal | MiniDumpWithProcessThreadData |
    MiniDumpWithDataSegs | MiniDumpWithHandleData);
  // DbgHelp v6.3 - Passing unsupported flags to a lower version of DbgHelp
     does not cause any issues
  miniDumpType = (MINIDUMP_TYPE) (miniDumpType | MiniDumpWithFullMemoryInfo |
    MiniDumpWithThreadInfo);
  if ((argc == 2) && (_stscanf_s(argv[1], _T("%ld"), &dwProcessId) == 1))
  {
    // Generate the filename (ISO format)
    SYSTEMTIME systemTime;
    GetLocalTime(&systemTime);
    TCHAR szFilename[64];
    _stprintf_s(szFilename, 64, _T("c:\\dumps\\minidump_%04d-%02d-
      %02d_%02d-%02d-%02d-%03d.dmp"),
        systemTime.wYear, systemTime.wMonth, systemTime.wDay,
        systemTime.wHour, systemTime.wMinute, systemTime.wSecond,
        systemTime.wMilliseconds);
    // Create the folder and file
    CreateDirectory(_T("c:\\dumps"), NULL);
    if ((hFile = CreateFile(szFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
      FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
    {
      _tprintf(_T("Unable to open '%s' for write (Error: %08x)\n"), szFilename,
        GetLastError());
      nResult = 2;
    }
    // Open the process
    else if ((hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId)) == NULL)
    {
      _tprintf(_T("Unable to open process %ld (Error: %08x)\n"), dwProcessId,
        GetLastError());
      nResult = 3;
    }
    // Take a hang dump
    else
    {
      nResult = WriteDump(hProcess, dwProcessId, hFile, miniDumpType);
    }
    if (hFile) CloseHandle(hFile);
    if (hProcess) CloseHandle(hProcess);
    if (nResult == 0)
    {
      _tprintf(_T("Dump Created - '%s'\n"), szFilename);
    }
    else
    {
      DeleteFile(szFilename);
    }
  }
  else
  {
    _tprintf(_T("Usage: %s <pid>\n"), argv[0]);
    nResult = 1;
  }
  return 0;
}
int WriteDump(HANDLE hProcess, DWORD dwProcessId, HANDLE hFile, MINIDUMP_TYPE miniDumpType)
{
  if (!MiniDumpWriteDump(hProcess, dwProcessId, hFile, miniDumpType, NULL, NULL, NULL))
  {
    _tprintf(_T("Failed to create hang dump (Error: %08x)\n"), GetLastError());
    return 11;
  }
  return 0;
}

Der Parameter DumpType ist eine MINIDUMP_TYPE-basierte Bitmaske, die die Aufnahme (oder den Ausschluss) von bestimmten Arten von Speicher verursacht. Die MINIDUMP_TYPE-Flags sind sehr mächtig und können Sie die Erfassung von vielen Speicherbereiche ohne die Notwendigkeit für zusätzliche Codierung über einen Rückruf direkt. Die Optionen wird im MiniDump01 Beispiel sind die gleichen wie die von ProcDump verwendet. Sie erstellen einen Dump (Mini), der zum Zusammenfassen von eines Prozess verwendet werden kann.

Die DumpType hat immer das MiniDumpNormal Flag zu präsentieren, denn es einen Wert von 0 x 00000000 hat. Die DumpType verwendet, enthält jeder Stapel (MiniDumpNormal), alle Informationen zu PEB und TEB (MiniDumpWithProcessThreadData), die geladene Modul-Informationen sowie alle Globals (MiniDumpWithDataSegs), behandeln alle Informationen (MiniDumpWithHandleData), alle Speicher Regionsinformationen (MiniDumpWithFullMemoryInfo) und alle Threads Zeit und Affinität Informaton (MiniDumpWithThreadInfo). Mit diesen Flags, das Speicherabbild erstellt ist ein rich-Version von einem Mini-Dump, aber es ist immer noch recht klein (weniger als 30 MB sogar für die größten Appli­kationen). Beispiel Debuggerbefehle gestützt durch diese MINIDUMP_TYPE-Flags finden Sie Abbildung 2.

Abbildung 2 Debugger-Befehle

MINIDUMP_TYPE Debugger-Befehle
MiniDumpNormal knL99
MiniDumpWithProcessThreadData ! Peb! Teb
MiniDumpWithDataSegs LM, dt <global>
MiniDumpWithHandleData ! behandeln,! Cs
MiniDumpWithFullMemoryInfo ! Adresse
MiniDumpWithThreadInfo ! außer Kontrolle geratenen

Wenn mit MiniDumpWriteDump, den Dump ergriffen die Architektur des Capture-Programm, nicht das Ziel, übereinstimmen wird so benutzen Sie eine 32-Bit-Version von Ihrem-Capture-Programm bei der Aufnahme von eines 32-Bit-Prozess, und eine 64-Bit-Version von Ihrem-Capture-Programm bei der Aufnahme von eines 64-Bit-Prozess. Wenn Sie "Windows 32-Bit on Windows 64-Bit" (WOW64) Debuggen müssen, nehmen Sie ein 64-Bit-Abbild der 32-Bit-Prozess.

Wenn Sie die Architektur (zufällig oder absichtlich) nicht übereinstimmen, müssen Sie die effektive Maschine (.effmach X 86) im Debugger Zugriff auf die 32-Bit-Stacks in einen 64-Bit-Dump ändern. Beachten Sie, dass viele der Debuggererweiterungen in diesem Szenario schlägt fehl.

Ausnahme Kontextdatensatz

Microsoft Support-Technikern verwendet die Begriffe "hängen Dump" und "Crash-Dump". Wenn sie für einen Crash-Dump Fragen, wollen sie einen Dump mit einen Ausnahmedatensatz Kontext. Wenn sie für einen Dump hängen bitten, meinen sie (meist) eine Reihe von Deponien ohne ein. Ein Dump mit Informationen zur Ausnahme ist jedoch nicht immer aus der Zeit des eine Bankrotts; Es kann von jeder Zeit sein. Die Informationen zur Ausnahme ist nur ein Mittel, um zusätzliche Daten in ein Abbild bereitstellen. Die Benutzer Stream Infor­Mation ist ähnlich wie die Informationen zur Ausnahme in dieser Hinsicht.

Ein Ausnahmedatensatz Kontext ist die Kombination aus einer CONTEXT-Struktur (CPU-Register) und eine EXCEPTION_RECORD Struktur (die Ausnahme-Code, der Anweisung Adresse und So weiter). Wenn Sie einen Ausnahmedatensatz Kontext in der Dump und führen .ecxr einschließen, der aktuellen Debugger Kontext (Thread und Register Status) auf die Anweisung, die die Ausnahme ausgelöst festgelegt ist (siehe Abbildung 3).

Abbildung 3 Wechsel der Kontextdatensatz Ausnahme

Diese Abbilddatei ist eine Ausnahme von Interesse gespeichert.

Die gespeicherte Ausnahmeinformationen kann über .ecxr zugegriffen werden.

(17cc.1968174c.6f8): Access violation - code c0000005 (first/second chance not available)
eax=00000000 ebx=001df788 ecx=75ba31e7 edx=00000000 esi=00000002 edi=00000000
eip=77c7014d esp=001df738 ebp=001df7d4 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ntdll!NtWaitForMultipleObjects+0x15:
77c7014d 83c404          add     esp,4
0:000> .ecxr
eax=00000000 ebx=00000000 ecx=75ba31e7 edx=00000000 esi=00000001 edi=003b3374
eip=003b100d esp=001dfdbc ebp=001dfdfc iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
CrashAV_x86!wmain+0x140xd:
00000001`3f251014 45891b003b100d 8900            mov     dword ptr [r11],r11deax],eax  ds:002b:00000000`00000000=????????=????????

Förderung von .ecxr, die optional MINIDUMP_EXCEPTION_­Informationsstruktur an MiniDumpWriteDump übergeben werden muss. Sie können Informationen zur Ausnahme zur Laufzeit oder bei post-mortem erhalten.

Laufzeitausnahmen

Wenn Sie eine Debugger-Ereignis-Schleife implementieren, ist Ausnahmeinformationen an Sie weitergegeben, wenn die Ausnahme auftritt. Die Debugger-Ereignisschleife erhalten eine EXCEPTION_DEBUG_EVENT Struktur für Haltepunkte, Ausnahmen der ersten Chance und der zweiten Chance-Ausnahmen.

Die MiniDump02-Beispielanwendung veranschaulicht, wie MiniDumpWriteDump von innerhalb einer Debugger-Ereignis-Schleife aufgerufen, damit die zweite Chance der Kontextdatensatz in den Dump enthalten ist (entspricht "procdump.exe -e"). Diese Funktion wird ausgeführt, wenn Sie die -e-Option verwenden. Da der Code ziemlich lang ist, erscheint der Pseudo-Code für die Anwendung Abbildung 4. In diesem Artikel Codedownload für den vollständigen Quellcode finden Sie unter.

Abbildung 4 MiniDump02 Pseudo-Code

Function Main
Begin
  Check Command Line Arguments
  CreateFile(c:\dumps\minidump_YYYY-MM-DD_HH-MM-SS-MS.dmp)
  OpenProcess(PID)
  If "–e" Then
    DebugEventDump
    TerminateProcess(Process)
  Else
    WriteDump(NULL)
  CloseHandle(Process)
  CloseHandle(File)
End
Function WriteDump(Optional Exception Context Record)
Begin
  MiniDumpWriteDump(Optional Exception Context Record)
End
Function DebugEventDump
Begin
  DebugActiveProcess(PID)
  While (Not Done)
  Begin
    WaitForDebugEvent
    Switch (Debug Event Code)
    Begin
    Case EXCEPTION_DEBUG_EVENT
      If EXCEPTION_BREAKPOINT
        ContinueDebugEvent(DBG_CONTINUE)
      Else If "First Chance Exception"
        ContinueDebugEvent(DBG_EXCEPTION_NOT_HANDLED)
      Else "Second Chance Exception"
        OpenThread(Debug Event Thread ID)
        GetThreadContext
        WriteDump(Exception Context Record)
        CloseHandle(Thread)
        Done = True
    Case EXIT_PROCESS_DEBUG_EVENT
      ContinueDebugEvent(DBG_CONTINUE)
      Done = True
    Case CREATE_PROCESS_DEBUG_EVENT
      CloseHandle(CreateProcessInfo.hFile)
      ContinueDebugEvent(DBG_CONTINUE)
    Case LOAD_DLL_DEBUG_EVENT
      CloseHandle(LoadDll.hFile)
      ContinueDebugEvent(DBG_CONTINUE)
    Default
      ContinueDebugEvent(DBG_CONTINUE)
    End Switch
  End While
  DebugActiveProcessStop(PID)
End

Die Anwendung startet durch Überprüfen der Befehlszeilenargumente für eine PID. Anschließend ruft es OpenProcess einen Prozesshandle des Ziels ab und ruft dann CreateFile, ein Dateihandle abzurufen. Wenn der Switch -e fehlt dauert es Dumps hängen als vor. Wenn der -e-Schalter vorhanden ist, fügt die Anwendung an das Ziel (wie ein Debugger) mit DebugActiveProcess. In einer While-Schleife wartet eine DEBUG_EVENT Struktur, die von WaitForDebugEvent zurückgegeben werden. Die Switch-Anweisung verwendet den DwDebugEventCode-Member der DEBUG_EVENT-Struktur. Nach die Müllkippe getroffen wurden hat, oder der Prozess beendet hat, wird DebugActiveProcessStop aufgerufen, um vorbei zu trennen.

Die EXCEPTION_DEBUG_EVENT innerhalb der DEBUG_EVENT-Struktur enthält einen Ausnahmedatensatz innerhalb eine Ausnahme. Wenn die Ausnahmedatensatz ein Haltepunkt ist, wird sie behandelt lokal durch Aufrufen von ContinueDebugEvent mit DBG_CONTINUE. Wenn die Ausnahme eine erste Chance ist, ist nicht es behandelt, so dass es an eine Ausnahme der zweiten Chance wenden kann (wenn das Ziel einen Handler verfügt). Um dies zu tun, wird ContinueDebugEvent mit DBG_EXCEPTION_NOT_HANDLED aufgerufen. Das verbleibende Szenario ist eine Ausnahme der zweiten Chance. Mithilfe der DEBUG_EVENT-Struktur DwThreadId, wird OpenThread aufgerufen, um einen Handle für den Thread mit der Ausnahme erhalten. Das Threadhandle wird mit GetThreadContext zum Auffüllen der CONTEXT-Struktur erforderlich. (Ein Wort der Vorsicht hier: die CONTEXT-Struktur ist in der Größe im Laufe der Jahre gewachsen wie Prozessoren zusätzliche Register hinzugefügt wurden. Wenn eine spätere OS erhöht sich die Größe der Struktur CONTEXT, müssen Sie diesen Code neu kompilieren zu müssen.) Die erhaltene CONTEXT-Struktur und der EXCEPTION_RECORD aus den DEBUG_EVENT werden verwendet, um eine EXCEPTION_POINTERS-Struktur auffüllen und wird verwendet, um eine MINIDUMP_EXCEPTION_INFORMATION-Struktur aufzufüllen. Diese Struktur ist für die Verwendung mit MiniDumpWriteDump an die Anwendung WriteDump Funktion übergeben.

Die EXIT_PROCESS_DEBUG_EVENT ist speziell für das Szenario behandelt, wo endet das Ziel, bevor eine Ausnahme auftritt. ContinueDebugEvent mit DBG_CONTINUE, dieses Ereignis zu bestätigen aufgerufen wird und die While-Schleife beendet wird.

Die Ereignisse CREATE_PROCESS_DEBUG_EVENT und LOAD_DLL_DEBUG_EVENT sind speziell behandelt, wie ein HANDLE muss geschlossen werden. Diesen Bereichen aufrufen, ContinueDebugEvent mit DBG_CONTINUE.

Der Standardfall behandelt alle anderen Ereignisse durch Aufrufen von weiter­DebugEvent mit DBG_CONTINUE fortsetzen und schließen das Handle übergeben.

Post Mortem Ausnahmen

Windows Vista eingeführt einen dritten Parameter, um die Post Mortem Debugger Befehlszeile ein, um die Weitergabe von Informationen zur Ausnahme unterstützen. Um den dritten Parameter zu erhalten, müssen Sie einen Debugger-Wert (in der AeDebug Schlüssel) haben, der drei % ld Ersetzungen enthält. Die drei Werte sind: Prozess-ID, Ereignis-ID und JIT-Adresse. Die JIT-Adresse ist die Adresse einer JIT_DEBUG_INFO Struktur in das Ziel-Adressraum. Windows Error Reporting (WER) weist dieser Speicher in das Ziel-Adressraum, wenn WER als Ergebnis eine unbehandelte Ausnahme aufgerufen wird. Es füllt die JIT_DEBUG_INFO Struktur, ruft den Post-Mortem-Debugger (übergeben Sie die Adresse der Zuweisung) und gibt dann den Speicher frei, nach der Post-Mortem-Debugger beendet.

Um zu bestimmen, den Kontextdatensatz Ausnahme, liest der post-mortem Anwendung die JIT_DEBUG_INFO-Struktur aus der Ziel-Adressraum. Die Struktur hat die Adresse eine CONTEXT-Struktur und EXCEPTION_RECORD Struktur in das Ziel-Adressraum. Statt lesen die Kontext und EXCEPTION_RECORD Strukturen aus der Ziel-Adressraum, ich nur EXCEPTION_POINTERS-Struktur mit diesen Adressen ausgefüllt und dann das ClientPointers Mitglied in der MINIDUMP_EXCEPTION_INFORMATION-Struktur auf TRUE festgelegt. Dadurch wird den Debugger alle die schwere Arbeit zu tun. Es liest die Daten aus der Ziel-Adressraum (unter Berücksichtigung Unterschiede in der Architektur, so dass es möglich ist, nehmen ein 64-Bit-Abbild der 32-Bit-Prozess).

Die MiniDump03-Beispielanwendung veranschaulicht, wie die JIT_DEBUG_INFO-Unterstützung implementieren (siehe Abbildung 5).

Abbildung 5 MiniDump03 – JIT_DEBUG_INFO-Handler

int JitInfoDump(HANDLE hProcess, DWORD dwProcessId, HANDLE hFile, MINIDUMP_TYPE miniDumpType, ULONG64 ulJitInfoAddr)
{
  int nResult = -1;
  JIT_DEBUG_INFO jitInfoTarget;
  SIZE_T numberOfBytesRead;
  if (ReadProcessMemory(hProcess, (void*)ulJitInfoAddr, &jitInfoTarget, sizeof(jitInfoTarget), &numberOfBytesRead) &&
    (numberOfBytesRead == sizeof(jitInfoTarget)))
  {
    EXCEPTION_POINTERS exceptionPointers = {0};
    exceptionPointers.ContextRecord = (PCONTEXT)jitInfoTarget.lpContextRecord;
    exceptionPointers.ExceptionRecord = (PEXCEPTION_RECORD)jitInfoTarget.lpExceptionRecord;
    MINIDUMP_EXCEPTION_INFORMATION    exceptionInfo = {0};
    exceptionInfo.ThreadId = jitInfoTarget.dwThreadID;
    exceptionInfo.ExceptionPointers = &exceptionPointers;
    exceptionInfo.ClientPointers = TRUE;
    nResult = WriteDump(hProcess, dwProcessId, hFile, miniDumpType, &exceptionInfo);
  }
  else
  {
    nResult = WriteDump(hProcess, dwProcessId, hFile, miniDumpType, NULL);
  }
  return nResult;
}

Wenn eine Anwendung als Post-Mortem-Debugger aufgerufen wird, obliegt es nach der Anwendung den Prozess über einen-TerminateProcess-Aufruf gewaltsam beendet. Im Beispiel MiniDump03 ist-TerminateProcess-aufgerufen, nachdem die Müllkippe getroffen hat:

// Post Mortem (AeDebug) dump - JIT_DEBUG_INFO - Vista+
else if ((argc == 4) && (_stscanf_s(argv[3], _T("%ld"), &ulJitInfoAddr) == 1))
{
  nResult = JitInfoDump(hProcess, dwProcessId, hFile, miniDumpType, ulJitInfoAddr);
  // Terminate the process
  TerminateProcess(hProcess, -1);
}

Um den Post-Mortem-Debugger mit Ihrer eigenen Anwendung zu ersetzen, zeigen Sie den Debugger AeDebug Schlüssel/Wert auf die post-mortem Anwendung mit der geeigneten Architektur. Indem Sie die entsprechende Anwendung, entfernen Sie die Notwendigkeit, die effektive Maschine (.effmach) im Debugger anzupassen.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug
Debugger (REG_SZ) = "C:\dumps\minidump03_x64.exe %ld %ld %ld"
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug
Debugger (REG_SZ) = "C:\dumps\minidump03_x86.exe %ld %ld %ld"

MiniDumpCallback-Funktion

So weit, enthält die Müllkippe genommen Speicher, dass der DumpType-Parameter angegeben werden sollen (und optional einen Ausnahmedatensatz Kontext). Durch die Implementierung eines MiniDumpCallback Funktionsprototyps, können wir nicht nur zusätzliche Speicherbereiche, sondern auch einige Fehlerbehandlung hinzufügen. Ich werde beschreiben, später zum Implementieren von ausschließlich MiniDumpCallback Funktionsprototyp für die Verwendung mit Sysinternals ProcDump v4. 0.

Es gibt derzeit 16 Rückruf-Typen, mit denen Sie steuern mehrere Aspekte des Dumpings, wie des Speichers für Module und Threads, Speicher selbst, die Fähigkeit, einen Dump stornieren, in Fortschritt, die Kontrolle über die Müllkippe Dateieingabe/-Ausgabe und die Fehlerbehandlung enthalten.

Die Switch-Anweisung in meinem Template-Code (siehe Abbildung 6) umfasst alle Arten die Rückruf etwa in der Reihenfolge von ihrer ersten Invo­kation. Einige Rückrufe können mehr als einmal aufgerufen werden und können anschließend von Sequenz auftreten. Ebenso gibt es keinen Vertrag für die Bestellung und so es könnte in zukünftigen Versionen geändert.

Abbildung 6 Vorlagenimplementierung von der MiniDumpCallback Funktionsprototyp

BOOL CALLBACK MiniDumpCallbackRoutine(
  __in     PVOID CallbackParam,
  __in     const PMINIDUMP_CALLBACK_INPUT CallbackInput,
  __inout  PMINIDUMP_CALLBACK_OUTPUT CallbackOutput
)
{    // Callback supported in Windows 2003 SP1 unless indicated
  // Switch statement is in call order
  switch (CallbackInput->CallbackType)
  {
  case IoStartCallback:  //  Available in Vista/Win2008
    break;
  case SecondaryFlagsCallback:  //  Available in Vista/Win2008
    break;
  case CancelCallback:
    break;
  case IncludeThreadCallback:
    break;
  case IncludeModuleCallback:
    break;
  case ModuleCallback:
    break;
  case ThreadCallback:
    break;
  case ThreadExCallback:
    break;
  case MemoryCallback:
    break;
  case RemoveMemoryCallback:
    break;
  case WriteKernelMinidumpCallback:
    break;
  case KernelMinidumpStatusCallback:
    break;
  case IncludeVmRegionCallback:  //  Available in Vista/Win2008
    break;
  case IoWriteAllCallback:  //  Available in Vista/Win2008
    break;
  case IoFinishCallback:  //  Available in Vista/Win2008
    break;
  case ReadMemoryFailureCallback:  // Available in Vista/Win2008
    break;
  }
  return TRUE;
}

Das MiniDump04-Beispiel enthält einen Rückruf, der zwei Dinge tut; Es umfasst den gesamten Inhalt, und lesen Sie Fehler ignoriert. Dieses Beispiel verwendet ThreadCallback und MemoryCallback auf den gesamten Stapel, und die ReadMemoryFailureCallback, die lesen Sie Fehler zu ignorieren.

Zum Aufrufen des Rückrufs, die optionale MINIDUMP_CALLBACK_­Informationsstruktur an die MiniDumpWriteDump-Funktion übergeben wird. Die Struktur CallbackRoutine Mitglied wird verwendet, um auf die MiniDumpCallback-Funktion implementiert (MiniDumpCallbackRoutine in meine Vorlage und Beispiel) verweisen. Der CallbackParam-Member ist ein Zeiger vom Typ VOID *, der Ihnen erlaubt, Zusammenhang zwischen Delegatrückrufen behalten. Die WriteDump Funktion aus dem Mini­Dump04 Beispiel ist Abbildung 7.

Abbildung 7 WriteDump aus dem MiniDump04-Beispiel

int WriteDump(HANDLE hProcess, DWORD dwProcessId, HANDLE hFile, MINIDUMP_TYPE miniDumpType, PMINIDUMP_EXCEPTION_INFORMATION pExceptionParam)
{
  MemoryInfoNode* pRootMemoryInfoNode = NULL;
  MINIDUMP_CALLBACK_INFORMATION callbackInfo;
  callbackInfo.CallbackParam = &pRootMemoryInfoNode;
  callbackInfo.CallbackRoutine = MiniDumpCallbackRoutine;
  if (!MiniDumpWriteDump(hProcess, dwProcessId, hFile, miniDumpType, pExceptionParam, NULL, &callbackInfo))
  {
    _tprintf(_T("Failed to create hang dump (Error: %08x)\n"), GetLastError());
    while (pRootMemoryInfoNode)
    {    // If there was an error, we'll need to cleanup here
      MemoryInfoNode* pNode = pRootMemoryInfoNode;
      pRootMemoryInfoNode = pNode->pNext;
      delete pNode;
    }
    return 11;
  }
  return 0;
}

Ich habe (MemoryInfoNode) Zusammenhang zwischen Aufrufen beibehalten definierte Struktur ist eine Verknüpfung Listenknoten, der eine Adresse und eine Größe enthält etwa so:

struct MemoryInfoNode
{
  MemoryInfoNode* pNext;
  ULONG64 ulBase;
  ULONG ulSize;
};

Gesamten Stack

Wenn Sie das MiniDumpWithProcessThreadData-Flag in der DumpType-Parameter verwenden, sind die Inhalte von jedem Stapel aus dem Stapel Base aufgenommen, um die aktuelle Stapelzeiger. Meine MiniDumpCallbackRoutine-Funktion in das MiniDump04-Beispiel implementiert ergänzt dies durch die Einbeziehung des Rest des Stapels. Durch die Aufnahme des gesamten Stapels, Sie möglicherweise in der Lage, den Ursprung einer Stack-Müll zu bestimmen.

Ein Stack-Trash ist tritt ein Pufferüberlauf auf einen stapelbasierten Puffer. Der Pufferüberlauf schreibt über die Rücksprungadresse Stack und bewirkt, dass die Ausführung von "ret" Opcode Pop-Pufferinhalt als einen Anweisungszeiger, statt den Anweisungszeiger PUSHed durch die "Anruf" Opcode. Dies führt in Ausführung eine ungültige Speicheradresse, oder noch schlimmer, die Ausführung von einem zufälligen Stück Code.

Wenn ein Stapel Mülleimer auftritt, den Speicher unten (denken Sie daran, Stapel wachsen nach unten) der aktuelle Stapelzeiger wird noch die unveränderte Stapel Daten enthalten. Mit diesen Daten können Sie den Inhalt des Puffers bestimmen und — die meiste Zeit — die Funktionen, die aufgerufen wurden, um den Inhalt des Puffers zu generieren.

Wenn Sie den Speicher oberhalb des Limits Stack in ein mit und ohne die zusätzliche Stack-Speicher Dump vergleichen, werden Sie sehen, dass der Speicher als fehlend angezeigt wird (die? Symbol) in der normalen Dump, aber in der Dump gemacht mit dem Rückruf enthalten ist (siehe Abbildung 8).

Abbildung 8 Stack Inhalt Vergleich

0:003> !teb
TEB at 000007fffffd8000
  ExceptionList:        0000000000000000
  StackBase:            000000001b4b0000
  StackLimit:           000000001b4af000
  SubSystemTib:         0000000000000000
...
// No Callback
0:003> dp poi($teb+10) L6
00000000`1b4af000  ????????`????????
????????`????????
00000000`1b4af010  ????????`????????
????????`????????
00000000`1b4af020  ????????`????????
????????`????????
// Callback
0:003> dp poi($teb+10) L6
00000000`1b4af000  00000000`00000000 00000000`00000000
00000000`1b4af010  00000000`00000000 00000000`00000000
00000000`1b4af020  00000000`00000000 00000000`00000000

Der erste Teil des Codes "gesamten Stack" ist der Umgang mit den ThreadCallback Callback-Typ (siehe Abbildung 9). Diese Callback-Typ wird einmal pro Thread im Prozess aufgerufen. Der Rückruf wird eine MINIDUMP_THREAD_CALLBACK-Struktur über den CallbackInput-Parameter übergeben. Die Struktur enthält einen StackBase Member, die den Stapel des Threads Basis. Der StackEnd-Member ist der aktuelle Stapelzeiger (esp/Rsp für X 86 / x 64 bzw.). Die Struktur umfassen nicht das Stack-Limit (Teil von der Thread Umgebungsblock).

Abbildung 9 ThreadCallback dient zum Sammeln der Stapel Region jedes Threads

case ThreadCallback:
{    // We aren't passed the StackLimit so we use a 1MB offset from StackBase
  MemoryInfoNode** ppRoot = (MemoryInfoNode**)CallbackParam;
  if (ppRoot)
  {
    MemoryInfoNode* pNode = new MemoryInfoNode;
    pNode->ulBase = CallbackInput->Thread.StackBase - 0x100000;
    pNode->ulSize = 0x100000; // 1MB
    pNode->pNext = *ppRoot;
    *ppRoot = pNode;
  }
}
break;

Im Beispiel wird einen vereinfachten Ansatz und setzt voraus, dass der Stapel 1 MB groß ist – dies ist die Standardeinstellung für die meisten Anwendungen. Im Fall, dass dies unter den Stapelzeiger ist, bewirkt der DumpType-Parameter den Speicher enthalten sein. Im Fall, dass der Stack größer als 1 MB ist, wird ein partieller Stack enthalten sein. Und im Fall, dass der Stapel kleiner als 1 MB ist, zusätzliche Daten werden nur im Preis inbegriffen. Beachten Sie, dass wenn der Speicherbereich von der Rückruf angefordert eine kostenlose Region umfasst oder überlappt mit einem anderen Aufnahme, tritt kein Fehler auf.

Die StackBase und der 1 MB-Offset werden in eine neue Instanziierung der MemoryInfoNode-Struktur erfasst, die ich definiert haben. Die neue Instanziierung wird an der Vorderseite des Link-Liste hinzugefügt, die über das CallbackParam-Argument an den Rückruf übergeben wird. Nach der mehrere Aufrufe von ThreadCallback enthält die verknüpfte Liste mehrere Knoten zusätzlicher Speicher gehören.

Der letzte Teil des Codes "gesamten Stack" ist der Umgang mit den MemoryCallback Callback-Typ (siehe Abbildung 10). MemoryCallback heißt kontinuierlich während Sie zurück TRUE aus dem Rückruf, und bietet einen Wert ungleich NULL für die MemoryBase und MemorySize Mitglieder der MINIDUMP_CALLBACK_OUTPUT-Struktur.

Abbildung 10 MemoryCallback wird kontinuierlich während der Rückkehr einer Stack-Region bezeichnet

case MemoryCallback:
{    // Remove the root node and return its members in the callback
  MemoryInfoNode** ppRoot = (MemoryInfoNode**)CallbackParam;
  if (ppRoot && *ppRoot)
  {
    MemoryInfoNode* pNode = *ppRoot;
    *ppRoot = pNode->pNext;
    CallbackOutput->MemoryBase = pNode->ulBase;
    CallbackOutput->MemorySize = pNode->ulSize;
    delete pNode;
  }
}
break;

Der Code legt die Werte für den Parameter CallbackOutput fest und löscht anschließend den Knoten aus der verknüpften Liste. Nach mehrere Aufrufe von MemoryCallback die verknüpfte Liste wird keine weitere Knoten enthalten, und NULL-Werte sind bis Ende den MemoryCallback-Aufruf zurückgegeben. Beachten Sie, dass die MemoryBase und MemorySize Mitglieder sind 0 (null) Wenn übergeben; Sie müssen nur TRUE zurück.

MemoryListStream Bereich der Ausgabe des Befehls .dumpdebug können Sie sehen alle Speicherbereiche in den Dump (Beachten Sie, dass benachbarte Blöcke zusammengeführt werden könnte). siehe Abbildung 11.

Abbildung 11 .dumpdebug die Ausgabe des Befehls

0:000> .dumpdebug
----- User Mini Dump Analysis
...
Stream 3: type MemoryListStream (5), size 00000194, RVA 00000E86
  25 memory ranges
  range#    RVA      Address      Size
       0 0000101A    7efdb000   00005000
       1 0000601A    001d6000   00009734
       2 0000F74E    00010000   00021000
       3 0003074E    003b0f8d   00000100
       4 0003084E    003b3000   00001000
...
Read Memory Failure

Das letzte Stück der Code ist ziemlich einfach (siehe Abbildung 12). Er legt das Status-Mitglied der MINIDUMP_CALLBACK_OUTPUT-Struktur auf S_OK an, es ist OK, um einen Bereich des Arbeitsspeichers auszulassen, die während der Aufnahme nicht gelesen werden konnte.

Abbildung 12 ReadMemoryFailureCallback wird für lesen Fehler aufgerufen.

case ReadMemoryFailureCallback:  // DbgHelp.dll v6.5; Available in Vista/Win2008
  {    //  There has been a failure to read memory.
Set Status to S_OK to ignore it.
CallbackOutput->Status = S_OK;
  }
  break;

Diese einfache Implementierung implementiert der Rückruf die gleiche Funktionalität bereit wie das MiniDumpIgnoreInaccessibleMemory-Flag. Der ReadMemoryFailureCallback-Rückruf wird der Fehlercode, der Offset und Anzahl der Bytes über die Struktur der MINIDUMP_READ_MEMORY_FAILURE_CALLBACK im CallbackInput-Parameter übergeben. Diese Informationen können Sie einen komplexeren Rückruf festzustellen, ob der Speicher für Analyse dump von entscheidender Bedeutung ist, und ob die Müllkippe abgebrochen werden sollte.

Sezieren Memory

So wie wissen Sie, was Sie können und nicht leisten können, einen Dump zu entziehen? Sysinternals VMMap ist eine große Weise, wie eine Anwendung Speicher aussieht. Wenn Sie auf einen verwalteten Prozess Sysinternals VMMap verwenden, werden Sie feststellen, dass Zuordnungen zugeordnet die Garbage Collection (GC)-Heap gesammelt, und Zuordnungen mit der Anwendung Bilder und Dateien zugeordnet sind. Es ist der GC-Heap, den Sie in ein Abbild von einem verwalteten Prozess benötigen, da die Son of Strike (SOS)-Debuggererweiterung intakt Datenstrukturen von innerhalb des GC-Heaps, die Müllkippe zu interpretieren erforderlich ist.

Um den Speicherort des GC-Heaps bestimmen, können Sie einen anspruchsvollen Ansatz durch Starten einer Sitzung Debuggen Engine (DbgEng) gegen das Ziel mit DebugCreate und IDebugClient::AttachProcess nehmen. Mit dieser Debugsitzung konnte Sie die SOS-Debuggererweiterung laden und ausführen Befehle um die Heapinformationen (Dies ist ein Beispiel für die Verwendung Domänenwissen) zurückzugeben.

Alternativ können Sie Heuristik verwenden. Sie umfassen alle Regionen, die einen Speichertyp Private (MEMORY_PRIVATE) oder ein Schutz der Lese-/Schreibzugriff haben (PAGE_READWRITE oder PAGE_EXECUTE_­READWRITE). Diese sammelt mehr Speicher als absolut notwendig aber macht immer noch eine erhebliche Einsparung durch den Ausschluss der Anwendungs selbst. Das MiniDump05-Beispiel besitzt dieser Ansatz (siehe Abbildung 13) des MiniDump04-Beispiels Thread-Stack Code durch eine einmalige VirtualQueryEx Schleife in der ThreadCallback-Rückruf (die neue Logik noch verursacht den gesamten Stapel als vor werden) ersetzen. Es verwendet dann den gleichen MemoryCallback-Code in das Beispiel MiniDump04 verwendet, um den Speicher in den Dump aufnehmen.

Abbildung 13 MiniDump05 — ThreadCallback wird einmal verwendet, um die Speicherbereiche sammeln

case ThreadCallback:
{    // Collect all of the committed MEM_PRIVATE and R/W memory
  MemoryInfoNode** ppRoot = (MemoryInfoNode**)CallbackParam;
  if (ppRoot && !*ppRoot)    // Only do this once
  {
    MEMORY_BASIC_INFORMATION mbi;
    ULONG64 ulAddress = 0;
    SIZE_T dwSize = VirtualQueryEx(CallbackInput->ProcessHandle, (void*)ulAddress, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
    while (dwSize == sizeof(MEMORY_BASIC_INFORMATION))
    {
      if ((mbi.State == MEM_COMMIT) &&
        ((mbi.Type == MEM_PRIVATE) || (mbi.Protect == PAGE_READWRITE) || (mbi.Protect == PAGE_EXECUTE_READWRITE)))
      {
        MemoryInfoNode* pNode = new MemoryInfoNode;
        pNode->ulBase = (ULONG64)mbi.BaseAddress;
        pNode->ulSize = (ULONG)mbi.RegionSize;
        pNode->pNext = *ppRoot;
        *ppRoot = pNode;
      }
      // Loop
      ulAddress = (ULONG64)mbi.BaseAddress + mbi.RegionSize;
      dwSize = VirtualQueryEx(CallbackInput->ProcessHandle, (void*)ulAddress, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
    }
  }
}
break;

Zugeordneten Speicherdateien Bild

Sie wundern sich vielleicht, wie Sie einen Dump mit Bild Regionen (MEM_IMAGE) fehlende Debuggen können. Zum Beispiel: wie sehen Sie den Code, der ausgeführt wird? Die Antwort ist ein wenig aus-of-the-Box. Wenn der Debugger auf eine fehlende Bild-Region in Dumps nicht vollständig zugreifen muss, ruft es die Daten aus der Image-Datei stattdessen. Es findet die Image-Datei mithilfe der Pfad Modul laden, der PDB-Datei des ursprünglichen Image-Datei-Speicherort, oder verwendet die.sympath/.exepath-Suchpfade. Wenn Sie Lmvm <module> ausführen, sehen Sie ein "Zugeordnete Speicher Bilddatei" Zeile angibt, dass die Datei in den Dump zugeordnet wurde wie folgt:

0:000> lmvm CrashAV_x86
start    end        module name
003b0000 003b6000   CrashAV_x86   (deferred)            
  Mapped memory image file: C:\dumps\CrashAV_x86.exe
  Image path: C:\dumps\CrashAV_x86.exe
  Image name: CrashAV_x86.exe
...

Unter Berufung auf die "Zugeordnete Speicher Bilddatei" Fähigkeit des Debuggers ist ein großer Ansatz für die Größe der Deponien klein zu halten. Es funktioniert besonders gut mit native Anwendungen, weil die Binärdateien kompiliert die verwendeten sind und sind daher auf Ihrem internen Buildserver (und Spitzen, von den PDB-Dateien). Mit verwalteten Anwendungen erschwert die JIT-Kompilierung auf dem entfernten Computer des Kunden dies. Wenn Sie einen verwalteten Anwendung Dump von einem anderen Computer debuggen möchten, müssen Sie die Binärdateien (sowie die Deponien) kopieren lokal. Dies ist immer noch eine Einsparungen, da mehrere Abbilder schnell getroffen werden können, und dann kann eine einzelne (groß) Anwendung Datei Bildersammlung ohne Ausfall erfolgen. Um die Dateisammlung zu vereinfachen, können Sie die ModuleCallback, um ein Skript zu schreiben, die sammelt die Module (Dateien) in den Dump verwiesen wird.

Schließen Sie mich!

Die Standalone-Anwendung mithilfe von Sysinternals ProcDump ändern macht v4. 0 Ihr Leben viel einfacher. Sie müssen nicht mehr alle den Code aufrufen MiniDumpWriteDump zugeordnet, und, noch wichtiger ist, alle den Code, um den Dump zum richtigen Zeitpunkt auslösen implementieren. Sie müssen nur zum Implementieren einer MiniDumpCallback Funktion und als MiniDumpCallbackRoutine in einer DLL exportieren.

Das MiniDump06-Beispiel (siehe Abbildung 14) enthält den Rückrufcode der von MiniDump05 mit nur wenigen Änderungen.

Abbildung 14 MiniDumpCallbackRoutine geändert, um eine globale anstelle von CallbackParam verwenden

MemoryInfoNode* g_pRootMemoryInfoNode = NULL;
...
case IncludeThreadCallback:
{
  while (g_pRootMemoryInfoNode)
  {    //Unexpected cleanup required
    MemoryInfoNode* pNode = g_pRootMemoryInfoNode;
    g_pRootMemoryInfoNode = pNode->pNext;
    delete pNode;
  }
}
break;
...
case ThreadCallback:
{    // Collect all of committed MEM_PRIVATE and R/W memory
  if (!g_pRootMemoryInfoNode)    // Only do this once
  {
...
pNode->pNext = g_pRootMemoryInfoNode;
    g_pRootMemoryInfoNode = pNode;
...
}
}
break;
...
case MemoryCallback:
{    // Remove the root node and return its members in the callback
  if (g_pRootMemoryInfoNode)
  {
    MemoryInfoNode* pNode = g_pRootMemoryInfoNode;
    g_pRootMemoryInfoNode = pNode->pNext;
    CallbackOutput->MemoryBase = pNode->ulBase;
    CallbackOutput->MemorySize = pNode->ulSize;
    delete pNode;
  }
}
break;

Das neue MiniDump06-Projekt wird der Rückrufcode als DLL kompiliert. Das Projekt wird die MiniDumpCallbackRoutine (der Fall empfindlich) mithilfe einer DEF-Datei exportiert:

LIBRARY    "MiniDump06"
EXPORTS
  MiniDumpCallbackRoutine   @1

Da ProcDump CallbackParam der Wert NULL übergibt, muss die Funktion eine globale Variable stattdessen verwenden, um den Fortschritt über meine MemoryInfoNode Struktur zu verfolgen. Auf den ersten IncludeThreadCallback, gibt es neuer Code zurücksetzen (löschen) legen Sie die globale Variable If aus eine vorherige Aufnahme. Dies ersetzt den Code, der implementiert wurde, nach einem fehlgeschlagenen MiniDumpWriteDump in meiner WriteDump-Funktion aufrufen.

Um die DLL mit ProcDump verwenden, geben Sie der -d-Schalter gefolgt vom Namen der DLL, die die Architektur der Einnahme entspricht. Der -d-Schalter ist verfügbar, wenn dabei Mini (keine Option) sichert und voller (-Ma) sichert; Es ist nicht verfügbar, bei der Einnahme von MiniPlus (-mp) sichert:

procdump.exe -d MiniDump06_x64.dll notepad.exe
procdump.exe –ma -d MiniDump06_x64.dll notepad.exe

Beachten Sie, dass der Rückruf aufgerufen wird, mit verschiedenen Rückruf-Typen als die beschrieben in meine Proben bei vollen Dumps (-Ma) wird (siehe die MSDN Library-Dokumentation). Die MiniDumpWriteDump-Funktion behandelt den Dump als einen vollständigen Dump, wenn der DumpType-Parameter MiniDumpWithFullMemory enthält.

Sysinternals-ProcDump (procdump.exe) ist eine 32-Bit-Anwendung, die von sich selbst die 64-Bit-Version (procdump64.exe) extrahiert, wenn erforderlich. Nachdem procdump64.exe extrahiert und von procdump.exe ins Leben gerufen wurde, wird procdump64.exe Ihre (64-Bit)-DLL zu laden. Die 64-Bit-DLL Debuggen ist so ziemlich schwierig, da die gestartete Anwendung nicht das gewünschte Ziel ist. Die einfachste Sache zu tun, um Ihre 64-Bit-DLL Debuggen unterstützt wird die temporäre procdump64.exe in einen anderen Ordner kopieren und dann Debuggen mithilfe des Exemplars. Auf diese Weise keine Extraktion erfolgt, und Ihre DLL in die Anwendung Sie Starten geladen werden innerhalb des Debuggers (z. B. Visual Studio).

Pause!

Bestimmung des Ursprungs der Abstürze und Blockaden ist nicht einfach, wenn Sie nur einen Mini-Dump leisten können. Machen eine Abbilddatei mit wichtigen Zusatzinformationen löst dies ohne die Kosten für einen vollständigen Dump.

Wenn Sie bei der Umsetzung Ihrer eigenen Müllkippe Anwendung oder DLL interessiert, ich schlage vor, Sie untersuchen die Wirksamkeit der Sysinternals ProcDump, WER und AdPlus-Dienstprogramme zuerst — das Rad nicht neu erfinden.

Beim Schreiben eines Rückrufs stellen Sie sicher, dass Sie die Zeit zu verstehen, das genaue Layout des Speichers innerhalb der Anwendung. Sysinternals VMMap Snapshots und Dumps der Anwendung verwenden, um die Einzelheiten geht. Kleinere und kleinere Dumps ist eine iterative Ansatz. Mit/ohne offensichtliche Bereichen starten Sie, und dann zu verfeinern Sie Ihre Algorithmus. Sie müssen möglicherweise Heuristik und Domäne wissen Ansätze verwenden, um Sie an Ihr Ziel gelangen. Sie können Ihre Entscheidungsfindung helfen, indem Sie ändern die Zielanwendung. Beispielsweise könnten Sie bekannten (und einzigartige) Zuteilung Größen für jede Art von Speicher verwenden. Die wichtige Sache ist es, kreativ denken wird entscheiden, wie um zu bestimmen, welche Speicher erforderlich ist, in der Zielanwendung und die Dumpingspanne Anwendung.

Wenn Sie einen Kernel-Entwickler und Rückrufe interessiert sind, gibt es ein ähnlicher Kernel-basierten Mechanismus; finden Sie in der Dokumentation BugCheckCallback-Routine auf msdn.com.

Andrew Richards ist Microsoft senior Escalation Engineer für Windows OEM. Er hat eine Leidenschaft für Support-Tools, ist die Schaffung ständig Debugger Erweiterungen und Rückrufe und die Aufgabe vereinfacht das unterstützen Ingenieure. Er kontaktiert werden kann, um andrew.richards@microsoft.com.

Dank der folgenden technischen Experten für die Überprüfung dieses Artikels: Drew Bliss und Mark Russinovich