2011年10月05日
アプリをたくさんインストールするとndk-gdbが使えなくなる
NDKを使用してネイティブライブラリをビルドして組み込んだアプリケーションは、ndk-gdbを使ってデバッグすることができます。しかし、ndk-gdbがエラーで起動できないという場面に遭遇し、調べてみると、なんとアプリをたくさんインストールしてある端末でそのようなことが発生するということがわかりました。以下はその詳細です。
なぜかある特定の端末でだけndk-gdbが起動に失敗します。動かしているのはNDKのサンプルプログラムのSanAngelesです。
$ ../../ndk-gdb --start ERROR: Could not extract package's data directory. Are you sure that your installed application is debuggable?
ndk-gdbはshell scriptなので、その中を見てみるとどうもrun-asコマンドのところでエラーになっているらしい。切り分けのためにSanAngelesを手で起動した状態で、adbでログインしてrun-asを試すと
$ adb shell $ ps .... app_140 4347 2577 184108 19724 ffffffff 00000000 S com.example.SanAngeles shell 4378 4279 964 340 00000000 afd0b4ac R ps $
$ run-as com.example.SanAngeles /system/bin/sh -c pwd run-as: Package 'com.example.SanAngeles' is unknown
やはり run-asがエラーを返しています。
他のうまくいく端末で同じことをすると
$ run-as com.example.SanAngeles /system/bin/sh -c pwd /data/data/com.example.SanAngeles
これが想定している応答です。
run-asコマンドのソースを見ると、/data/system/packages.list というファイルをopenしてreadしています。このファイルはテキストファイルで、各アプリケーションのパッケージ名、UID、デバッグ可否、ディレクトリが書かれています。run-asコマンドはこのファイルからそのアプリケーションがデバッグを許可しているかを調べ、許可している場合のみそのアプリケーションと同じUIDで指定のコマンドを実行するわけです。
adb shell cat /data/system/packages.list | grep SanAngeles
とすると com.example.SanAngelesも登録されているのがわかりました。
よくよくソースを見ると、run-asコマンドは一回のreadでファイル全体をバッファに読み込んで処理するようになっていますが、そのバッファを固定的に8192バイトしかとっていません。つまりファイルの先頭の8KB分しか読まれない!!
うまくいかない端末での/data/system/packages.list はサイズが13KBくらいありました。
確かに、run-asで /data/system/packages.list の最初のほうのパッケージ名を指定すると、unknown packageというエラーでなく、not debuggable というエラーに変わりました。
つまりrun-asが想定通りに動かないのはバッファのサイズが足りないというコーディングミスによるもののようです。
そのため、/data/system/packages.listのサイズが8KBよりも小さくなるように不要不急なアプリを全てアンインストールしてから、デバッグ対象のプログラムをインストールしたらrun-asコマンドが効くようになり、gdb-ndkを起動することができるようになりました。
最後にインストールしたアプリがこのpackages.listの最後に追加されるというわけではありません。(このファイルを書き出しているところのソースは調べていませんが、メモリ内にハッシュテーブルを作った後にそれをダンプしているような感じです。)そのため、packages.listが8KBを超えた状態では、アプリをインストール、アンインストールすることでndk-gdbが使えたり、使えなかったりという不可思議な状態になると思います。
追記
確認したのはAndroid 2.3.4_r1のソースです。
masterのソースではPACKAGES_LIST_BUFFER_SIZEは8192バイトから65536バイトに拡張されていました。
system/core/run-as/package.c