Ereignisse für benutzerdefinierte native ETW-Heaps

Visual Studio enthält eine Vielzahl von profiling and diagnostic tools (Profilerstellungs- und Diagnosetools), einschließlich einer nativen Speicherprofilerstellung. Dieser Profiler hängt sich an ETW-Ereignisse vom Heap-Anbieter, und bietet eine Analyse, wie Speicher zugeordnet und verwendet wird. Dieses Tool kann standardmäßig nur aus dem standardmäßigen Windows-Heap vorgenommene Zuordnungen analysieren. Zuordnungen außerhalb dieses nativen Heap werden nicht angezeigt.

Es gibt viele Fälle, in denen Sie Ihren eigenen benutzerdefinierten Heap verwenden und den Zuordnungsaufwand aus dem Standard-Heap vermeiden möchten. Beispielsweise können Sie VirtualAlloc verwenden, um eine große Menge an Speicher am Anfang der App oder des Spiels zuzuordnen, und anschließend Ihre eigenen Blöcke in dieser Liste zu verwalten. In diesem Szenario würde das Speicherprofilerstellungstool nur diese anfänglichen Zuordnung und nicht die benutzerdefinierte Verwaltung innerhalb des Speicherblocks finden. Jedoch können Sie mithilfe des benutzerdefinierten nativen Heap-ETW-Anbieters dafür sorgen,dass das Tool alle Zuordnungen kennt, die Sie außerhalb des Standard-Heaps vornehmen.

In einem Projekt wie dem Folgenden, in dem MemoryPool ein benutzerdefinierter Heap ist, würden Sie z.B. nur eine einzige Zuordnung auf dem Windows-Heap sehen:

class Foo
{
public:
    int x, y;
};

...

// MemoryPool is a custom managed heap, which allocates 8192 bytes
// on the standard Windows Heap named "Windows NT"
MemoryPool<Foo, 8192> mPool;

// the "allocate" method requests memory from the pool created above
// and is cast to an object of type Foo, shown above
Foo* pFoo1 = (Foo*)mPool.allocate();
Foo* pFoo2 = (Foo*)mPool.allocate();
Foo* pFoo3 = (Foo*)mPool.allocate();

Eine Momentaufnahme aus dem Speicherauslastungstool ohne benutzerdefinierte Heap-Nachverfolgung würde nur einfach die einzelne 8.192 Byte-Zuordnung und keine der benutzerdefinierten Zuordnungen, die vom Pool vorgenommen wurden, anzeigen:

Windows heap allocation

Sie können dieses Tool auch zum Nachverfolgen der Speicherauslastung in Ihrem benutzerdefinierten Heap verwenden, indem Sie die folgenden Schritte ausführen.

Verwendung

