it-swarm.com.de

Wie debuggen Sie Heap-Korruptionsfehler?

Ich debugge eine (native) Multithread-C++ - Anwendung unter Visual Studio 2008. Bei scheinbar zufälligen Anlässen erhalte ich die Fehlermeldung "Windows hat einen Haltepunkt ausgelöst ...". Dabei wird darauf hingewiesen, dass dies möglicherweise auf eine Beschädigung des Systems zurückzuführen ist Haufen. Diese Fehler führen nicht immer zum Absturz der Anwendung, obwohl sie wahrscheinlich kurz danach abstürzen werden.

Das große Problem bei diesen Fehlern besteht darin, dass sie erst angezeigt werden, nachdem die Korruption tatsächlich stattgefunden hat, was es sehr schwer macht, sie zu verfolgen und zu debuggen, insbesondere bei einer Multithread-Anwendung.

  • Welche Dinge können diese Fehler verursachen?

  • Wie debugge ich sie?

Tipps, Tools, Methoden, Erkenntnisse ... sind willkommen.

153
Beef

Application Verifier in Kombination mit Debugging Tools for Windows ist ein erstaunliches Setup. Sie können beides als Teil des Windows Driver Kit oder des leichteren Windows SDK erhalten. (Informierte mich über Application Verifier bei der Suche nach einem frühere Frage zu einem Heap-Problem mit Korruption .) Ich habe in der Vergangenheit auch BoundsChecker und Insure ++ (in anderen Antworten erwähnt) verwendet, obwohl ich überrascht war, wie viel Funktionalität vorhanden war in der Anwendungsüberprüfung.

Elektrozaun (auch bekannt als "Efence"), dmalloc , valgrind und so weiter sind alle erwähnenswert, aber die meisten von ihnen sind viel einfacher unter * nix als Windows auszuführen. Valgrind ist lächerlich flexibel: Ich habe große Server-Software mit vielen Heap-Problemen getestet.

Wenn alles andere fehlschlägt, können Sie Ihrem eigenen globalen Operator neue/delete- und malloc/calloc/realloc-Überladungen bereitstellen. Die Vorgehensweise hängt von Compiler und Plattform ab. Dies ist jedoch eine Investition - aber es kann sich langfristig auszahlen. Die Liste der wünschenswerten Features sollte aus dmalloc und electricfence bekannt sein, und das überraschend hervorragende Buch Writing Solid Code :

  • Sentry-Werte: Lassen Sie vor und nach jeder Zuweisung etwas mehr Speicherplatz zu, wobei die maximale Ausrichtungsanforderung zu berücksichtigen ist. fülle mit magischen Zahlen (hilft, Pufferüberläufe und Unterläufe sowie gelegentlichen "Wild" -Zeiger zu erfassen)
  • alloc fill: füllt neue Zuordnungen mit einem magischen Nicht-0-Wert - Visual C++ erledigt dies bereits für Sie in Debug-Builds (hilft bei der Verwendung von nicht initialisierten Variablen)
  • free fill: Füllen Sie den freigegebenen Speicher mit einem magischen Nicht-0-Wert, der einen Segfault auslösen soll, wenn er in den meisten Fällen dereferenziert wird (hilft, unklare Zeiger zu finden).
  • delayed free: Geben Sie den freigegebenen Speicher nicht für eine Weile auf den Heap zurück, sondern lassen Sie ihn frei, aber nicht verfügbar. (Ermöglicht das Auffinden von mehr baumelnden Zeigern, das Auffinden von Doppelfreiheitszeichen).
  • tracking: Es kann manchmal nützlich sein, aufzuzeichnen, wo eine Zuordnung vorgenommen wurde

Beachten Sie, dass wir in unserem lokalen Homebrew-System (für ein eingebettetes Ziel) die Nachverfolgung von den meisten anderen Elementen getrennt halten, da der Laufzeit-Overhead viel höher ist.


Wenn Sie an weiteren Gründen für die Überlastung dieser Zuweisungsfunktionen/-operatoren interessiert sind, werfen Sie einen Blick auf meine Antwort auf "Grund zur Überladung des globalen Operators new und delete?" ; Abgesehen von der schamlosen Eigenwerbung werden weitere Techniken aufgelistet, die beim Verfolgen von Heap-Korruptionsfehlern hilfreich sind, sowie andere anwendbare Tools.

