VC++ でメモリリークを検出する方法として、これまで _CrtDumpMemoryLeaks 関数を使っていた。 VC++ メモリリークの検出

ただ、これだと OpenCV や Qt といったライブラリ側でのどうしようもないメモリリークまで拾ってしまい、非常に困っていた。

そこで別の方法としてVisual Leak Detectorを試してみる。

以下よりダウンロードして、インストールする。
Visual Leak Detector for Visual C++ 2008-2015

VC++ プロジェクトのプロパティから以下を設定する。
[C/C++] -[全般] - [追加のインクルードディレクトリ]に以下を追加。
  C:\Program Files (x86)\Visual Leak Detector\include
[リンカー] - [全般] - [追加のライブラリディレクトリ]に以下を追加。
  ビルド構成が Win32 の場合
    C:\Program Files (x86)\Visual Leak Detector\lib\Win32
  ビルド構成が x64 の場合
    C:\Program Files (x86)\Visual Leak Detector\lib\Win64
[リンカー] - [入力] - [追加の依存ファイル]に以下を追加。
  vld.lib

リンクは、#pragma comment(lib, xxxx) の方法ではビルドができなかった。

最後に、プログラムのいずれかの cpp ファイル、たとえば main.cpp などに以下を記載する。
#include <vld.h>

あとはビルドして実行するだけ。
終了時、出力ウィンドウにメモリリークの状況が出力される。
OpenCV や Qt といったライブラリ側でのメモリリークは検知しなかった。モノによってはするかもしれないが。

メモリリークがないときはこのように出力される。
No memory leaks detected.
Visual Leak Detector is now exiting.

