Nachverfolgen von Heapreservierungsanforderungen

Aktualisiert: November 2007

Dieses Thema gilt für folgende Anwendungsbereiche:

Edition

Visual Basic

C#

C++

Web Developer

Express

Nur "Systemeigen"

Standard

Nur "Systemeigen"

Pro und Team

Nur "Systemeigen"

Tabellenlegende:

Vorhanden

Nicht vorhanden

Befehl oder Befehle, die standardmäßig ausgeblendet sind.

Die Angabe des Quelldateinamens und der Zeilennummer, in der eine Assertion oder ein Berichtsmakro ausgeführt wird, ist häufig sehr nützlich, um die Fehlerursache festzustellen. Auf Heapreservierungsfunktionen trifft dies in der Regel jedoch nicht zu. Makros können an vielen geeigneten Stellen in der logischen Struktur einer Anwendung eingefügt werden, während eine Reservierung meist in einer speziellen Routine verborgen wird, die zu unterschiedlichen Zeitpunkten von vielen verschiedenen Stellen aus aufgerufen wird. Die Frage ist in der Regel nicht, welche Codezeile eine Fehlreservierung verursacht, sondern welche der vielen tausend Reservierungen durch diese Codezeile fehlerhaft waren und warum.

Eindeutige Reservierungsanforderungsnummern und "_crtBreakAlloc"

Der gesuchte falsche Heapreservierungsaufruf lässt sich am besten feststellen, wenn man die eindeutige Reservierungsanforderungsnummer jedes Blocks im Debugheap berücksichtigt. Wenn Blockinformationen über eine der Dumpfunktionen ausgegeben werden, wird die Reservierungsanforderungsnummer in geschweiften Klammern angezeigt (z. B. "{36}").

Wenn Sie die Reservierungsanforderungsnummer eines falsch reservierten Blocks kennen, können Sie diese Nummer an _CrtSetBreakAlloc übergeben, um einen Haltepunkt zu erstellen. Die Ausführung wird dann unmittelbar vor der Reservierung des betreffenden Blocks unterbrochen, und Sie können zurückverfolgen, welche Routine für den falschen Aufruf verantwortlich ist. Um eine erneute Kompilierung zu vermeiden, können Sie im Debugger genauso verfahren, indem Sie _crtBreakAlloc auf die gewünschte Reservierungsanforderungsnummer festlegen.

Erstellen von Debugversionen von Reservierungsroutinen

Etwas komplizierter ist ein anderes Verfahren, bei dem Sie Debugversionen eigener Reservierungsroutinen erstellen. Diese sind vergleichbar mit den _dbg-Versionen der Heapreservierungsfunktionen. Sie können dann den Quelldateinamen und die Zeilennummer als Argumente an die zugrunde liegenden Heapreservierungsroutinen übergeben und sofort den Ursprung einer Fehlreservierung feststellen.

Angenommen, die Anwendung enthält eine häufig verwendete Routine, die etwa wie folgt aussieht:

int addNewRecord(struct RecStruct * prevRecord,
                 int recType, int recAccess)
{
    // ...code omitted through actual allocation... 
    if ((newRec = malloc(recSize)) == NULL)
    // ... rest of routine omitted too ... 
}

In einer Headerdatei können Sie dann beispielsweise folgenden Code hinzufügen:

#ifdef _DEBUG
#define  addNewRecord(p, t, a) \
            addNewRecord(p, t, a, __FILE__, __LINE__)
#endif

Als nächstes ändern Sie die Reservierung in der Routine zum Erstellen von Datensätzen wie folgt:

int addNewRecord(struct RecStruct *prevRecord,
                int recType, int recAccess
#ifdef _DEBUG
               , const char *srcFile, int srcLine
#endif
    )
{
    /* ... code omitted through actual allocation ... */
    if ((newRec = _malloc_dbg(recSize, _NORMAL_BLOCK,
            srcFile, scrLine)) == NULL)
    /* ... rest of routine omitted too ... */
}

Nun werden der Quelldateiname und die Zeilennummer, in der addNewRecord aufgerufen wurde, in jedem dadurch reservierten Block im Debugheap gespeichert und beim Überprüfen des Blocks ausgegeben.

Siehe auch

Weitere Ressourcen

Der CRT-Debugheap