122
leander

Sie können viele Heap-Beschädigungsprobleme erkennen, indem Sie Page Heap für Ihre Anwendung aktivieren. Dazu müssen Sie gflags.exe verwenden, das Bestandteil von Debugging-Tools für Windows ist.

Führen Sie Gflags.exe aus und aktivieren Sie in den Image-Dateioptionen für Ihre ausführbare Datei die Option "Seitenheap aktivieren".

Starten Sie nun Ihr Exe neu und verbinden Sie es mit einem Debugger. Wenn Page Heap aktiviert ist, wird die Anwendung bei jeder Heap-Beschädigung in den Debugger eingebrochen.

34
Canopus
13
Vijay

Um die Dinge wirklich zu verlangsamen und eine Vielzahl von Laufzeitüberprüfungen durchzuführen, fügen Sie am Anfang Ihrer main() oder eines entsprechenden Objekts in Microsoft Visual Studio C++ Folgendes hinzu

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );
12
Dave Van Wagner

Ein kurzer Tipp, den ich von Detection Zugang zu freigegebenem Speicher bekam, ist folgender:

Wenn Sie den Fehler suchen möchten schnell, ohne jeden zu überprüfen Anweisung, die auf den Speicher zugreift Block können Sie den Speicherzeiger einstellen auf einen ungültigen Wert nach dem Freigeben von Block:

#ifdef _DEBUG // detect the access to freed memory
#undef free
#define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666;
#endif
8
StackedCrooked

Welche Dinge können diese Fehler verursachen?

Unartige Dinge mit dem Gedächtnis machen, z. Schreiben nach dem Ende eines Puffers oder Schreiben in einen Puffer, nachdem er auf dem Heap wieder freigegeben wurde.

Wie debugge ich sie?

Verwenden Sie ein Instrument, das Ihrer ausführbaren Datei eine automatische Begrenzungsprüfung hinzufügt: d. H. Valgrind unter Unix oder ein Tool wie BoundsChecker (Wikipedia schlägt unter Windows auch Purify und Insure ++ vor).

Beachten Sie, dass diese Anwendungen Ihre Anwendung verlangsamen, sodass sie möglicherweise unbrauchbar sind, wenn Ihre Anwendung eine Echtzeitanwendung ist.

Ein anderes mögliches Debugging-Hilfsprogramm/Tool könnte der HeapAgent von MicroQuill sein.

8
ChrisW

Das beste Werkzeug, das ich für nützlich befunden habe und jedes Mal gearbeitet habe, ist die Codeüberprüfung (mit guten Codeüberprüfern).

Anders als bei der Codeüberprüfung würde ich zuerst Page Heap versuchen. Das Einrichten des Page-Heaps dauert einige Sekunden. Mit etwas Glück kann er Ihr Problem feststellen.

Wenn Sie mit Page Heap kein Glück haben, laden Sie Debugging Tools für Windows von Microsoft herunter und lernen Sie, die WinDbg zu verwenden. Sorry konnte Ihnen keine spezifischere Hilfe geben, aber das Debuggen von Multi-Threaded-Heap-Korruption ist mehr eine Kunst als eine Wissenschaft. Google für "WinDbg-Heap-Korruption" und Sie sollten viele Artikel zu diesem Thema finden. 

5
Shing Yip

Sie können auch prüfen, ob Sie eine Verknüpfung mit der dynamischen oder statischen C-Laufzeitbibliothek herstellen. Wenn Ihre DLL -Dateien mit der statischen C-Laufzeitbibliothek verknüpft werden, haben die DLL -Dateien separate Heap-Speicher.

Wenn Sie also ein Objekt in einer DLL erstellen und versuchen, es in einer anderen DLL freizugeben, erhalten Sie die gleiche Meldung, die Sie oben sehen. Auf dieses Problem wird in einer anderen Stack Overflow-Frage verwiesen, Geben Sie Speicher frei, der in einem anderen DLL zugewiesen wurde.

4
dreadpirateryan

Wenn diese Fehler zufällig auftreten, besteht eine hohe Wahrscheinlichkeit, dass Sie auf Datenrennen stießen. Bitte überprüfen Sie: Ändern Sie Shared Memory-Zeiger von verschiedenen Threads? Intel Thread Checker kann helfen, solche Probleme in Multithread-Programmen zu erkennen.

