WebOS Goodies

WebOS の未来を模索する、ゲームプログラマあがりの Web 開発者のブログ。

WebOS Goodies へようこそ! WebOS はインターネットの未来形。あらゆる Web サイトが繋がり、共有し、協力して創り上げる、ひとつの巨大な情報システムです。そこでは、あらゆる情報がネットワーク上に蓄積され、我々はいつでも、どこからでも、多彩なデバイスを使ってそれらにアクセスできます。 WebOS Goodies は、さまざまな情報提供やツール開発を通して、そんな世界の実現に少しでも貢献するべく活動していきます。
Subscribe       

Google の Leak Finder を使ってみました

先日、 Google から Leak Finder というツールがリリースされました。主に Closure Library を対象として、メモリリークの検出を行うツールです。 Closure Library は比較的規模の大きいアプリケーションに使われることが多く、その分メモリリークの危険も高いので、こうしたツールはとてもありがたい。さっそく試してみました。

インストール

Leak Finder は Python で構築されているので、 Python の処理系(バージョンが明記されていないのですが、たぶん 2.6 か 2.7?)が必要です。もっとも、 Closure Library で開発をしているならすでにインストール済みでしょうから、それを使ってください。

インストール先のディレクトリはどこでもいいのですが、私は ~/leak-finder ディレクトリを作成して、そこにすべてを入れるようにしました。

mkdir ~/leak-finder
cd ~/leak-finder

このあたりは好みの問題なので、適当にアレンジしてください。

それでは、必要なツール等をインストールしていきます。以下は Mac OS (Mountain Lion) での例ですが、他の環境でも大きくは変わらないはずです。

depot_tools のインストール

まずは Google 内製のパッケージシステムである depot_tools です。 Chromium とかで使われているものですね。 Subversion か Git がインストールされていれば、以下のいずれかのコマンドでダウンロードできます。私はとりあえず Subversion で落としました。

svn co http://src.chromium.org/chrome/trunk/tools/depot_tools
git clone https://git.chromium.org/chromium/tools/depot_tools.git

もし Windows 環境で svn も git もなければ、こちらの zip ファイルをダウンロードして展開するという手もあるようです。

ダウンロードができたら、実行パスを追加しておきます。パスの先頭ではなく、最後に追加しなければいけないようです。

export PATH="$PATH":~/lib/leak-finder/depot_tools

私は確認していませんが、 Windows 環境の場合はここで一度 gclient を実行する必要があるようです。それによって、 svn などのツールがインストールされるとのこと。

Leak Finder のインストール

depot_tools の gclient を使って Leak Finder をインストールするのですが、その前にリポジトリとなるディレクトリを準備します。私は ~/leak-finder/repos としました。

mkdir ~/leak-finder/repos

そして、以下の内容のファイルを ~/leak-finder/repos/.gclient として作成します。

solutions = [
  { "name"        : "leak-finder",
    "url"         : "git+https://code.google.com/p/leak-finder-for-javascript",
  },
]

あとは、リポジトリディレクトリで gclient sync を実行すれば、 Leak Finder と必要な依存ファイルがインストールされます。

cd ~/leak-finder/repos
gclient sync

よくできてる・・・。

simplejson のインストール

Leak Finder は Python の simplejson ライブラリを使っているので、最後にそれをインストールします。 easy_install 等を使うのが手軽ですが、私はパッケージを ~/leak-finder 以下に直接展開して、 PYTHONPATH に含めるようにしました。

cd ~/leak-finder
curl -O http://pypi.python.org/packages/source/s/simplejson/simplejson-2.6.1.tar.gz
tar zxvf simplejson-2.6.1.tar.gz
export PYTHONPATH=$PYTHONPATH:$HOME/leak-finder/simplejson-2.6.1

これでインストール作業は完了です。

使ってみる

Leak Finder を使う手順は、概ね以下になります。

  1. リモートデバッグを有効にして Chrome を起動する
  2. Chrome 上でデバッグ対象のアプリケーションのページを開く
  3. Leak Finder を実行する

(2) で実行するアプリケーションは、以下の条件を満たしていなければなりません。

  • Closure Library の 2012/7/10 以降のバージョンを使っている
  • Closure Compiler による最適化をかけていない
  • goog.Disposable を require した直後に(goog.Disposable インスタンスをひとつも new していない時点で) 「goog.Disposable.ENABLE_MONITORING = true」を実行している

Chrome を起動する

ここでは、 Leak Finder のリポジトリにあるテスト用の HTMLで試すことにしましょう。

まずは Chrome をリモートデバッグ可能な状態で起動します。具体的には、「--remote-debugging-port=9222 --js-flags=--stack_trace_limit=-1 --user-data-dir=/tmp/jsleakcheck」というオプションを付けて起動すれば OK です。 Mac OS の場合はシェルで以下のコマンドを実行するのがよいでしょう。

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 --js-flags=--stack_trace_limit=-1 --user-data-dir=/tmp/jsleakcheck

