VS Tipps & Tricks: Heap Bugs finden (Teil 1)

Probleme finden, die mit dem Heap zusammenhängen ist oft genug eine Sache für sich und für Anfänger nicht selten ein Buch mit sieben Siegeln. Die CRT und der Debugger stellen aber einige Werkzeuge zur Verfügung, die es einem doch mit etwas Geschick und Wissen erlauben auch komplexere versteckte Fehler zu finden, die Heapfehler auslösen.

Jeder C/C++ Entwickler hat schon Meldungen dieser Art beim Testen seiner Programme gesehen:

HEAP[CrashTest.exe]: Heap block at 006D7920 modified at 006D79B0 past requested size of 88
Windows has triggered a breakpoint in CrashTest.exe.
This may be due to a corruption of the heap, which indicates a bug in CrashTest.exe or any of the DLLs it has loaded. 

oder etwas in dieser Art:

Debug Error!
Program: …nts\Visual Studio 2008\Projects\CrashTest\Debug\CrashTest.exe
HEAP CORRUPTION DETECTED: after Normal block (#110) at 0x000D7948.
CRT detected that the application wrote to memory after end of heap buffer.

Ursache ist ein Fehler wie in diesem kleinen Beispielprogramm:

#include <windows.h>
#include <tchar.h>
#include <crtdbg.h>
int _tmain(int argc, _TCHAR* argv[]) 
{ 
  char *pCorrupt = new char[100];
  ZeroMemory(pCorrupt,106); // -- This will corrupt the heap 
  char *pOther = new char[100]; 
  ZeroMemory(pOther,100); 
  delete [] pOther; 
  delete [] pCorrupt; 
  return 0; 
}

Wenn die Debug-CRT benutzt wird erhält man automatisch einen Break im Debugger wenn der Speicherblock pCorrupt freigeben wird (Zeile 11). Man braucht also nur den Call-Stack oder Stacktrace anzusehen und kann zumindest feststellen, welcher Block defekt ist.

Schwieriger wird es dann schon die Stelle zu finden, an der der Block überschrieben wird. In meinem Beispiel also die Zeile 7. Besonders dann wird es komplex, wenn das Programm größer ist, und der Speicherblock an evtl. sehr vielen Stellen genutzt wird.

Um die Position einzugrenzen und nicht evtl. bis zum Programmende warten zu müssen, wenn man (hoffentlich) alle Objekte freigibt kann man die CRT veranlassen den Heap zu prüfen. Dies geschieht mit _CrtCheckMemory oder AfxCheckMemory.
Streut man also in seinem Code an strategisch guten Stellen das folgende Statement in seinen Code

ASSERT(AfxCheckMemory()); // oder _CrtCheckMemory

kann man relativ gut die Stelle einkreisen die den Fehler verursacht, und das ohne große Performanceverluste. Man erhält sofort einen ASSERT, ab dem Moment ab dem die Integrität des Heaps zerstört wurde und der Check durchgeführt wird.

Noch etwas einfacher ist es, die CRT dazu zu bringen sich sofort zu melden wenn der Heap zerstört wird. Dies kann man erreichen indem man das Debug-Flag _CRTDBG_CHECK_ALWAYS_DF setzt.

Platziert man bei Programmstart die folgende Codezeile in seinem Programm

_CrtSetDbgFlag(_CrtSetDbgFlag(0)|_CRTDBG_CHECK_ALWAYS_DF);

dann unterbricht die CRT das Programm sofort bei der nächsten Allokation eines Speicherbocks, nachdem der Heap zerstört wurde. In meinem Beispiel also direkt bei der nächsten Allokation in Zeile 8!
Nachteil ist, dass bei jeder Allokation der Heap geprüft wird und damit die Performance schon in den Keller gehen kann, wenn das Programm groß ist und der Fehler evtl. selten auftritt.

Man kann also mit den einfachen Bordmitteln der CRT einen Fehler schon relativ leicht eingrenzen.

Soweit für heute. Was man noch alles machen kann um effektiv Heap-Fehler zu finden werde ich demnächst noch in weiteren Artikeln zu diesem Thema beschreiben.

7 Gedanken zu „VS Tipps & Tricks: Heap Bugs finden (Teil 1)“

  1. Hallo Martin,

    sehr interessanter Beitrag. Weil solche Fehler zum Teil erst beim Beenden des Programms in den Destruktoren auftreten, aber die Ursache zeitlich erheblich davor liegen kann, sind sie sehr undankbar. Benutzt du in den Debug Versionen _CrtSetDbgFlag oder AfxCheckMemory() ?

  2. Gehört zu den nächsten Tipps, die ich geben werde, aberhier vorab:
    Ich habe immer in meinem Mainframe einen Timer laufen (200msec), der ASSERT(AfxCheckMemory()); aufruft. Ich habe also sofort einen ASSERT wenn was schief geht.

    Der Impact von _CrtSetDbgFlag(_CrtSetDbgFlag(0)|_CRTDBG_CHECK_ALWAYS_DF); ist mir grundsätzlich zu hoch. Das aktiviere ich nur für die gezielte Suche, wenn ich weiß ich habe ein Problem!

    HTH

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

I accept that my given data and my IP address is sent to a server in the USA only for the purpose of spam prevention through the Akismet program.More information on Akismet and GDPR.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.