Diese Bibliothek kann problemlos in C und C++ verwendet werden.

  1. Fügen Sie den Header für den benutzerdefinierten Heap-ETW-Anbieter ein:

    #include <VSCustomNativeHeapEtwProvider.h>
    
  2. Hinzufügen des __declspec(allocator)-Decorator für alle Funktionen in Ihrem benutzerdefinierten Heapmanager, das einen Zeiger auf den neu zugeordneten Heapspeicher zurückgibt. Dieser Decorator ermöglicht es dem Tool, den Typ des zurückgegebenen Speichers korrekt zu identifizieren. Beispiel:

    __declspec(allocator) void *MyMalloc(size_t size);
    

    Hinweis

    Dieser Decorator informiert den Compiler, dass diese Funktion ein Aufruf an eine Zuweisung ist. Jeder Aufruf der Funktion wird die Adresse der Aufrufsite, die Größe der Aufrufanweisung und die TypeId des neuen Objekts zu einem neuen S_HEAPALLOCSITE-Symbol ausgeben. Wenn eine Aufrufliste zugeordnet ist, wird Windows ein ETW-Ereignis mit diesen Informationen ausgeben. Der Speicherprofilerstellungstool führt die Aufrufliste dazu, eine Rückgabeadresse entsprechend des S_HEAPALLOCSITE-Symbols zu suchen. Die TypeId-Informationen im Symbol wird verwendet, um den Laufzeittyp der Zuordnung anzuzeigen.

    Kurz gesagt bedeutet dies, dass ein Aufruf, der wie (B*)(A*)MyMalloc(sizeof(B)) aussieht, im Tool als B-Typ angezeigt wird, nicht void oder A.

  3. Erstellen Sie für C++ das VSHeapTracker::CHeapTracker-Objekt, das einen Namen für den Heap bereitstellt, der im Profilerstellungstool angezeigt werden wird:

    auto pHeapTracker = std::make_unique<VSHeapTracker::CHeapTracker>("MyCustomHeap");
    

    Wenn Sie C verwenden, verwenden Sie stattdessen.die OpenHeapTracker-Funktion. Diese Funktion gibt ein Handle zurück, das Sie verwenden, wenn Sie andere Nachverfolgungsfunktionen aufrufen:

    VSHeapTrackerHandle hHeapTracker = OpenHeapTracker("MyHeap");
    
  4. Rufen Sie beim Zuweisen von Speicher mithilfe der benutzerdefinierte Funktion die AllocateEvent (C++)- oder VSHeapTrackerAllocateEvent (C)-Methode auf, und übergeben Sie den Zeiger an den Speicher und ihre Größe, um die Zuordnung zu verfolgen:

    pHeapTracker->AllocateEvent(memPtr, size);
    

    oder

    VSHeapTrackerAllocateEvent(hHeapTracker, memPtr, size);
    

    Wichtig

    Vergessen Sie nicht, Ihre benutzerdefinierte Zuweisungsfunktion mit dem weiter oben beschriebenen __declspec(allocator)-Decorator-Element zu markieren.

  5. Rufen Sie beim Freigeben von Speicher mithilfe der benutzerdefinierte Funktion die DeallocateEvent (C++)- oder VSHeapTracerDeallocateEvent (C)-Funktion auf und übergeben Sie den Zeiger an den Speicher, um die Freigabe zu verfolgen:

    pHeapTracker->DeallocateEvent(memPtr);
    

    oder:

    VSHeapTrackerDeallocateEvent(hHeapTracker, memPtr);
    
  6. Rufen Sie beim Neuzuordnen von Speicher mithilfe der benutzerdefinierte Funktion die ReallocateEvent (C++)- oder VSHeapReallocateEvent (C)-Methode auf, und übergeben Sie einen Zeiger an den neuen Speicher, an die Größe der Zuordnung und einen Zeiger an den alten Speicher:

    pHeapTracker->ReallocateEvent(memPtrNew, size, memPtrOld);
    

    oder:

    VSHeapTrackerReallocateEvent(hHeapTracker, memPtrNew, size, memPtrOld);
    
  7. Verwenden Sie schlussendlich den CHeapTracker-Destruktor, entweder manuell oder über standardmäßige Bereichsregeln, oder die CloseHeapTracker-Funktion in C, um die benutzerdefinierte Heap-Nachverfolgung in C++ zu schließen und zu bereinigen:

    delete pHeapTracker;
    

    oder:

    CloseHeapTracker(hHeapTracker);
    

Nachverfolgen der Arbeitsspeicherauslastung

Mit diesen Aufrufen kann Ihr benutzerdefinierter Heapverbrauch jetzt mithilfe des Standard-Speicherauslastungs-Tools in Visual Studio nachverfolgt werden. Weitere Informationen zur Verwendung dieses Tools finden Sie unter der Speicherauslastungs-Dokumentation. Stellen Sie sicher, dass Sie die Heap-Profilerstellung mit Momentaufnahmen aktiviert haben, andernfalls wird Ihr benutzerdefinierter Heapverbrauch nicht angezeigt.

Enable Heap Profiling

Verwenden Sie zum Anzeigen Ihrer benutzerdefinierten Heap-Nachverfolgung den Heap-Dropdown, der sich in der oberen rechten Ecke des Snapshot-Fensters befindet, um die Anzeige von NT-Heap in Ihren eigenen zuvor genannten Heap zu ändern.

Heap Selection

Mithilfe des obigen Codebeispiels, mit MemoryPool zum Erstellen eines VSHeapTracker::CHeapTracker-Objekts, und unserer eigenen allocate-Methode, die nun die AllocateEvent-Methode aufruft, können Sie das Ergebnis der benutzerdefinierten Zuordnung sehen, die drei Instanzen mit insgesamt 24 Bytes zeigt, alle sind vom Typ Foo.

Das Standardheap NT-Heap sieht genauso aus wie vorher, außer dass das CHeapTracker-Objekt hinzugefügt wurde.

NT Heap with Tracker

Wie bei dem standardmäßigen Windows-Heap, können Sie dieses Tool auch verwenden, um Momentaufnahmen zu vergleichen und um nach Verlusten und Beschädigung in Ihrem benutzerdefinierten Heap zu suchen, das in der Hauptdokumentation Speicherauslastung beschrieben wird.

Tipp

Visual Studio enthält auch ein Speicherauslastungstool im Leistungsprofilerstellungs-Toolset, das in der Menüoption Debuggen>Leistungsprofilerstellung oder über die Tastenkombination ALT+F2 aktiviert wird. Diese Funktion enthält keine Heap-Nachverfolgung und wird Ihren benutzerdefinierten Heap nicht wie hier beschrieben anzeigen. Nur das Diagnosetools-Fenster, das im Menü Debuggen>Windows>Diagnosetools anzeigen oder mit der Tastenkombination STRG+ALT+F2 aktiviert werden kann, enthält diese Funktion.