Compartilhar via


Eventos de heap de ETW nativos personalizados

O Visual Studio contém uma variedade de ferramentas de criação de perfil e diagnóstico, incluindo um criador de perfil de memória nativa. Esse criador de perfil vincula eventos ETW do provedor de heap e fornece uma análise de como a memória está sendo alocada e utilizada. Por padrão, essa ferramenta só pode analisar alocações feitas do heap padrão do Windows e todas as alocações fora desse heap nativo não serão exibidas.

Há vários casos em que você pode desejar usar seu próprio heap personalizado e evitar a sobrecarga de alocação do heap padrão. Por exemplo, você poderá usar VirtualAlloc para alocar uma grande quantidade de memória no início do aplicativo ou do jogo e depois gerenciar seus próprios blocos nessa lista. Nesse cenário, a ferramenta de criador de perfil de memória só verá essa alocação inicial e não o gerenciamento personalizado feito na parte de memória. No entanto, ao usar o Provedor ETW de Heap Nativo Personalizado, é possível informar a ferramenta sobre as alocações que estão sendo feitas fora do heap padrão.

Por exemplo, em um projeto como o seguinte, em que MemoryPool é um heap personalizado, você verá apenas uma única alocação no heap do 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();

Um instantâneo da ferramenta Uso de Memória sem o acompanhamento de heap personalizado mostrará apenas a única alocação de byte 8192 e nenhuma das alocações personalizadas feitas pelo pool:

Windows heap allocation

Ao realizar as etapas a seguir, podemos usar essa mesma ferramenta para acompanhar o uso de memória em nosso heap personalizado.

Como usar

Essa biblioteca pode ser usada no C e C++ com facilidade.

  1. Inclua o cabeçalho do provedor ETW de heap personalizado:

    #include <VSCustomNativeHeapEtwProvider.h>
    
  2. Adicione o decorador __declspec(allocator) a qualquer função no gerenciador de heap personalizado que retorna um ponteiro para a memória de heap recém-alocada. Esse decorador permite que a ferramenta identifique corretamente o tipo da memória que está sendo retornado. Por exemplo:

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

    Observação

    Esse decorador informará o compilador que essa função é uma chamada a um alocador. Cada chamada à função gerará o endereço do site da chamada, o tamanho da instrução de chamada e a typeId do novo objeto para um novo símbolo S_HEAPALLOCSITE. Quando uma pilha de chamadas for alocada, o Windows emitirá um evento ETW com essas informações. A ferramenta de criador de perfil de memória percorre a pilha de chamadas em busca de uma correspondência de endereço de retorno com um símbolo S_HEAPALLOCSITE e as informações de typeId no símbolo são usadas para exibir o tipo de runtime da alocação.

    Em resumo, isso significa que uma chamada parecida com (B*)(A*)MyMalloc(sizeof(B)) será mostrada na ferramenta como sendo do tipo B, não void nem A.

  3. Para o C++, crie o objeto VSHeapTracker::CHeapTracker, fornecendo um nome para o heap, que será mostrado na ferramenta de criação de perfil:

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

    Se você estiver usando o C, use a função OpenHeapTracker. Essa função retornará um identificador que será usado ao chamar outras funções de acompanhamento:

    VSHeapTrackerHandle hHeapTracker = OpenHeapTracker("MyHeap");
    
  4. Ao alocar a memória usando a função personalizada, chame o método AllocateEvent (C++) ou VSHeapTrackerAllocateEvent (C), passando o ponteiro para a memória e seu tamanho, para acompanhar a alocação:

    pHeapTracker->AllocateEvent(memPtr, size);
    

    ou

    VSHeapTrackerAllocateEvent(hHeapTracker, memPtr, size);
    

    Importante

    Não se esqueça de marcar a função de alocador personalizada com o decorador __declspec(allocator) descrito anteriormente.

  5. Ao desalocar a memória usando a função personalizada, chame a função DeallocateEvent (C++) ou VSHeapTracerDeallocateEvent (C), passando o ponteiro para a memória, para acompanhar a desalocação:

    pHeapTracker->DeallocateEvent(memPtr);
    

    ou:

    VSHeapTrackerDeallocateEvent(hHeapTracker, memPtr);
    
  6. Ao realocar a memória usando a função personalizada, chame o método ReallocateEvent (C++) ou VSHeapReallocateEvent (C), passando um ponteiro para a nova memória, o tamanho da alocação e um ponteiro para a memória antiga:

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

    ou:

    VSHeapTrackerReallocateEvent(hHeapTracker, memPtrNew, size, memPtrOld);
    
  7. Por fim, para fechar e limpar o controlador de heap personalizado no C++, use o destruidor CHeapTracker, manualmente ou por meio de regras de escopo padrão ou a função CloseHeapTracker no C:

    delete pHeapTracker;
    

    ou:

    CloseHeapTracker(hHeapTracker);
    

Acompanhar o uso de memória

Com essas chamadas implementadas, o uso de heap personalizado agora pode ser acompanhado usando a ferramenta padrão Uso de Memória no Visual Studio. Para obter mais informações sobre como usar essa ferramenta, consulte a documentação Uso de memória. Verifique se você habilitou a de criação de perfil de heap com instantâneos; caso contrário, você não verá o uso de heap personalizado exibido.

Enable Heap Profiling

Para exibir o acompanhamento de heap personalizado, use o menu suspenso Heap localizado no canto superior direito da janela Instantâneo para alterar a exibição de Heap NT para seu próprio heap nomeado anteriormente.

Heap Selection

Usando o exemplo de código acima, com MemoryPool criando um objeto VSHeapTracker::CHeapTracker e nosso próprio método allocate agora chamando o método AllocateEvent, agora é possível ver o resultado da alocação personalizada, mostrando três instâncias que totalizam 24 bytes, todos do tipo Foo.

O heap padrão Heap NT tem a mesma aparência que anteriormente, com a adição de nosso objeto CHeapTracker.

NT Heap with Tracker

Assim como ocorre com o heap padrão do Windows, também é possível usar essa ferramenta para comparar instantâneos e procurar perdas e danos no heap personalizado, que é descrito na documentação principal Uso de memória.

Dica

O Visual Studio também contém uma ferramenta Uso de Memória no conjunto de ferramentas Criação de Perfil de Desempenho, que é habilitada na opção de menu Depurar>Criador de Perfil de Desempenho ou na combinação de teclas Alt+F2. Esse recurso não inclui o acompanhamento de heap e não exibirá o heap personalizado descrito aqui. Somente a janela Ferramentas de Diagnóstico, que pode ser habilitada com o menu Depurar>Windows>Mostrar Ferramentas de Diagnóstico ou a combinação de teclas Ctrl+Alt+F2, contém essa funcionalidade.