C++のメモリリーク検出!Visual Studio系

プログラミングでメモリリークを作りこむと検出するのが厄介です。今回は、マイクロソフト謹製のツールを使って、Visual Studio C++における、動的解析でのCRT系とWin32API系のメモリリークの検知方法について紹介します。

スポンサーリンク
スポンサーリンク

サンプルコード

以下のサンプルコードを使用します。CRT系のmallocとWin32APIでのHeapAllocを、後始末なく終了しています。Sleepはデータ採取用に仕掛けたもので、結果に影響はしません。実行ファイル名は、test_leak.exeとしています。

#define _CRTDBG_MAP_ALLOC
#include <cstdlib>
#include <crtdbg.h>

#include <iostream>
#include <windows.h>

void func1(void)
{
    char* p = (char*)malloc(10000);
}

void func2(void)
{
    char* p = (char*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, 10000);
}

int main()
{
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    std::cout << "Hello World!\n";
    ::Sleep(20 * 1000);

    func1();
    func2();

    std::cout << "Allocated\n";
    ::Sleep(20 * 1000);

    std::cout << "End process\n";

    _CrtDumpMemoryLeaks();
}

CRT系の検出

コードについて

まず、CRT系のリークを検出します。コードの下記部分がポイントです。これらの命令は、Debug版のみ作用します。

#define _CRTDBG_MAP_ALLOC
#include <cstdlib>
#include <crtdbg.h>
...
int main()
{
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

...
    _CrtDumpMemoryLeaks();
}

シンボルファイルのダウンロード

後々のために、シンボルファイルをダウンロードするように設定しておきます。[ツール] > [オプション] > [デバッグ] > [シンボル]から、Microsoft シンボルサーバーにチェックし、格納フォルダを指定します。

実行結果

上記コードをVisual Studio C++でデバッガ実行した時の結果を示します。プロセス終了後に下記レポートが出力されており、malloc文でメモリリークしていることを教えてくれます。

Detected memory leaks!
Dumping objects ->
%USERPROFILE%\source\repos\test_leak\test_leak\test_leak.cpp(12) : {160} normal block at 0x0000014A40704C70, 10000 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

ただし、HeapAllocの方は教えてくれません。

Win32系の検出

次に、Windows SDKとDebug Diagnostic Toolを導入して、HeapAllocのリークを検出してみます。

gflags.exeの設定

まず、Windows10用Windows SDKから”Debugging Tools for Windows”をインストールします。

次に下記フォルダにある、C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\gflags.exeを、管理者権限で起動します。”image”欄にファイル名(test_leak.exe)を入力して、TABキーをクリックすると、下の項目が選択可能になります。”Create user mode stack trace database”他必要そうな項目にチェックを入れて、[適用]ボタンをクリックします。

gflagsの設定を解除する場合は、すべてのチェックをOFFにした後、[適用]ボタンをクリックしてください。また、gflagは設定により、アプリ及びOSのパフォーマンスに大きな影響を与える場合があるので注意して使用してください。

gflags.exeの詳細は、GFlags Detailsを参照してください。

Debug Diagnostic Toolによるデータ収集

まず、Debug Diagnostic Toolをインストールします。

次に、Windowsメニューから、”Debug Diag 2 Collection”ツールを起動します。[Tools] > [Option and Settins]、で”Symbol Search Path”をテストモジュールpdbファイル格納フォルダ(%USERPROFILE%\source\repos\test_leak\x64\Debug)を設定しておきます。

テスト対象モジュール(test_leak.exe)を起動したら、[Add Rule]から”Native(Non-.NET) Memory Leak and Handle Leak”を選択し、プロセスを選択します。他の項目はそのまま進みます。すると、一覧に”Leak Rule”と”Crash Rule”が表示されます。

対象プロセスがメモリ確保処理を実施し、プロセスが終了するまでに、一覧を右クリックして、[Dump Targe Process] > [Create User Full Dump]をクリックします。

Debug Diagnostic Toolによる解析結果

次に、Windowsメニューから、”Debug Diag 2 Analysis”ツールを起動します。[歯車メニュー] > [Symbol search path]欄に下記フォルダを登録します。

  • シンボルサーバーフォルダ(ex. d:symbol)
  • 対象シンボルファイルフォルダ(ex. %USERPROFILE%\source\repos\test_leak\x64\Debug)

“Data Files”欄に、データ取集したdmpファイルをC:\Program Files\DebugDiag\Logs\フォルダから登録します。”Analysis Rules”欄で”MemoryAnalysis”にチェックを入れて[Start Analysis]をクリックします。

解析が終了すると、ブラウザが起動し結果が表示されます。中から、自分が作成したファイル名を探すと、HeapAllocの場所を指摘されています。

まとめ

こういったデバッグ系のツールには得手不得手があるので、複数のツールを組み合わせて使用すると効果的です。また、gflagsはWinDBGなどと組み合わせて、Debug DiagはIIS等のメモリリーク以外の調査に活躍します。多段チェックでバグの少ないプログラムに改善していきましょう。

以上、Visual Studio C++のメモリリーク検出方法手順の紹介でした。

スポンサーリンク
スポンサーリンク
スポンサーリンク
開発
Tech WalkIt

コメント