カスタム ネイティブ ETW ヒープ イベントCustom Native ETW Heap Events

Visual Studio には、プロファイリングと診断のためのさまざまなツールがあります。その 1 つがネイティブ メモリ プロファイラーです。Visual Studio contains a variety of profiling and diagnostic tools, including a native memory profiler. このプロファイラーはヒープ プロバイダーから ETW イベントをフックし、メモリの割り当て状況と使用状況を分析します。This profiler hooks ETW events from the heap provider and provides analysis of how memory is being allocated and used. 既定では、このツールは、標準の Windows ヒープから行われた割り当てのみを分析できます。このネイティブ ヒープ外の割り当ては表示されません。By default, this tool can only analyze allocations made from the standard Windows heap, and any allocations outside this native heap would not be displayed.

カスタム ヒープを使用し、標準ヒープの割り当てオーバーヘッドを回避する方法を使用したい場合があります。There are many cases in which you may want to use your own custom heap and avoid the allocation overhead from the standard heap. たとえば、VirtualAlloc を使用し、アプリまたはゲームの開始時に大量のメモリを割り当て、そのリスト内で独自のブロックを管理できます。For instance, you could use VirtualAlloc to allocate a large amount of memory at the start of the app or game, and then manage your own blocks within that list. このシナリオでは、メモリ プロファイラー ツールは最初の割り当てのみを認識し、メモリ チャンク内で行われたカスタム管理は認識されません。In this scenario, the memory profiler tool would only see that initial allocation, and not your custom management done inside the memory chunk. ただし、カスタム ネイティブ ヒープの ETW プロバイダーを使用すると、標準ヒープ外で行うあらゆる割り当てをこのツールに認識させることができます。However, using the Custom Native Heap ETW Provider, you can let the tool know about any allocations you are making outside the standard heap.

たとえば、MemoryPool がカスタム ヒープである次のようなプロジェクトでは、Windows ヒープに割り当てが 1 つだけ表示されます。For example, in a project like the following where MemoryPool is a custom heap, you would only see a single allocation on the Windows heap:

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

カスタム ヒープの追跡がないメモリ使用量ツールからのスナップショットには、8192 バイトの割り当てが 1 つだけ表示され、プールにより行われたカスタム割り当ては何も表示されません。A snapshot from the Memory Usage tool without custom heap tracking would show just the single 8192 byte allocation, and none of the custom allocations being made by the pool:

Windows ヒープ割り当て

次の手順を実行することで、この同じツールを使用し、カスタム ヒープのメモリ使用量を追跡できます。By performing the following steps, we can use this same tool to track memory usgae in our custom heap.

使い方How to Use

このライブラリは C と C++ で簡単に使用できます。This library can be easily used in C and C++.

  1. カスタム ヒープ ETW プロバイダーのヘッダーを追加します。Include the header for the custom heap ETW provider:

    #include <VSCustomNativeHeapEtwProvider.h>
    
  2. 新しく割り当てられたヒープ メモリにポインターを返すカスタム ヒープ マネージャーの関数に __declspec(allocator) デコレータを追加します。Add the __declspec(allocator) decorator to any function in your custom heap manager that returns a pointer to newly allocated heap memory. このデコレータにより、返されるメモリの種類をツールが正確に識別できます。This decorator allows the tool to correctly identify the type of the memory being returned. 例:For example:

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

    注意

    このデコレータは、この関数がアロケーターの呼び出しであることをコンパイラに通知します。This decorator will tell the compiler that this function is a call to an allocator. 関数が呼び出されるたびに、呼び出しサイトのアドレス、呼び出し指示のサイズ、新しいオブジェクトのタイプ ID が新しい S_HEAPALLOCSITE 記号に出力されます。Each call to the function will output the address of the callsite, the size of the call instruction, and the typeId of the new object to a new S_HEAPALLOCSITE symbol. 呼び出し履歴が割り当てられると、Windows はこの情報で ETW イベントを発行します。When a callstack is allocated, Windows will emit an ETW event with this information. メモリ プロファイラー ツールは呼び出し履歴で S_HEAPALLOCSITE 記号に一致するリターン アドレスを探します。この記号のタイプ ID 情報を利用し、割り当てのランタイム タイプが表示されます。The memory profiler tool walks the callstack looking for a return address matching an S_HEAPALLOCSITE symbol, and the typeId information in the symbol is used to display the runtime type of the allocation.

    つまり、(B*)(A*)MyMalloc(sizeof(B)) のように見える呼び出しは、voidA ではなく、タイプ B としてツールに表示されます。In short, this means a call that looks like (B*)(A*)MyMalloc(sizeof(B)) will show up in the tool as being of type B, not void or A.

  3. C++ の場合、ヒープの名前を指定して VSHeapTracker::CHeapTracker オブジェクトを作成します。この名前はプロファイリング ツールに表示されます。For C++, create the VSHeapTracker::CHeapTracker object, providing a name for the heap, which will show up in the profiling tool:

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

    C を使用している場合、代わりに OpenHeapTracker 関数を使用します。If you are using C, use the OpenHeapTracker function instead. この関数は、他の追跡関数を呼び出すときに使用するハンドルを返します。This function will return a handle that you will use when calling other tracking functions:

    VSHeapTrackerHandle hHeapTracker = OpenHeapTracker("MyHeap");
    
  4. カスタム関数を利用してメモリを割り当てるとき、メモリのポインターと割り当てサイズを渡して AllocateEvent (C++) または VSHeapTrackerAllocateEvent (C) メソッドを呼び出し、割り当てを追跡記録します。When allocating memory using your custom function, call the AllocateEvent (C++) or VSHeapTrackerAllocateEvent (C) method, passing in the pointer to the memory and its size, to track the allocation:

    pHeapTracker->AllocateEvent(memPtr, size);
    

    またはor

    VSHeapTrackerAllocateEvent(hHeapTracker, memPtr, size);
    

    重要

    先に説明した __declspec(allocator) デコレータを利用し、カスタム アロケーター関数にタグを付けることを忘れないでください。Don't forget to tag your custom allocator function with the __declspec(allocator) decorator described earlier.

  5. カスタム関数を利用してメモリを解放するとき、メモリのポインターを渡して DeallocateEvent (C++) または VSHeapTracerDeallocateEvent (C) 関数を呼び出し、割り当て解除を追跡記録します。When deallocating memory using your custom function, call the DeallocateEvent (C++) or VSHeapTracerDeallocateEvent (C) function, passing in the pointer to the memory, to track the deallocation:

    pHeapTracker->DeallocateEvent(memPtr);
    

    またはor:

    VSHeapTrackerDeallocateEvent(hHeapTracker, memPtr);
    
  6. カスタム関数を利用してメモリを再割り当てするとき、新しいメモリのポインター、割り当てのサイズ、古いメモリのポインターを渡して ReallocateEvent (C++) または VSHeapReallocateEvent (C) メソッドを呼び出します。When reallocating memory using your custom function, call the ReallocateEvent (C++) or VSHeapReallocateEvent (C) method, passing in a pointer to the new memory, the size of the allocation, and a pointer to the old memory:

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

    またはor:

    VSHeapTrackerReallocateEvent(hHeapTracker, memPtrNew, size, memPtrOld);
    
  7. 最後になりますが、C++ でカスタム ヒープ トラッカーを閉じ、クリーンアップするには、CHeapTracker デストラクターを使用します。その場合、手動で行うか、標準のスコープ規則または C の CloseHeapTracker 関数を利用します。Finally, to close and clean up the custom heap tracker in C++, use the CHeapTracker destructor, either manually or via standard scoping rules, or the CloseHeapTracker function in C:

    delete pHeapTracker;
    

    またはor:

    CloseHeapTracker(hHeapTracker);
    