3

Welche Art von Zuweisungsfunktionen verwenden Sie? Ich habe kürzlich einen ähnlichen Fehler mit den Heap * -Stile-Zuordnungsfunktionen festgestellt. 

Es stellte sich heraus, dass ich versehentlich den Heap mit der Option HEAP_NO_SERIALIZE erstellt habe. Dies führt im Wesentlichen dazu, dass die Heap-Funktionen ohne Threadsicherheit ausgeführt werden. Es ist eine Leistungsverbesserung, wenn es richtig verwendet wird, aber nicht verwendet werden sollte, wenn Sie HeapAlloc in einem Multithread-Programm verwenden [1]. Ich erwähne dies nur, weil in Ihrem Beitrag erwähnt wird, dass Sie eine Multithread-App haben. Wenn Sie HEAP_NO_SERIALIZE irgendwo verwenden, löschen Sie das, und das Problem wird wahrscheinlich behoben. 

[1] In bestimmten Situationen ist dies zulässig, aber Sie müssen Aufrufe von Heap * serialisieren. Dies ist normalerweise bei Multithread-Programmen nicht der Fall. 

3
JaredPar

Erwägen Sie nicht nur nach Werkzeugen, sondern auch nach einem wahrscheinlichen Übeltäter. Gibt es eine Komponente, die Sie verwenden, möglicherweise nicht von Ihnen geschrieben, die möglicherweise nicht für die Ausführung in einer Multithread-Umgebung entwickelt und getestet wurde? Oder einfach eine, die Sie nicht know in einer solchen Umgebung ausgeführt haben.

Das letzte Mal passierte es mir, es war ein natives Paket, das seit Jahren erfolgreich von Batch-Jobs verwendet wurde. Es war jedoch das erste Mal in diesem Unternehmen, dass es von einem .NET-Webdienst (der Multithreading ist) verwendet wurde. Das war es - sie hatten gelogen, dass der Code Thread-sicher war.

1
John Saunders

Sie können VC CRT-Heap-Check-Makros für _ CrtSetDbgFlag : _ CRTDBG_CHECK_ALWAYS_DF verwenden. oder _ CRTDBG_CHECK_EVERY_16_DF .. _ CRTDBG_CHECK_EVERY_1024_DF .

0
KindDragon

Ich hatte ein ähnliches Problem - und es kam ganz zufällig auf. Vielleicht war etwas in den Build-Dateien beschädigt, aber am Ende habe ich das Problem behoben, indem das Projekt zuerst gereinigt und dann neu erstellt wurde.

Also zusätzlich zu den anderen Antworten:

Welche Dinge können diese Fehler verursachen? In der Build-Datei ist etwas beschädigt.

Wie debugge ich sie? Projekt reinigen und umbauen. Wenn es behoben wurde, war dies wahrscheinlich das Problem.

0
Marty

Ich möchte meine Erfahrung hinzufügen. In den letzten Tagen habe ich einen Fehler in meiner Anwendung behoben. In meinem speziellen Fall waren die Fehler im Code:

  • Elemente aus einer STL-Auflistung entfernen, während Sie darüber iterieren (Ich glaube, in Visual Studio gibt es Debug-Flags, um diese Dinge abzufangen; ich habe es während der Codeüberprüfung erwischt)
  • Dieses ist komplexer, ich teile es in Schritten auf:
    • Rufen Sie in einem nativen C++ - Thread den verwalteten Code auf
    • Rufen Sie in verwaltetem Land Control.Invoke auf und entsorgen Sie ein verwaltetes Objekt, das das native Objekt umgibt, zu dem der Rückruf gehört.
    • Da das Objekt innerhalb des nativen Threads noch lebt, bleibt es im Callback-Aufruf bis zum Ende von Control.Invoke gesperrt. Ich sollte klarstellen, dass ich boost::thread verwende, also eine Member-Funktion als Thread-Funktion.
    • Lösung : Benutze stattdessen Control.BeginInvoke (meine GUI wird mit Winforms erstellt), damit der native Thread enden kann, bevor das Objekt zerstört wird (der Zweck des Callbacks ist die genaue Benachrichtigung, dass der Thread beendet wurde und das Objekt zerstört werden kann).
0
dario_ramos