Eventos de montón ETW nativos personalizados

Visual Studio contiene diversas herramientas de diagnóstico y de generación de perfiles, incluido un generador de perfiles de memoria nativa. Este generador de perfiles enlaza los eventos ETW del proveedor de montón y proporciona un análisis de la manera en que la memoria se asigna y se usa. De forma predeterminada, esta herramienta solo puede analizar las asignaciones realizadas desde el montón de Windows estándar y no se mostrarán las asignaciones que se encuentran fuera de este montón nativo.

Hay muchos casos en los que podría interesarle usar su propio montón personalizado y evitar la sobrecarga de asignación del montón estándar. Por ejemplo, puede usar VirtualAlloc para asignar una gran cantidad de memoria cuando se inicia la aplicación o el juego y, después, administrar sus propios bloques dentro de esa lista. En este escenario, la herramienta de generador de perfiles de memoria solo vería esa asignación inicial y no la administración personalizada realizada dentro del bloque de memoria. En cambio, mediante el uso del proveedor ETW de montón nativo personalizado, puede dejar que la herramienta conozca las asignaciones que realiza fuera del montón estándar.

Por ejemplo, en un proyecto similar al siguiente, donde MemoryPool es un montón personalizado, solo vería una única asignación en el montón de Windows:

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();

En una instantánea de la herramienta Uso de memoria sin el seguimiento del montón personalizado, solo se mostraría la asignación de 8192 bytes y ninguna de las asignaciones personalizadas realizadas por el grupo:

Windows heap allocation

Con los pasos siguientes, podemos usar esta misma herramienta para realizar un seguimiento del uso de memoria en el montón personalizado.

Cómo se usa

Esta biblioteca se puede usar fácilmente en C y C++.

  1. Incluya el encabezado para el proveedor ETW de montón personalizado:

    #include <VSCustomNativeHeapEtwProvider.h>
    
  2. Agregue el elemento Decorator __declspec(allocator) a cualquier función del administrador de montones personalizados que devuelva un puntero a la memoria de montón recién asignada. Este elemento Decorator permite que la herramienta identifique correctamente el tipo de la memoria que se devuelve. Por ejemplo:

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

    Nota

    Este elemento Decorator le indica al compilador que esta función es una llamada a un asignador. Cada llamada a la función dará como resultado la dirección del sitio de llamada, el tamaño de la instrucción de llamada y el identificador de tipo del nuevo objeto en un nuevo símbolo S_HEAPALLOCSITE. Cuando se asigna una pila de llamadas, Windows emite un evento ETW con esta información. La herramienta de generador de perfiles de memoria recorre la pila de llamadas en busca de una dirección de devolución que coincida con un símbolo S_HEAPALLOCSITE, y la información del identificador de tipo del símbolo se usa para mostrar el tipo de tiempo de ejecución de la asignación.

    En resumen, esto significa que una llamada similar a (B*)(A*)MyMalloc(sizeof(B)) se mostrará en la herramienta como si fuera de tipo B, no void o A.

  3. Para C++, cree el objeto VSHeapTracker::CHeapTracker y asígnele un nombre al montón, que se mostrará en la herramienta de generación de perfiles:

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

    Si usa C, use la función OpenHeapTracker. Esta función devuelve un identificador que usará cuando realice una llamada a otras funciones de seguimiento:

    VSHeapTrackerHandle hHeapTracker = OpenHeapTracker("MyHeap");
    
  4. Cuando asigne memoria mediante la función personalizada, llame al método AllocateEvent (C++) o VSHeapTrackerAllocateEvent (C) y pase el puntero a la memoria y su tamaño para realizar un seguimiento de la asignación:

    pHeapTracker->AllocateEvent(memPtr, size);
    

    or

    VSHeapTrackerAllocateEvent(hHeapTracker, memPtr, size);
    

    Importante

    No olvide etiquetar la función de asignador personalizado con el elemento Decorator __declspec(allocator) descrito anteriormente.

  5. Cuando desasigne memoria mediante la función personalizada, llame a la función DeallocateEvent (C++) o VSHeapTracerDeallocateEvent (C) y pase el puntero a la memoria para realizar un seguimiento de la desasignación:

    pHeapTracker->DeallocateEvent(memPtr);
    

    O bien

    VSHeapTrackerDeallocateEvent(hHeapTracker, memPtr);
    
  6. Cuando reasigne memoria mediante la función personalizada, llame al método ReallocateEvent (C++) o VSHeapReallocateEvent (C), pase un puntero a la memoria nueva y al tamaño de la asignación, y pase un puntero a la memoria antigua:

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

    O bien

    VSHeapTrackerReallocateEvent(hHeapTracker, memPtrNew, size, memPtrOld);
    
  7. Por último, para cerrar y limpiar el rastreador de montón personalizado en C++, use el destructor CHeapTracker, ya sea manualmente o a través de reglas de ámbito estándar, o la función CloseHeapTracker en C:

    delete pHeapTracker;
    

    O bien

    CloseHeapTracker(hHeapTracker);
    

Seguimiento de uso de la memoria

Una vez realizadas estas llamadas, se puede realizar un seguimiento del uso del montón personalizado mediante la herramienta estándar Uso de memoria en Visual Studio. Para obtener más información sobre cómo usar esta herramienta, vea la documentación sobre el uso de memoria. Asegúrese de que ha habilitado la generación de perfiles de montón con instantáneas. En caso contrario, no verá el uso del montón personalizado.

Enable Heap Profiling

Para ver el seguimiento del montón personalizado, use la lista desplegable Montón situada en la esquina superior derecha de la ventana Instantánea para cambiar la vista del Montón de NT a su propio montón, con el nombre que le ha asignado anteriormente.

Heap Selection

Mediante el ejemplo de código anterior, en el que MemoryPool crea un objeto VSHeapTracker::CHeapTracker, y nuestro propio método allocate que llama al método AllocateEvent, ahora puede ver el resultado de esa asignación personalizada, que muestra tres instancias con un total de 24 bytes, todas de tipo Foo.

El montón predeterminado Montón de NT tiene el mismo aspecto que antes, pero se le ha agregado el objeto CHeapTracker.

NT Heap with Tracker

Al igual que en el montón de Windows estándar, también puede usar esta herramienta para comparar instantáneas y buscar fugas y daños en el montón personalizado. Esto se describe en la documentación principal sobre el uso de memoria.

Sugerencia

Visual Studio también contiene la herramienta Uso de memoria en el conjunto de herramientas Generación de perfiles de rendimiento, que se habilita en la opción de menú Depurar>Generador de perfiles de rendimiento o mediante la combinación de teclado Alt+F2. Esta característica no incluye el seguimiento del montón y no mostrará el montón personalizado como se describe aquí. Esta funcionalidad solo está incluida en la ventana Herramientas de diagnóstico, que se puede habilitar en el menú Depurar>Windows>Mostrar herramientas de diagnóstico o mediante la combinación de teclado Ctrl+Alt+F2.