メモリ使用量を追跡記録するTracking Memory Usage

呼び出しが所定の場所にあるので、Visual Studio の標準メモリ使用量ツールを利用し、カスタム ヒープ使用量を追跡できます。With these calls in place, your custom heap usage can now be tracked using the standard Memory Usage tool in Visual Studio. このツールの使用方法については、メモリ使用量に関する文書を参照してください。For more information on how to use this tool, please see the Memory Usage documentation. スナップショットによるヒープ プロファイリングを有効にしてください。有効にしない場合、カスタム ヒープ使用量が表示されません。Ensure you have enabled heap profiling with snapshots, otherwise you will not see your custom heap usage displayed.

ヒープ プロファイリングを有効にする

カスタム ヒープ追跡を表示するには、[スナップショット] ウィンドウの右上隅にある [ヒープ] ドロップダウンを利用し、NT ヒープから先に名前を付けた独自のヒープに変更します。To view your custom heap tracking, use the Heap dropdown located at the upper-right corner of the Snapshot window to change the view from NT Heap to your own heap as named previously.

ヒープ選択

MemoryPoolVSHeapTracker::CHeapTracker オブジェクトを作成し、独自の allocate メソッドが AllocateEvent メソッドを呼び出す上記のコード サンプルで、そのカスタム割り当ての結果が表示されます。3 つのインスタンスで合計 24 バイトです。種類はすべて Foo です。Using the code example above, with MemoryPool creating a VSHeapTracker::CHeapTracker object, and our own allocate method now calling the AllocateEvent method, you can now see the result of that custom allocation, showing 3 instances totaling 24 bytes, all of type Foo.

既定の NT ヒープは前と同じに見えますが、CHeapTracker オブジェクトが追加されています。The default NT Heap heap looks the same as earlier, with the addition of our CHeapTracker object.

NT ヒープとトラッカー

標準 Windows ヒープと同様に、このツールを利用してスナップショットを比較し、カスタム ヒープのリークや破損を探すこともできます。詳しくは、メモリ使用量に関する文書を参照してください。As with the standard Windows heap, you can also use this tool to compare snapshots and look for leaks and corruption in your custom heap, which is described in the main Memory Usage documentation.

ヒント

Visual Studio のパフォーマンス プロファイリング ツールセットにもメモリ使用量ツールがあります。[デバッグ]、[パフォーマンス プロファイラー] の順に選択するか、キーボード ショートカットの Alt + F2 を押してください。Visual Studio also contains a Memory Usage tool in the Performance Profiling toolset, which is enabled from the Debug > Performance Profiler menu option, or the Alt+F2 keyboard combination. この機能にはヒープ追跡がありません。ここの説明のようにカスタム ヒープが表示されることはありません。This feature does not include heap tracking and will not display your custom heap as described here. この機能があるのは [診断ツール] ウィンドウだけです。[デバッグ]、[Windows]、[診断ツールの表示] の順に選択するか、キーボード ショートカットの Ctrl+Alt+F2 を押してください。Only the Diagnostic Tools window, which can be enabled with the Debug > Windows > Show Diagnostic Tools menu, or the Ctrl+Alt+F2 keyboard combination, contains this functionality.

参照See Also

プロファイリング ツールProfiling Tools
メモリ使用量Memory Usage