Android* のシステムレベルの Java*/C++ コードのデバッグ
この記事は、インテル® デベロッパー・ゾーンに掲載されている「Android* System-Level Java*/C++ Code Debugging」の日本語参考訳です。
1 はじめに
アプリケーション開発者は現在、Android* SDK を使って Eclipse* から Android* アプリケーション・プロジェクトの作成、ビルド、デバッグを行うことができます。しかし、Android* コードベースであるシステムレベルの Java*/C++ コードをデバッグする機能はありません。Android* SDK では、これらのコードをビルドしたり、デバッグできません。この記事では、Eclipse* で Android* のシステムレベルの Java*/C++ コードをデバッグする方法を説明します。
2 インストール
2.1 JDK のインストール
http://java.sun.com/javase/downloads/index.jsp (英語) から JDK6 (Java* SE 6 アップデート・パッケージ) をダウンロードします。
現在の JDK6 バージョンは jdk-6u32-linux-x64.bin です。ここでは、このバージョンを使用します。
JDK をインストールして、デフォルトの設定を使用します。
$ cd /usr/lib/jvm $ sudo /path/to/jdk-6u32-linux-x64.bin $ sudo ln -s jdk1.6.0_32 java-6-sun $ export PATH=/usr/lib/jvm/java-6-sun/bin:$PATH $ export JAVA_HOME=/usr/lib/jvm/java-6-sun
2.2 Eclipse* のインストール
http://www.eclipse.org/downloads/ (英語) から Eclipse* 3.6.2 以上をダウンロードします。
現在の Eclipse Classic バージョンは 3.7.2 (Indigo) です。ここでは、このバージョンを使用します。
$ mkdir ~/android-sdk $ cd ~/android-sdk $ tar zxf /path/to/eclipse-SDK-3.7.2-linux-gtk-x86_64.tar.gz
2.3 Eclipse* CDT プラグインのインストール
C/C++ プロジェクトを作成し、C++ コードをデバッグするには、CDT プラグインをインストールする必要があります。
Eclipse* を起動します。
$ cd ~/android-sdk/eclipse/ $ ./eclipse
ファイアウォールが有効な場合は、[Window] > [Preferences] > [General] > [Network Connection] を選択して、図 1 に示すダイアログでプロキシーを設定します。
図 1. Eclipse* のプロキシー設定
[Help] > [Check for Updates] を選択して、最新の Eclipse* プラグインリストをダウンロードします。[Window] > [Preferences] > [Install/Update] > [Available Software Sites] を選択して、次のサイトを有効にします。
http://download.eclipse.org/sequoyah/updates/2.0/
図 2. Eclipse* ソフトウェア・アップデート
[Help] > [Install New Software] を選択して、CDT プラグインをインストールします。http://download.eclipse.org/sequoyah/updates/2.0/ を選択し、[Group items by category] チェックボックスをオフにしてから、[Select All] をクリックしてすべてのプラグインをインストールします。
図 3. Sequoyah プラグインのインストール
2.4 Android* SDK のインストール
http://developer.android.com/sdk/index.html (英語) から Android* SDK をダウンロードします。現在の Android* SDK バージョンは r18 です。ここでは、このバージョンを使用します。
Android* SDK をインストールします。
$ cd ~/android-sdk/ $ tar zxf /path/to/android-sdk_r18-linux.tgz
Android* パッケージ・マネージャーを起動して、Android* SDK パッケージをダウンロードします。
$ cd ~/android-sdk/android-sdk-linux/tools/ $ ./android
プロキシーを設定して、[Force https://…sources to be fetched using http://] チェックボックスをオンにします。
図 4. Android* SDK Manager のプロキシー設定
パッケージを選択してダウンロードします (Tools パッケージは必須です)。
図 5. Android* SDK アップデートのインストール
2.5 Android* ADT プラグインのインストール
Android* ADT (Android* Development Tool) をインストールします。
Eclipse* を起動します。
$ cd ~/android-sdk/eclipse/ $ ./eclipse[Help] > [Install New Software] を選択して、表示されるダイアログで [Add Repository] ボタンをクリックします。
表示される [Add Repository] ダイアログで、[Name] に「ADT Plugin」と入力し、[Location] に次の URL を入力します。
https://dl-ssl.google.com/android/eclipse/
図 6. ADT プラグインのインストール
画面の指示に従って ADT プラグインをインストールします。
その後、[Window] > [Preference] > [Android] を選択し、[SDK location] に Android* SDK のパスを入力します。
図 7. Android* SDK の設定
2.6 Android* システム・デバッグ・ユーティリティーのインストール
Linux* ホスト上に次のパッケージを保存して展開します。
$ cd ~/android-sdk $ tar zxf android-debug-utility.tar.gz
3 Android* のシステム Java* コードのデバッグ
3.1 system_process のデバッグ
プロセス system_process は、いわゆる Android* のシステムサーバーです。このプロセスは、システムの起動時に zygote によって生成されます。ここでは、Eclipse* を使ってこのプロセスをデバッグする方法を示します。
3.1.1 system_process 用の疑似 Android* プロジェクトの作成
DDMS (Dalvik Debug Monitor Server) は、Android* プロジェクト内のコードのみデバッグできます。しかし、system_process は、Android* プロジェクトではなく、Android* コードベースからビルドされています。Eclipse*/DDMS で system_process をデバッグするには、疑似 Android* プロジェクトを作成する必要があります。
[File] > [New] > [Project] で [Android project] を選択します。
図 8. Android* プロジェクトの作成
図 9. Android* パッケージ名
[AndroidManifest.xml] をクリックして、パッケージ名を system_process に変更します。図 10. 変更後の Android* パッケージ名
3.1.2 DDMS による system_process のデバッグ
DDMS ビューに移動し、system_process を選択して、[Debug] ボタンをクリックします。
図 11. Android* DDMS ビュー
デバッグビューに移動し、デバッグするスレッドを選択して、[Suspend] ボタンをクリックします。
図 12. Android* デバッグビュー
Java* ソースコードのデバッグを行うには、Android* ソースの場所を指定する必要があります。[Edit Source lookup Path] ボタンをクリックし、表示されるダイアログで [Add] ボタンをクリックして、[File System Directory] を選択します。Android* の Java* コードは、主に frameworks/base ディレクトリーと libcore ディレクトリーに配置されています。
frameworks/base を選択します。
図 13. framework/base ソースパスの設定
libcore ディレクトリーも追加する必要があります。
図 14. libcore ソースパスの設定
エディターウィンドウに Java* コードが表示されます。
図 15. Android* プロジェクト・デバッガー
これで、Android* のシステムレベルの Java* コードでブレークポイントを設定し、デバッグする準備ができました。
3.2 Android* ビルトイン・アプリケーションのデバッグ
Android* には、カレンダー、設定、ギャラリーなど、多数のビルトイン・アプリケーション (システム・アプリケーションとも呼ばれる) があります。これらのプロジェクトは、Android* コードベースに含まれており、system.img と一緒にビルドされています。これらのプロジェクトをデバッグするには、system_process と同様に、疑似プロジェクトを作成する必要があります。その場合、前述のステップに従ってプロジェクトを作成し、パッケージ名を system_process の代わりに、アプリケーション名にします。
図 16. Android* アプリケーション・パッケージ名
DDMS に移動し、プロセス com.android.calendar を選択して [Debug] ボタンをクリックします。
図 17. デバッグするアプリケーションの選択
図 18. Android* アプリケーション・デバッガー
4 Android* のシステム C/C++ コードのデバッグ4.1 system_process のデバッグ
通常、gdbserver/gdb で Android* のネイティブ C/C++ コードをデバッグできます。つまり、gdbserver を実行中のプロセスにアタッチして、gdb を使ってリモートでデバッグします。しかし、system_ process は、システムの起動時に zygote によって生成され、gdbserver をアタッチする前に初期化を完了してしまいます。最初から system_process をデバッグするには、android-debug-utility を使って system_process が最初から無限ループに入るように強制し、できるだけ早期に gdbserver をアタッチします。そして、system_process を無限ループから出し、実行を続けます。
4.1.1 C/C++ プロジェクトの作成
新しい C/C++ プロジェクトを作成するか、 前の節で作成した既存の Android* プロジェクト system_process を C/C++ プロジェクトに変換します。[File] > [New] > [Convert to a C/C++ Project] を選択し、表示されるダイアログで system_process プロジェクトを選択します。
図 19. Android* プロジェクトを C/C++ プロジェクトに変換
4.1.2 デバッグ構成の作成
デバッグビューで [Debug] ボタンをクリックし、[Debug Configurations] を選択します。[C/C++ Remote Application] でデバッグ構成を作成し、app_process バイナリーのパスを入力して、[Disable auto build] をオンにします。
図 20. デバッグするアプリケーション・イメージの設定
gdb* に正しいシンボル・ファイル・ディレクトリーを知らせるには、android-debug-utility のディレクトリーに移動し、gdb* 初期化スクリプト sdk_x86_gdb.setup を開いて、$android と $__board を Android* ルート・ディレクトリーとデバイス名に変更します。
shell echo set solib-absolute-prefix $android/out/target/product/$__board/symbols > tmp.gdb shell echo set solib-search-path $android/out/target/product/$__board/symbols/system/lib:$android/out/target/product/$ __board/symbols/system/lib/hw:$android/out/target/product/$__board/symbols/system/li b/egl:$android/out/target/product/$__board/symbols/system/lib/soundfx:$android/out/tar get/product/$__board/symbols/system/lib/bluez-plugin:$android/out/target/product/$__b oard/symbols/system/vendor/lib/egl:$android/out/target/product/$__board/symbols/syste m/vendor/lib/hw:$android/out/target/product/$__board/symbols/system/vendor/lib:$andr oid/out/target/product/$__board/symbols/system/lib/parameter-framework-plugins/Audio: $android/out/target/product/$__board/symbols/system/lib/soundfx:$android/out/target/p roduct/$__board/symbols/system/usr/lib/alsa-lib >> tmp.gdb source tmp.gdb
あるいは、Eclipse* を起動する前に、次のコマンドを実行してこの 2 つの環境変数を export します。
$ export android=<android root directory> $ export __board=<devicename>
デバッグビューで [Debug] ボタンをクリックし、[Debug Configurations] を選択し、[C/C++ Remote Application] のデバッグ構成の [Debugger] タブで sdk_x86_gdb.setup のパスを入力します。
図 21. gdb* 初期化スクリプトの設定
[Connection] タブでポート番号を 1234 に変更します。図 22. gdb* デバッグポートの設定
4.1.3 system_process のデバッグ
Android* デバイスの電源を入れて、USB ケーブルでホストと接続します。system_process を再起動して、gdbserver をアタッチします。
$ cd ~/android-sdk/android-debug-utility/target $ ./start_system_server.sh zygote:115 killall: gdbserver: no process killed killall: gdb: no process killed /lib/gdbserver: No such file or directory mkdir failed for /lib, File exists push: ./lib/ld-linux.so.2 -> /lib/ld-linux.so.2 push: ./lib/system_server -> /lib/system_server push: ./lib/libthread_db.so.1 -> /lib/libthread_db.so.1 push: ./lib/libpthread.so.0 -> /lib/libpthread.so.0 push: ./lib/libc.so.6 -> /lib/libc.so.6 push: ./lib/libreadline.so.5.2 -> /lib/libreadline.so.5.2 push: ./lib/libreadline.so.6 -> /lib/libreadline.so.6 push: ./lib/gdb -> /lib/gdb push: ./lib/libdl.so.2 -> /lib/libdl.so.2 push: ./lib/app -> /lib/app push: ./lib/libz.so.1 -> /lib/libz.so.1 push: ./lib/libm.so.6 -> /lib/libm.so.6 push: ./lib/libutil.so.1 -> /lib/libutil.so.1 push: ./lib/libexpat.so.1 -> /lib/libexpat.so.1 push: ./lib/libpython2.6.so.1.0 -> /lib/libpython2.6.so.1.0 push: ./lib/libcrypto.so.0.9.8 -> /lib/libcrypto.so.0.9.8 push: ./lib/service -> /lib/service push: ./lib/libssl.so.0.9.8 -> /lib/libssl.so.0.9.8 push: ./lib/gdbserver -> /lib/gdbserver push: ./lib/libncurses.so.5 -> /lib/libncurses.so.5 20 files pushed.0 files skipped. 1770 KB/s (10526293 bytes in 5.805s) 4 KB/s (440 bytes in 0.099s) please connect to the system server via gdb remote Attached; pid = 309 Listening on port 1234
adb でローカル TCP ポート 1234 を Android* デバイスへ転送します。
$ adb forward tcp:1234 tcp:1234
Eclipse* でデバッグビューに移動します。
[Debug] ボタンをクリックし、[Debug Configurations] を選択します。表示されるダイアログで system_process_1234 構成を選択し、[Debug] ボタンをクリックします。図 23. C/C++ デバッガーの起動
gdb* が gdbserver に接続するまで待機します。
図 24. system_server の C/C++ デバッガー
タスクを中断して [Console] に移動し、コマンド thread 1 と go を入力します。これにより、タスクが無限ループから抜けます。
thread 1 [Switching to thread 1 (Thread 309)] #0 setgid () at bionic/libc/arch-x86/syscalls/setgid.S:10 10 pushl %ebx go system server in dead loop, trying to restore...done
ブレークポイントを設定して、コードをデバッグします。
4.2 Android* アプリケーションのデバッグ
system_process と同様に、Android* アプリケーションも zygote によって生成されます。そのため、main 関数を含む通常の C/C++ プログラムのように起動することはできません。Android* アプリケーションをデバッグするには、system_process と同様に、アプリケーションが開始時に無限ループに入るようにし、できるだけ早期に gdbserver をアタッチします。また、C/C++ プロジェクトとデバッグ構成を作成する必要があります。便宜上、ここでは、C/C++ プロジェクト system_process とデバッグ構成 system_process_1234 を再利用します。
4.2.1 Android* アプリケーションのデバッグ
Android* デバイスの電源を入れて、USB ケーブルでホストと接続します。start_app.sh スクリプトを実行します。
$ cd ~/android-sdk/android-debug-utility/target $ ./start_app.sh zygote:305 killall: gdbserver: no process killed please launch the app
Android* ランチャーから Android* アプリケーションを起動するか、Eclipse* から Android* アプリケーションを実行します。アプリケーションに接続できることを示すメッセージが表示されます。
$ ./start_app.sh zygote:305 killall: gdbserver: no process killed please launch the app please connect to the app 1362 via gdb remote Attached; pid = 1362 Listening on port 1234
Eclipse* でデバッグビューに移動します。
[Debug] ボタンをクリックし、[Debug Configurations] を選択します。表示されるダイアログで system_process_1234 構成を選択し、[Debug] ボタンをクリックします。図 25. C/C++ デバッガーの起動
gdb* が gdbserver に接続するまで待機します。
図 26. Android* アプリケーションの C/C++ デバッガー
タスクを中断して [Console] に移動し、コマンド thread 1 と go を入力します。
ブレークポイントを設定して、コードをデバッグします。
4.3 Android* サービスのデバッグ
Android* 初期化スクリプト init.rc では、システムの起動時に次のネイティブサービスが起動されます。
service mtpd /system/bin/mtpd class main socket mtpd stream 600 system system user vpn group vpn net_admin inet net_raw disabled oneshot service keystore /system/bin/keystore /data/misc/keystore class main user keystore group keystore socket keystore stream 666
サービスは通常、C/C++ で記述されており、main() から呼び出されます。しかし、これらのサービスは初期化プロセスによって起動され、その動作は手動で実行した場合と異なります。これらのサービスをデバッグするには、start_service.sh スクリプトを使用します。また、C/C++ プロジェクトと C/C++ デバッグ構成を作成する必要があります。便宜上、ここでは、既存の system_process プロジェクトとデバッグ構成を使用します。
4.3.1 Android* サービスのデバッグ
使用方法: start_service.sh [ service name ]
ホスト上で次のコマンドを実行します。
$ cd ~/android-sdk/android-debug-utility/target $ ./start_service.sh mtpd init:1 killall: gdbserver: no process killed killall: gdb: no process killed 5 KB/s (585 bytes in 0.099s) please start the service: start mtpd
ターゲット上で次のコマンドを実行します。
# start mtpd
サービスをデバッグできるようになったことを示すメッセージがホスト上に表示されます。
user@ubuntu:~/android-sdk/android-debug-utility/target$ ./start_service.sh mtpd init:1 killall: gdbserver: no process killed killall: gdb: no process killed 5 KB/s (585 bytes in 0.099s) please start the service: start mtpd please connect to the app 1561 via gdb remote
ほかの Android* Java* アプリケーションと異なり、サービスはスタンドアロンの C/C++ アプリケーションであり、zygote と同じイメージ app_process を共有しません。そのため、デバッグ構成でサービスバイナリーの場所を指定する必要があります。
図 27. デバッグする Android* サービスバイナリーの設定
デバッグ構成 system_process_1234 でデバッガーを起動し、gdbserver に接続するのを待機します。
図 28. Android* サービスの C/C++ デバッガー
タスクを中断して [Console] に移動し、コマンド thread 1 と go を入力します。
thread 1 [Switching to thread 1 (Thread 1561)] #0 0x08049663 in main (argc=1, argv=0xbf9a3d64) at external/mtpd/mtpd.c:155 155 { go android service in dead loop, trying to restore...done
ブレークポイントを設定して、コードをデバッグします。
5 Java と C/C++ のハイブリッド・コードのデバッグ
5.1 Java* からネイティブへのコールトレースの検証
場合によっては、Java* からネイティブへのすべてのコールトレースを検証することもあるでしょう。その場合、前述の Java* と C/C++ の両方のデバッグ手法が必要になります。
- start_system_server.sh スクリプトまたは start_app.sh スクリプトを使って、system_process とアプリケーションが無限ループに入るようにします。
- DDMS で、system_process またはアプリケーションにアタッチします。
- go コマンドを使って、system_process またはアプリケーションを無限ループから出します。
- ネイティブ関数で C/C++ ブレークポイントを切り替えます。
- loop コマンドを使って、ネイティブ関数が無限ループに入るようにします。
- しばらく実行させます。
- Android* デバッガーに移動し、スレッドを停止して、各スレッドのコールスタックを確認します。ネイティブ関数の呼び出し元 (無限ループにある) を特定します。
Java* コールトレースと C/C++ コールトレースを 1 つにまとめて、Java* からネイティブへの全体のコールトレースを確認します。
以下のケースでは、次のようなコールトレースになります。
Java*: WindowManagerService$Session.add-> SurfaceSession.init()->
C/C++: SurfaceSession_init()->android::SurfaceFlinger::createConnection()
5. IPC コールトレースの検証
Android* プロセス間通信 (IPC) は、バインダーにより実装されます。1 つのタスクは 1 つの関数を呼び出します。関数は別のタスクによって実行されることがあり、その場合は実行タスクから結果を受け取ります。場合によっては、IPC 呼び出しで、複数のタスクにわたるコールトレース全体を検証することもあるでしょう。幸い、バインダー IPC は同期処理なので、 呼び出し先のタスクが完了していない場合、呼び出し元のタスクはリターンしません。そのため、呼び出し先タスクが無限ループに入るようにすると、呼び出し元タスクは IPC 呼び出しでブロックされます。これにより、呼び出し元と呼び出し先の両方のタスクのコールトレースを検証することができます。
両方のタスクを検証するには、それぞれのタスクに 1 つずつ、計 2 つの gdbserver/gdb セッションを起動する必要があります。別の C/C++ プロジェクト system_process_2 と、デバッガー TCP ポート 1235 を使用する別のデバッグ構成を作成します。
- 5.1 のステップに従って、最初にデバッグ構成 system_process_1234 で 1 つ目のタスクに接続します。
- 次に、デバッグ構成 system_process_1235 で 2 つ目のタスクに接続します。
注: start_app.sh でデバッガーが監視するポートを 1234 から 1235 に変更し、adb でローカルポート 1235 をデバイスに転送します。
- 呼び出し先タスクが無限ループに入るようにするか、特定の関数にブレークポイントを設定してから、中断されるまで実行します。
- 2 つのタスクのコールトレースを検証します。
これで、呼び出し元タスクと呼び出し先タスクのすべてのコールトレースが確認できます。さらに、Java* デバッガーをアタッチすると、すべての IPC コールトレースで Java* トレースも確認できます。
著者紹介
Jack Ren
インテル コーポレーションのモバイル・コンピューティング・グループ (MCG) Android* エンジニアリング・チームに所属する Android* アーキテクト。Linux* カーネルから Android* ソフトウェア・スタックまで、Linux* 開発において 8 年の経験があります。MCG Android* 上海チームを設立し、初代マネージャーとして、 2 年前にゼロからスタートし商品化に至るまでの MCG Android* ソフトウェア開発の飛躍的な成長を見届けてきました。現在は、インテルの MCG/PSI Android* 開発チームの主力メンバーとして、LLVM、Dalvik、UX、Google へのインテルのパッチのアップストリーミングなど、Android* の技術分野に従事しています。
著作権と商標について
本資料に掲載されている情報は、インテル製品の概要説明を目的としたものです。本資料は、明示されているか否かにかかわらず、また禁反言によるとよらずにかかわらず、いかなる知的財産権のライセンスを許諾するものではありません。製品に付属の売買契約書『Intel’s Terms and Conditions of Sale』に規定されている場合を除き、インテルはいかなる責任を負うものではなく、またインテル製品の販売や使用に関する明示または黙示の保証 (特定目的への適合性、商品適格性、あらゆる特許権、著作権、その他知的財産権の非侵害性への保証を含む) に関してもいかなる責任も負いません。
インテルによる書面での合意がない限り、インテル製品は、その欠陥や故障によって人身事故が発生するようなアプリケーションでの使用を想定した設計は行われていません。
インテル製品は、予告なく仕様や説明が変更される場合があります。機能または命令の一覧で「留保」または「未定義」と記されているものがありますが、その「機能が存在しない」あるいは「性質が留保付である」という状態を設計の前提にしないでください。これらの項目は、インテルが将来のために留保しているものです。インテルが将来これらの項目を定義したことにより、衝突が生じたり互換性が失われたりしても、インテルは一切責任を負いません。この情報は予告なく変更されることがあります。この情報だけに基づいて設計を最終的なものとしないでください。
本資料で説明されている製品には、エラッタと呼ばれる設計上の不具合が含まれている可能性があり、公表されている仕様とは異なる動作をする場合があります。現在確認済みのエラッタについては、インテルまでお問い合わせください。
最新の仕様をご希望の場合や製品をご注文の場合は、お近くのインテルの営業所または販売代理店にお問い合わせください。
本資料で紹介されている資料番号付きのドキュメントや、インテルのその他の資料を入手するには、1-800-548-4725 (アメリカ合衆国) までご連絡いただくか、
http://www.intel.com/design/literature.htm (英語) を参照してください。
性能に関するテストに使用されるソフトウェアとワークロードは、性能がインテル® マイクロプロセッサー用に最適化されていることがあります。SYSmark* や MobileMark* などの性能テストは、特定のコンピューター・システム、コンポーネント、ソフトウェア、操作、機能に基づいて行ったものです。結果はこれらの要因によって異なります。製品の購入を検討される場合は、他の製品と組み合わせた場合の本製品の性能など、ほかの情報や性能テストも参考にして、パフォーマンスを総合的に評価することをお勧めします。
本資料に含まれるソフトウェア・ソース・コードはソフトウェア・ライセンス契約に基づいて提供されるものであり、その使用および複製はライセンス契約で定められた条件下でのみ許可されます。
Intel、インテル、Intel ロゴは、アメリカ合衆国および / またはその他の国における Intel Corporation の商標です。
© 2012 Intel Corporation. 無断での引用、転載を禁じます。
* その他の社名、製品名などは、一般に各社の表示、商標または登録商標です。
コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。