2 Stimmen

C++: Debugging von Speicherlecks im Referenzzählsystem

Ich habe eine Reihe von Referenz gezählt Klassen in einigen meiner Anwendungen, und auch in dlls diese Anwendungen verwenden, alle erben von und implementieren eine IRefCounted-Schnittstelle.

Um bei der Suche nach der Quelle dieser Speicherlecks zu helfen, möchte ich, dass jede Anwendung eine Liste aller vorhandenen referenzierten Klassen führt.

Das Problem ist die Verwaltung von Instanzen dieser Listen, so dass ihre Verwendung keine Auswirkungen auf die Verwendung meiner Klassen hat (z. B. muss ich nicht einen Zeiger auf eine Liste um die ganze Zeit übergeben, stattdessen irgendwie an den Prozess anhängen).

-Es ist gut möglich, dass mehrere dieser Anwendungen gleichzeitig ausgeführt werden und dieselbe DLL verwenden. Jede Anwendung benötigt ihre eigene Objektliste, und alle von dieser Anwendung geladenen Dlls usw. müssen diese Liste verwenden (aber bedenken Sie, dass eine Dll von mehreren Anwendungen geladen werden kann...).
-Die Liste muss nach jeder anderen globalen und statischen Variable in der Anwendung zerstört werden, so dass die Objekte, die in der Liste verbleiben, wenn diese zerstört wird, diejenigen sind, die in Wirklichkeit nicht korrekt freigegeben wurden.

Dann füge ich einfach einen Haltepunkt zum Destruktor der Liste hinzu, so dass ich alle nicht zugewiesenen Objekte im Debugger durchsehen kann.

1voto

Mr Fooz Punkte 102791

Ich nehme an, Sie verwenden COM. Sie müssen wahrscheinlich eine Möglichkeit finden, eine schwacher Zeiger damit die Registrierung von instanziierten Objekten nicht verhindert, dass diese zerstört werden.

Wenn Sie alle Klassen ändern können, könnten Sie ein statisches Mitglied einfügen, um alle Instanzen zu verfolgen und den Destruktor einer Instanz sich selbst aus dem statischen Mitglied entfernen lassen. Sie könnten zum Beispiel eine Basisklasse oder Hilfsklasse wie die folgende verwenden:

class InstanceRegistry {
protected:
    InstanceRegistry() {
       registry.insert(this);
    }
    ~InstanceRegistry() {
       registry.remove(this);
    }
private:
  static SomeContainerType<InstanceRegistry*> registry;
};

Zusätzliche Arbeit wäre nötig, wenn Sie ein anderes Register für verschiedene Arten von Klassen usw. wünschen.

1voto

Daniel Earwicker Punkte 111630

Wenn Prozesse dieselbe DLL verwenden, erhält jeder Prozess seine eigene private Kopie der statischen (oder "globalen") Daten dieser DLL.

Alles, was Sie also tun müssen, ist, die Liste zu einer globalen Variablen in einer DLL zu machen und von jeder Anwendung aus auf diese DLL zu verweisen. Auf diese Weise ist es nicht notwendig, etwas Zusätzliches weiterzugeben.

Ihre Idee, die Zerstörung der Liste zu fangen, ist aufgrund der Unvorhersehbarkeit der Reihenfolge der Zerstörung von Objekten in einem Multi-DLL-Prozess mit Schwierigkeiten behaftet.

Es wäre viel einfacher, den Inhalt der Liste am Ende Ihrer main o WinMain Funktion.

Wenn Sie nicht konsequent eine Smart-Pointer-Klasse verwenden, sollten Sie dies tun. Es kann sich auch lohnen, nach zyklischen Verweisen zu suchen - Objekt A hat einen Verweis auf Objekt B und umgekehrt. Dies ist eine häufige Ursache für nicht freigegebene Objekte.

更新しました。

Um alle statischen Destruktoren zum Ausführen und Freigeben von Objekten zu zwingen, so dass Sie die Einträge in der Liste anschließend untersuchen können, müssen Sie Ihre Anwendung auf eine bestimmte Weise strukturieren.

Nehmen wir an, Sie haben eine sehr minimale EXE, die den Prozess startet und eine Reihe von anderen DLLs lädt, die die eigentliche Arbeit erledigen. Diese anderen DLLs werden mit LoadLibrary geladen, irgendwie (vielleicht durch COM oder ein COM-ähnliches System). Die LoadLibrary-API lädt entweder die DLL in den Prozess oder erhöht einen internen Referenzzähler der DLL, wenn diese bereits geladen ist. Die FreeLibrary-API dekrementiert den Zähler, bis er Null erreicht, und entlädt dann die DLL (zu diesem Zeitpunkt werden die statischen Destruktoren für diese DLL ausgeführt).

Dem fügen wir nun unsere Diagnose-DLL hinzu, die eine Liste aller ausstehenden referenzierten Objekte enthält. Alle anderen DLLs verwenden eine Import-Lib-Verknüpfung mit der Diagnose-DLL, und die EXE verwendet LoadLibrary auch für diese.

Wenn main beendet werden soll, geht die EXE die Liste der DLL-Handles durch, die sie zuvor geladen hat, und ruft FreeLibrary für alle auf. Indem sie die Diagnose-DLL geladen lässt, stellt sie sicher, dass sie am Ende noch vorhanden ist. Das ist zumindest die Theorie.

Aber in welcher Reihenfolge sollten die anderen DLLs entladen werden? Wenn A.DLL statische Zeiger auf Objekte hat, die in B.DLL definiert sind, dann sollten Sie besser zuerst A entladen. Sie müssen also eine Vorstellung davon haben, wie Ihre verschiedenen DLLs eine "geschichtete" Architektur bilden, bei der höhere Schichten von niedrigeren Schichten abhängen, so dass Sie eine sichere Reihenfolge für das Entladen festlegen können.

Sobald Sie alle DLLs entladen haben, zeigen alle Einträge in der Diagnoseliste, die sich auf Objekte in den DLLs beziehen, auf gültige Daten auf dem Heap, aber die vtable zeigt auf Code, der von den DLLs definiert wurde, die jetzt entladen wurden, so dass Sie keine virtuellen Funktionen für diese Objekte aufrufen können. Sie sollten jedoch in der Lage sein, ihre Daten zu untersuchen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X