小さい頃はエラ呼吸

いつのまにやら肺で呼吸をしています。


WinDbgを使ってプロセスのハンドルリークを調査する方法

はじめに

アプリケーションを長時間動作させた際にハンドル数がいちじるしく増加していく場合、ハンドルリークが発生している可能性が疑われます。

デバッグの理論と実践 ―なぜプログラムはうまく動かないのか
Andreas Zeller
オライリージャパン
売り上げランキング: 210,805

ハンドルリークは、アプリケーションが不要になったリソースを解放しないことが原因で発生します。
結果としてハンドルリソースのリークが発生し、最終的には制限に達して、そのアプリケーションや他のアプリケーション、またはシステム全般の動作が異常になったり、動作の診断が難しくなります。

この記事では、WinDbgを使ってハンドルリークを調査する方法をまとめました。

必要なもの

インストーラを起動し、Debugging Tools for Windowsをインストールしてください。
f:id:replication:20150613112117p:plain

ハンドルリークするサンプルプログラム(HandleLeakApp.exe)

以下のプログラムを実験に使います。fopenしてfcloseしていないのでファイルハンドルが徐々に増えていきます。

#include "stdafx.h"
#include "windows.h"
#include <stdlib.h>
#pragma warning(disable : 4996)

int _tmain(int argc, _TCHAR* argv[])
{
  system("pause");
  FILE *fp;
  char *filename = "c:\\sample.txt";

  int i = 0;
  while (i < 10000)
  {
    // ファイルのオープン
    if ((fp = fopen(filename, "r")) == NULL)
    {
      fprintf(stderr, "%sのオープンに失敗しました(%d)\n", filename, GetLastError());
      return 1;
    }

    // handle leak!
    //fclose(fp);

    printf("%d", i);
    Sleep(500); // 500msスリープ
    i++;
  }
  return 0;
}

このプログラムを動作させると、タスクマネージャ上でハンドル数が増加していくのが分かります。
f:id:replication:20150613112105p:plain

WinDbgを使ってハンドルリークを調査する

1.WinDbgを起動します。
f:id:replication:20150613112212p:plain
2.Ctrl + Sでシンボルパスに以下を指定します。

srv*c:\websymbols*http://msdl.microsoft.com/download/symbols;

f:id:replication:20150613112330p:plain
3.調査対象のアプリケーション(ここではHandleLeakApp.exe)を起動します。
4.F6キーからHandleLeakApp.exeにアタッチします。
f:id:replication:20150613112530p:plain
5.!htrace -enableコマンドを実行します。
f:id:replication:20150613112657p:plain

0:001> !htrace -enable
Handle tracing enabled.
Handle tracing information snapshot successfully taken.

6.!htrace -snapshotコマンドを実行します。
f:id:replication:20150613113027p:plain

0:001> !htrace -snapshot
Handle tracing information snapshot successfully taken.

7.g コマンドを実行し、デバッガを走らせます。
f:id:replication:20150613113037p:plain
f:id:replication:20150613113242p:plain
8.HandleLeakApp.exeをしばらく動作させ、ハンドル数が増加していくことを確認します。
9.WinDbgをbreakさせます。
f:id:replication:20150613113402p:plain
10.!htrace -diffコマンドを実行します。
f:id:replication:20150613114235p:plain
スタックトレースが出力されています。
Displayed 0x5c stack traces for outstanding handles opened since the previous snapshot.
0x5c=10進数で92なので、snapshotを仕掛けてから92個のハンドルが開かれたことが分かります。

スタックが特定できると、ここから原因を調べていくことができるようになると思います。