ちなみに最後の --user-data-dir はプロファイルディレクトリの指定なので、とくに Windows では別の適当な場所を指定してください。このオプションはおそらく必須ではないでしょうが、公式サイトの例に倣って指定しています。

アプリケーションのページを開く

前述のとおり Leak Finder のテスト用 HTML を使います。 Chrome のアドレスバーに以下の URL を入力してページを開いてください。

http://leak-finder-for-javascript.googlecode.com/g...

複数のタブを開いたりせず、起動時の最初のタブを使う必要があるようです。ご注意ください。

Leak Finder を実行する

ページの読み込みが終わったら、 Leak Finder を実行します。シェルで以下のコマンドを実行すれば OK です。

cd ~/leak-finder/repos/leak-finder/src
python jsleakcheck.py -d closure-disposable -v

実行が成功すれば、以下の様な感じで出力されるはずです。私の環境ではうまくいかないことが多いのですが、詳しくは後述。

$ python2.6 jsleakcheck.py -d closure-disposable -v
INFO:root:Using leak definition closure-disposable
INFO:root:Reading suppressions from "closure-disposable-suppressions.txt"
INFO:root:Taking heap snapshot
INFO:root:Analyzing heap snapshot
INFO:root:Retrieving creating stack traces for leaking objects
INFO:root:Scanning for new leaks.
New memory leaks found:
Leak: 1 MyObj
allocated at:
  goog.Disposable
  Object.goog.base
  new MyObj
  MyObjCreator.Create
  http://leak-finder-for-javascript.googlecode.com/git/doc/test-page.html:23:23

単にリークが存在するというだけでなく、そのクラス名や確保時のコールスタックも表示されています。これだけ情報が表示されれば、リーク元を突き止めるのもかなり楽になりそうですね。

Leak Finder にできること・できないこと

テスト用 HTML ページの中を見ると、以下のようになっています。

<script src="http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js"></script>
<script>
goog.require('goog.Disposable');
</script>
<script>
goog.Disposable.ENABLE_MONITORING = true

MyObj = function() {
  goog.base(this);
}
goog.inherits(MyObj, goog.Disposable);

MyObjCreator = function() {}
MyObjCreator.prototype.Create = function() {
  return new MyObj();
}
var creator = new MyObjCreator();

// Not a leak.
var handle = creator.Create();

// Leak.
var handle2 = creator.Create();
handle2 = null;
</script>

最後のほうを見ると、 handle と handle2 の 2 つの MyObj インスタンスが生成されています。コードからすると両方共リークしているようにも読めますが、 Leak Finder がリークとして検出しているのは handle2 のほうだけです。これは、 Leak Finder がリークとして検出する条件が以下のようになっているからです。

  • goog.Disposable クラス(もしくはその派生クラス)のインスタンスである
  • 確保以降(goog.Disposable コンストラクタの呼び出し以降)、 dispose() メソッドが呼ばれていない
  • ページ内のいずれのオブジェクトからも参照されていない

つまり、 dispose() される前にすべての参照が外されたオブジェクトのみがリークとして検出され、それ以外はリークとはみなされません。例えば、以下は実際にメモリリークしていても、 Leak Fider では検出されません。たぶん。

  • goog.Dispose クラスを継承していないオブジェクト同士の相互参照
  • dispose() の呼び出しと参照の削除の両方を忘れている
  • disposeInternal() の実装がそもそも間違っている(DOM 要素への参照を null にしていないなど)

ということで、万能のツールというわけではないようです。

また、 Leak Finder のリリース時のブログ記事では Closure Library 以外のライブラリにも使える的な記述がありますが、それもそう簡単ではないと思います。そもそも disposable パターンのメモリ管理をしていないと意味がないでしょうし。事実上は Closure Library 専用のツールになるのではないかと思います。

まだ安定していない?

上記に加えて、少なくとも私の環境では、 Leak Finder の実行は 5 回に 1 回くらいしか成功しません。多くの場合は途中で動作が止まって、 Ctrl-C も効かず、 kill するしかない状態になります。少しだけ追ってみたのですが、 Chrome との通信、もしくはそのスレッド周りでデッドロックしているようです。必ず止まるわけではないので、たぶんタイミングなんでしょうね・・・。

これについては、なにかわかったらここに追記しようと思います。もし同様に試された方がおられましたら、情報を共有していただけるとありがたいです。

以上、本日は Google がリリースした Leak Finder をご紹介しました。あらゆるメモリリークを検出する、というわけではありませんが、有用なツールであることは確かだと思います。あとは安定して動くようになってくれれば・・・というところですね。今後の開発に期待したいです。

関連記事

この記事にコメントする

Recommendations
Books
「Closure Library」の入門書です。
詳しくはこちらの記事をどうぞ!
Categories
Recent Articles