メモリリークがあるときはこう。
WARNING: Visual Leak Detector detected memory leaks!
---------- Block 281 at 0x088800B0: 320 bytes ----------
  Leak Hash: 0x46FC2A4A, Count: 1, Total 320 bytes
  Call Stack (TID 6024):
    MSVCR120D.dll!operator new()
    c:\work\src\movieeditor\movieeditor\movieeditor\editwindow.cpp (42): MovieEditor.exe!EditWindow::EditWindow() + 0xA bytes
    c:\work\src\movieeditor\movieeditor\movieeditor\mainwindow.cpp (1032): MovieEditor.exe!MainWindow::editButton_clicked() + 0x27 bytes
    c:\work\src\movieeditor\movieeditor\movieeditor\generatedfiles\debug\moc_mainwindow.cpp (185): MovieEditor.exe!MainWindow::qt_static_metacall() + 0x8 bytes
    Qt5Cored.dll!QSortFilterProxyModel::mapToSource() + 0x30C5F4 bytes
    Qt5Cored.dll!QSortFilterProxyModel::mapToSource() + 0x30C03C bytes
    Qt5Widgetsd.dll!QAbstractScrollArea::setHorizontalScrollBar() + 0x4E73FB bytes
    Qt5Widgetsd.dll!QAbstractScrollArea::setHorizontalScrollBar() + 0x184F67 bytes
    Qt5Widgetsd.dll!QAbstractScrollArea::setHorizontalScrollBar() + 0x1844EB bytes
    Qt5Widgetsd.dll!QAbstractScrollArea::setHorizontalScrollBar() + 0x183912 bytes
    Qt5Widgetsd.dll!QAbstractScrollArea::setHorizontalScrollBar() + 0x7E0FD bytes
    Qt5Widgetsd.dll!QAbstractScrollArea::setHorizontalScrollBar() + 0x183471 bytes
    Qt5Widgetsd.dll!QAbstractScrollArea::setHorizontalScrollBar() + 0x244A1B bytes
    Qt5Widgetsd.dll!QAbstractScrollArea::setHorizontalScrollBar() + 0x33AAE bytes
    Qt5Widgetsd.dll!QAbstractScrollArea::setHorizontalScrollBar() + 0x3053B bytes
    Qt5Cored.dll!QSortFilterProxyModel::mapToSource() + 0x2CA892 bytes
    Qt5Cored.dll!QSortFilterProxyModel::mapToSource() + 0x3E9A43 bytes
    Qt5Widgetsd.dll!QAbstractScrollArea::setHorizontalScrollBar() + 0x35813 bytes
    Qt5Widgetsd.dll!QAbstractScrollArea::setHorizontalScrollBar() + 0xB2743 bytes
    Qt5Widgetsd.dll!QAbstractScrollArea::setHorizontalScrollBar() + 0xB17F8 bytes
    Qt5Widgetsd.dll!QAbstractScrollArea::setHorizontalScrollBar() + 0x33AAE bytes
    Qt5Widgetsd.dll!QAbstractScrollArea::setHorizontalScrollBar() + 0x2FECE bytes
    Qt5Cored.dll!QSortFilterProxyModel::mapToSource() + 0x2CA892 bytes
    Qt5Cored.dll!QSortFilterProxyModel::mapToSource() + 0x3E9A43 bytes
    Qt5Guid.dll!QPainter::drawImage() + 0x61D85 bytes
    Qt5Guid.dll!QPainter::drawImage() + 0x63DB9 bytes
    Qt5Guid.dll!QPainter::drawImage() + 0x38901 bytes
    qwindowsd.dll!qt_plugin_query_metadata() + 0x40295 bytes
    Qt5Cored.dll!QSortFilterProxyModel::mapToSource() + 0x35AFFB bytes
    USER32.dll!SetManipulationInputTarget() + 0x53 bytes
    USER32.dll!CallWindowProcW() + 0x300 bytes
    USER32.dll!DispatchMessageW() + 0x251 bytes
    USER32.dll!DispatchMessageW() + 0x10 bytes
    Qt5Cored.dll!QSortFilterProxyModel::mapToSource() + 0x35BB49 bytes
    qwindowsd.dll!qt_plugin_query_metadata() + 0x401B0 bytes
    Qt5Cored.dll!QSortFilterProxyModel::mapToSource() + 0x2C65F7 bytes
    Qt5Cored.dll!QSortFilterProxyModel::mapToSource() + 0x2C67FD bytes
    Qt5Cored.dll!QSortFilterProxyModel::mapToSource() + 0x2C8EBD bytes
    Qt5Guid.dll!QPainter::drawImage() + 0x606C8 bytes
    Qt5Widgetsd.dll!QAbstractScrollArea::setHorizontalScrollBar() + 0x2FB29 bytes
    c:\work\src\movieeditor\movieeditor\movieeditor\main.cpp (56): MovieEditor.exe!main() + 0x6 bytes
    c:\work\build\qt5_workdir\w\s\qtbase\src\winmain\qtmain_win.cpp (113): MovieEditor.exe!WinMain() + 0xD bytes
    f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c (466): MovieEditor.exe!WinMainCRTStartup()
    KERNEL32.DLL!BaseThreadInitThunk() + 0x24 bytes
    ntdll.dll!RtlUnicodeStringToInteger() + 0x253 bytes
    ntdll.dll!RtlUnicodeStringToInteger() + 0x21E bytes
  Data:
    00 00 00 00    00 00 00 00    00 00 00 00    80 E1 F9 57     ........ .......W
    00 00 CD CD    00 00 00 00    20 28 88 08    80 E1 F9 57     ........ .(.....W
    00 00 CD CD    00 00 00 00    D0 04 97 08    80 E1 F9 57     ........ .......W
    00 00 CD CD    00 00 00 00    60 7F 8F 05    80 E1 F9 57     ........ `......W
    00 00 CD CD    00 00 00 00    50 30 A7 08    80 E1 F9 57     ........ P0.....W
    00 00 CD CD    00 00 00 00    00 00 00 00    00 00 00 00     ........ ........
    00 00 00 00    CD CC CC 3E    01 00 00 00    FF FF FF FF     .......> ........
    00 00 FF FF    00 00 CD CD    70 00 9E 05    80 02 00 00     ........ p.......
    68 01 00 00    90 C0 93 05    7F 03 00 00    88 06 90 05     h....... ........
    80 52 79 05    C0 B9 79 05    30 42 8B 05    B0 7F A2 08     .Ry...y. 0B......
    B0 8D 9F 08    48 94 57 57    48 94 57 57    FF FF FF FF     ....H.WW H.WW....
    FF FF FF FF    00 CD CD CD    48 94 57 57    E0 FE 9D 05     ........ H.WW....
    01 CD CD CD    68 94 57 57    90 ED 90 05    88 0A 9E 05     ....h.WW ........
    20 0C 9E 05    E0 6F 8B 08    88 84 92 05    00 00 CD CD     .....o.. ........
    B8 FB 9D 05    68 7F 70 00    00 00 00 00    00 00 00 00     ....h.p. ........
    FF FF 00 00    00 00 00 00    00 00 CD CD    68 94 57 57     ........ ....h.WW


Visual Leak Detector detected 1 memory leak (356 bytes).
Largest number used: 4858632 bytes.
Total allocations: 18686028 bytes.
Visual Leak Detector is now exiting.

上のほうに、「editwindow.cpp (42)」とあるが、これがばっちり今回メモリリークの原因となったnewの位置を示していた。_CrtDumpMemoryLeaks 関数のように問題の場所を特定するための手間が不要なのがいい。

ただし、処理速度は恐ろしく遅い。そのため、常時使うというよりはメモリリークを調べるときだけ、プロジェクトの設定を変えることになりそう。

以上。