むらかみの雑記帳

Android とか iOS とかソフトウェア開発に関するネタ帳

Valgrind を使って iPhone アプリのメモリデバッグをする

Linux 上で開発をしている人なら知らない人はない(たぶん)と思われるメモリデバッグツールの Valgrind。これを使えば、メモリリークや解放済みメモリアクセスなど、メモリに関するバグつぶしの効率は飛躍的に改善します。

これがありゃー、Purify いらねーじゃん、って感じ。私は普段 Linux で開発しているので、使いまくりです。

さて、この Valgrind を iPhone の開発に使えないかと探していたら、すでにやってる人がいました。

残念ながら、実機ではデバッグできません。Valgrind は Intel CPU にばりばり依存しているので、ARM は無理なんです。だけど、iPhone Simulator なら動くという話。

手順をまとめておきます。

  1. こちらから、Mac OS X 版の Valgrind をダウンロードしてインストールしておきます。
  2. アプリの main.m に修正を当てて、Valgrind を起動できるようにしておきます。
  3. あとは Xcode から該当プログラムを実行すれば OK。Valgrind のログはコンソールに流れる(はず)です。

main.m の修正は以下のような感じになります(オリジナルのを少し変更)。ENABLE_VALGRIND を 0 に戻せば無効化されます。プロジェクトの設定(プリプロセッサの設定)で指定してもいいかもしれません。

#import <UIKit/UIKit.h>
#import <unistd.h>

#ifndef ENABLE_VALGRIND
#define ENABLE_VALGRIND 1
#endif

#define VALGRIND_PATH   "/usr/local/bin/valgrind"

int main(int argc, char *argv[])
{
#if ENABLE_VALGRIND
    // check if in the simulator
    NSString *model = [[UIDevice currentDevice] model];
    if ([model isEqualToString:@"iPhone Simulator"]) {

        // execute myself with valgrind
        if (argc < 2 || strcmp(argv[1], "--valgrind") != 0) {
            execl(VALGRIND_PATH, VALGRIND_PATH, "--leak-check=full", argv[0], "--valgrind", NULL);
        }
    }
#endif

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

最初、どうやって valgrind を起動するんだろ?と思ったのですが、execl を使って自分自身を valgrind 経由で起動しなおす、というなかなか豪快かつ素敵な手段で解決しております。

なお、ここではコマンドラインパラメータとして --leak-check=full しか渡していませんが、--show-reachable=yes も指定しておけば、どのコンテキストで割り当てたメモリがリークしているかも表示される、はずです。

って、まだ動作チェックしてないので本当にきちんと動くかわかりませんが。後ほどレポしたいと思います。