CRT-Debugverfahren

Wenn Sie ein Programm debuggen, das die C-Laufzeitbibliothek verwendet, sind diese Debugtechniken möglicherweise hilfreich.

Verwenden der CRT-Debugbibliothek

Die C-Runtime-Bibliothek (CRT) bietet umfassende Debuggingunterstützung. Um eine der CRT-Debugbibliotheken zu verwenden, müssen Sie eine Verknüpfung mit /DEBUG und kompilieren mit /MDd, /MTdoder /LDd.

Die Hauptdefinitionen und Makros für das CRT-Debugging finden Sie in der<crtdbg.h> Headerdatei.

Die Funktionen in den CRT-Debugbibliotheken werden mit Debuginformationen (/Z7, /Zd, /Zi, /ZI (Debuginformationsformat)) und ohne Optimierung kompiliert. Einige der Funktionen enthalten Assertionen, um die an sie übergebenen Parameter zu überprüfen. Außerdem ist Quellcode enthalten. Mit diesem Quellcode können Sie in die CRT-Funktionen springen, um sicherzustellen, dass sie erwartungsgemäß funktionieren, und fehlerhafte Parameter oder Speicherzustände suchen. (Einige CRT-Technologie ist proprietärer Code und stellt keinen Quellcode für die Ausnahmebehandlung, Gleitkomma und einige andere Routinen bereit.)

Weitere Informationen zum Verwenden der verschiedenen Laufzeitbibliotheken finden Sie unter C-Laufzeitbibliotheken.

Makros für die Berichterstellung

Für das Debuggen können Sie die _RPTn _RPTFn in dieser Definition definierten<crtdbg.h> Makros verwenden, um die Verwendung von printf Anweisungen zu ersetzen. Sie müssen sie nicht in #ifdef Direktiven einschließen, da sie in Ihrem Releasebuild automatisch ausgeblendet werden, wenn _DEBUG sie nicht definiert sind.

Makro Beschreibung
_RPT0, , _RPT1_RPT2, , _RPT3_RPT4 Gibt eine Meldungszeichenfolge und 0 (null) bis vier Argumente aus. Für _RPT1 die _RPT4Zeichenfolge dient die Nachrichtenzeichenfolge als Formatierungszeichenfolge im Printf-Stil für die Argumente.
_RPTF0, , _RPTF1_RPTF2, , _RPTF3_RPTF4 Identisch mit _RPTn, aber diese Makros geben auch den Dateinamen und die Zeilennummer aus, in der sich das Makro befindet.

Betrachten Sie das folgende Beispiel:

#ifdef _DEBUG
    if ( someVar > MAX_SOMEVAR )
        printf( "OVERFLOW! In NameOfThisFunc( ),
               someVar=%d, otherVar=%d.\n",
               someVar, otherVar );
#endif

Dieser Code gibt die Werte von someVar und otherVar an stdout. Sie können den folgenden Aufruf von _RPTF2 verwenden, um dieselben Werte und zusätzlich Dateinamen und Zeilennummer auszugeben:

if (someVar > MAX_SOMEVAR) _RPTF2(_CRT_WARN, "In NameOfThisFunc( ), someVar= %d, otherVar= %d\n", someVar, otherVar );

Einige Anwendungen benötigen möglicherweise Debugberichte, die die makros, die mit der C-Laufzeitbibliothek bereitgestellt werden, nicht bereitstellen. In diesen Fällen können Sie ein Makro schreiben, das speziell an Ihre Anforderungen angepasst ist. In einer Ihrer Headerdateien können Sie z. B. Code wie den folgenden einfügen, um ein Makro zu definieren, das aufgerufen wird ALERT_IF2:

#ifndef _DEBUG                  /* For RELEASE builds */
#define  ALERT_IF2(expr, msg, arg1, arg2)  do {} while (0)
#else                           /* For DEBUG builds   */
#define  ALERT_IF2(expr, msg, arg1, arg2) \
    do { \
        if ((expr) && \
            (1 == _CrtDbgReport(_CRT_ERROR, \
                __FILE__, __LINE__, msg, arg1, arg2))) \
            _CrtDbgBreak( ); \
    } while (0)
#endif

Ein Aufruf ALERT_IF2 kann alle Funktionen des printf Codes ausführen:

ALERT_IF2(someVar > MAX_SOMEVAR, "OVERFLOW! In NameOfThisFunc( ),
someVar=%d, otherVar=%d.\n", someVar, otherVar );

Sie können ein benutzerdefiniertes Makro problemlos ändern, um mehr oder weniger Informationen an verschiedene Ziele zu melden. Dieser Ansatz ist nützlich, da sich die Debuganforderungen weiterentwickeln.

Schreiben von Hookfunktionen zum Debuggen

Sie können verschiedene Arten von benutzerdefinierten Debug-Hook-Funktionen schreiben, mit denen Sie Ihren Code in einige vordefinierte Punkte innerhalb der normalen Verarbeitung des Debuggers einfügen können.

Hookfunktionen für Clientblöcke

Wenn Sie die in _CLIENT_BLOCK-Blöcken gespeicherten Daten überprüfen oder als Bericht ausgeben möchten, können Sie speziell für diesen Zweck eine Funktion schreiben. Die von Ihnen geschriebene Funktion muss einen Prototyp aufweisen, der wie folgt<crtdbg.h> definiert ist:

void YourClientDump(void *, size_t)

Mit anderen Worten, Ihre Hook-Funktion sollte einen void Zeiger auf den Anfang des Zuordnungsblocks akzeptieren, zusammen mit einem size_t Typwert, der die Größe der Zuordnung angibt, und rückgabe void. Andernfalls stehen Ihnen die Inhalte zur Seite.

Nachdem Sie Ihre Hook-Funktion mithilfe von _CrtSetDumpClient installiert haben, wird sie jedes Mal aufgerufen, wenn ein _CLIENT_BLOCK Block gedumpt wird. Mithilfe von _CrtReportBlockType können Sie anschließend Informationen zum Typ oder Untertyp der im Dump ausgegebenen Blöcke abrufen.

Der Zeiger auf ihre Funktion, an die Sie übergeben _CrtSetDumpClient , ist vom Typ _CRT_DUMP_CLIENT, wie in<crtdbg.h>:

typedef void (__cdecl *_CRT_DUMP_CLIENT)
   (void *, size_t);

Hookfunktionen für Reservierungen

Eine Zuordnungs-Hook-Funktion, die mithilfe _CrtSetAllocHookvon "installiert" installiert wird, wird jedes Mal aufgerufen, wenn Speicher zugewiesen, neu zugewiesen oder freigegeben wird. Diese Art von Hook kann für viele verschiedene Zwecke verwendet werden. Sie können damit beispielsweise testen, wie eine Anwendung auf Speichermangel reagiert, Reservierungsmuster überprüfen oder Reservierungsinformationen für die spätere Analyse protokollieren.

Hinweis

Beachten Sie die Einschränkung der Verwendung von C-Laufzeitbibliotheksfunktionen in einer Zuordnungs-Hook-Funktion, die in Zuordnungs-Hooks und Crt-Speicherzuordnungen beschrieben wird.

Der Prototyp einer Reservierungshookfunktion sollte etwa wie folgt aussehen:

int YourAllocHook(int nAllocType, void *pvData,
        size_t nSize, int nBlockUse, long lRequest,
        const unsigned char * szFileName, int nLine )

Der Zeiger, an _CrtSetAllocHook den Sie übergeben, ist vom Typ _CRT_ALLOC_HOOK, wie in<crtdbg.h>:

typedef int (__cdecl * _CRT_ALLOC_HOOK)
    (int, void *, size_t, int, long, const unsigned char *, int);

Wenn die Laufzeitbibliothek Ihren Hook aufruft, gibt das nAllocType Argument an, welcher Zuordnungsvorgang ausgeführt werden soll (_HOOK_ALLOC, _HOOK_REALLOCoder _HOOK_FREE). Bei einer Freigabe oder einer erneuten Reservierung enthält pvData einen Zeiger auf den Artikelbereich des freizugebenden Blocks. Im Falle einer Reservierung ist dieser Zeiger jedoch NULL, da die Reservierung nicht stattgefunden hat. Die verbleibenden Argumente enthalten die Größe der Zuordnung, den Blocktyp, eine sequenzielle Anforderungsnummer und einen Zeiger auf den Dateinamen. Wenn die Argumente verfügbar sind, enthalten sie auch die Zeilennummer, in der die Reservierung vorgenommen wurde. Nachdem die Hook-Funktion eine beliebige Analyse und andere vom Autor gewünschte Aufgaben ausführt, muss sie entweder TRUEzurückgegeben werden, was angibt, dass der Zuordnungsvorgang fortgesetzt werden kann, oder FALSE, was angibt, dass der Vorgang fehlschlagen soll. Ein einfacher Hook dieses Typs überprüft möglicherweise die bisher zugewiesene Arbeitsspeichermenge und gibt zurück FALSE , wenn dieser Betrag einen kleinen Grenzwert überschritten hat. Die Anwendung würde dann die Art von Zuordnungsfehlern erleben, die normalerweise nur auftreten würden, wenn der verfügbare Arbeitsspeicher niedrig war. Komplexere Hookfunktionen könnten Reservierungsmuster nachverfolgen, die Speichernutzung analysieren oder beim Auftreten bestimmter Situationen eine Meldung ausgeben.

Zuordnungshaken und CRT-Speicherzuordnungen

Eine wichtige Einschränkung der Zuordnungshakenfunktionen besteht darin, dass sie Blöcke explizit ignorieren _CRT_BLOCK müssen. Bei diesen Blöcken handelt es sich um die Speicherbelegungen, die von C-Laufzeitbibliotheksfunktionen intern vorgenommen werden, wenn Aufrufe an C-Laufzeitbibliotheksfunktionen gesendet werden, durch die interner Speicher belegt wird. Die _CRT_BLOCK-Blöcke können ignoriert werden, wenn Sie am Anfang der Belegungshookfunktion folgenden Code einfügen:

if ( nBlockUse == _CRT_BLOCK )
    return( TRUE );

Werden _CRT_BLOCK-Blöcke nicht vom Belegungshook ignoriert, kann es vorkommen, dass eine im Hook aufgerufene C-Laufzeitbibliotheksfunktion das Programm in eine Endlosschleife führt. Beispielsweise nimmt printf eine interne Reservierung vor. Wenn Ihr Hook-Code aufruft printf, wird die resultierende Zuordnung dazu führen, dass Der Hook erneut aufgerufen wird, was erneut aufgerufen printf wird usw., bis der Stapel überläuft. Wenn Sie _CRT_BLOCK-Reservierungsoperationen in einem Bericht ausgeben möchten, können Sie diese Beschränkung umgehen, indem Sie für die Formatierung und die Ausgabe anstelle der C-Laufzeitfunktionen Windows-API-Funktionen verwenden. Da der Heap der C-Laufzeitbibliothek nicht von Windows-APIs verwendet wird, führen sie den Belegungshook nicht in eine Endlosschleife.

Wenn Sie die Laufzeitbibliotheksquelldateien untersuchen, sehen Sie, dass sich die standardmäßige Zuordnungshakenfunktion ( _CrtDefaultAllocHook die einfach zurückgegeben TRUEwird) in einer eigenen Datei befindet. debug_heap_hook.cpp Wenn Ihr Zuordnungshaken auch für die Zuordnungen aufgerufen werden soll, die vom Laufzeitstartcode vorgenommen werden, der vor der Funktion Ihrer Anwendung main ausgeführt wird, können Sie diese Standardfunktion durch eine eigene funktion ersetzen, anstatt sie zu verwenden _CrtSetAllocHook.

Berichtshookfunktionen

Jedes Mal_CrtDbgReport, wenn ein Debugbericht generiert wird, wird eine Berichts-Hook-Funktion aufgerufen, die unter Verwendung _CrtSetReportHookinstalliert wird. Sie können mit dieser Funktion u. a. Berichte filtern, um bestimmte Reservierungstypen herauszustellen. Eine Berichtshakenfunktion sollte einen Prototyp wie dieses Beispiel aufweisen:

int AppReportHook(int nRptType, char *szMsg, int *retVal);

Der Zeiger, an _CrtSetReportHook den Sie übergeben, ist vom Typ _CRT_REPORT_HOOK, wie in <crtdbg.h>:

typedef int (__cdecl *_CRT_REPORT_HOOK)(int, char *, int *);

Wenn die Laufzeitbibliothek Ihre Hook-Funktion aufruft, enthält das nRptType Argument die Kategorie des Berichts (_CRT_WARNoder _CRT_ERROR_CRT_ASSERT), szMsg enthält einen Zeiger auf eine vollständig zusammengesetzte Berichtsmeldungszeichenfolge und retVal gibt an, ob _CrtDbgReport die normale Ausführung fortgesetzt werden soll, nachdem der Bericht generiert oder der Debugger gestartet wurde. (Ein retVal Wert von Null setzt die Ausführung fort, ein Wert von 1 startet den Debugger.)

Wenn der Hook die betreffende Nachricht vollständig verarbeitet, sodass keine weitere Berichterstattung erforderlich ist, sollte sie zurückgegeben werden TRUE. Wenn sie zurückgegeben FALSEwird, _CrtDbgReport meldet die Nachricht normal.

In diesem Abschnitt

  • Debugversionen von Heapreservierungsfunktionen

    Beschreibt die speziellen Debugversionen der Heap-Zuordnungsfunktionen, einschließlich: wie die CRT Aufrufe zugeordnet werden, welche Vorteile der Aufrufe explizit auftreten, wie Konvertierung vermieden, die separaten Zuordnungstypen in Clientblöcken nachverfolgt und die Ergebnisse nicht definiert _DEBUGwerden.

  • Details zum CRT-Debugheap

    Beschreibt die Speicherverwaltung und den Debug-Heap, die Typen von Blöcken für den Debug-Heap, heap-Zustandsberichterstattungsfunktionen und die Verwendung des Debug-Heaps zum Nachverfolgen von Zuordnungsanforderungen.

  • Suchen von Speicherlecks mithilfe der CRT-Bibliothek

    Hier werden Verfahren zum Erkennen und Isolieren von Arbeitsspeicherverlusten mithilfe des Debuggers und der C-Laufzeitbibliothek erläutert.

Siehe auch