前回の設定により JNIの開発環境としてもEclipseが使えるようになりました。
ビルド自体もcygwinからndk-buildコマンドを実行しなくとも、EclipseからC/C++(NDK)の含めてビルドや実行を することが可能になりました。
今回は、目標でもあるネイティブコードのソースコードレベルデバッグについて、設定方法&実行方法を説明してきます。
使用するもの
- 前回構築した各プラグインの導入されたEclipse (indigo)
- 前回サンプルとして使ったMyApplicationのサンプルコード
- Android実機
- 実機でデバッグできるようになると感動です
サンプルは手を加えて使うので、似たようなものが用意できれば特に前回のものと同じである必要はありません。
今回単にデバッグ対象というだけで使用します。
Androidの実機についてですが、GalaxyS だけはうまくいきませんでした。
ICONIA Tab A500、Nexus S、GalaxyS2 では、このデバッグ方法がうまくいくことを確認できています。
ネイティブデバッグのための設定変更
まず、AndroidManifest.xml で Debuggable を true にしておきます。これはデバッグする対象に必須です。
デバッグの構成を変更(あるいは新規作成します)。
この新規作成するデバッグの構成は、”C/C++ Application”のデバッグ構成です。
Debug Configurationのウィンドウを開き、”C/C++ Application”カテゴリを選択し、左上の新規作成のボタンを押して構成を作成します。
作成した構成を選択し、画面中央部分の、”Using GDB(DSF) Create Process Launcher “の部分のリンクできる場所をクリックします。新しいウィンドウが開き、以下の図に示すように “Standard Create Process Launder”を選択します。
このウィンドウを閉じて、設定を再開します。
Mainタブの部分では、デバッグ対象の .so 名が表示されています。これを修正します。
プロジェクトのパス内の obj/local/armeabi の中にも同様の .so が作成されています。
この場所で、後ほど作成される app_process というファイルを使用するのでこれに変更します。
→ “obj/local/armeabi/app_process”
続いて、Debuggerタブを開きます。
Debuggerが gdb/mi になっているので、 gdbserver に変更します。
GDB debugger の部分には、android-ndkに入っていたgdbを選択させます。
→ (NDKの配置先 ex:android-ndk-r6b)toolchainsarm-linux-androideabi-4.4.3prebuiltwindowsbinarm-linux-androideabi-gdb.exe
GDB command file の部分には後ほど作成する gdb2.setup を設定します。
これも obj/local/armeabi の中にできあがる予定となっています。
このDebugger Optionsの中のConnection タブを開きます。
この中の Type を TCP に変更します。そしてPort number を 5039 に変更します。忘れやすいので注意
ここまでの設定の画像を一応張っておきます。こんな感じになっているでしょうか。
次に、Android NDKに含まれている ndk-gdb のファイルを修正します。
とりあえず、ndk-gdbはコマンドではなく単にスクリプトなので、コピーしてから編集します。
このファイルを ndk-gdb-eclipse を名前を変えてコピーします。コピー後、適当なテキストエディタで開いて、末尾付近のコードを修正します。
$GDBCLIENT -x `native_path $GDBSETUP`
となっている部分を、以下のようにしてコメントアウトにしておきます。(先頭に # を入れるだけ)
# $GDBCLIENT -x `native_path $GDBSETUP`
さきほど後で作成するといった各ファイルをここで準備します。作成するためにまずJavaのコードで、.soがロードされたあたりでブレークポイントを張ります。前回のサンプルならば、onCreateにはいった直後あたりでとめます。この設定をしてから、MyApplicationをデバッグ実行します。
この止めてある状態で、cygwinを起動し、このプロジェクトのある場所までカレントディレクトリを移動します。
そこで、ndk-gdb-eclipseコマンドを実行します。 エラーがなければしばらくしてコマンドが終了します。
このときに、obj/local/armeabi ディレクトリをのぞいてみると、app_process, gdb.setup というファイルができあがっています。これをデバッグに使用していきます。
app_processはそのまま使用するので放置、ここでは gdb.setup を gdb2.setup としてコピーします。
コピー後、適当なテキストエディタで開き、 最終行付近にある “target remote :5039” という部分を削除して保存し直します。
ここまで準備ができたら先ほどデバッグ実行したコードは終了し、cygwinのウィンドウも終了させておきます。
デバッグの確認
さて、準備が整ったので、ソースコードレベルでバッグの動作確認をしてみます。
前回のコードよりもう少しC/C++のコードを追加して、変数の値が見れることやブレークポイントの設定が可能であることをみていきましょう。
ソースコードの変更
デバッグの効果をみてみたいので、ソースコードを変更します。
#include#include #include static int GlobalCounter = 0; extern "C" jstring Java_org_sample_MyApplicationActivity_helloFromJNI( JNIEnv* env, jobject thiz ) { char buf[ 256 ]; GlobalCounter++; sprintf( buf, "Hello from JNI(%d)", GlobalCounter ); return env->NewStringUTF( buf ); }
デバッグの方法
このように変更してアプリケーションをビルドします。
Javaの部分の onCreateにはいった直後あたりでブレークポイントを設定し、デバッグ実行を行います。
ブレークポイントで停止したら、cygwinを開き、プロジェクトのディレクトリまでカレントディレクトリを移動させて、ndk-gdb-eclipse コマンド(スクリプト)を実行します。
問題なく実行完了したら、Eclipseで 先ほど作成した C/C++用のデバッグ構成 で実行します。
(Debug As などで ウィンドウを開いて、C/C++ Application内の構成を選択して、Debugボタンを押します)
gdbデバッガを起動させている途中で、エラーが出ると思いますが、これは無視して先に進むことが可能です。エラーの内容としては main関数でstopする予定だったのに、その関数がないよ、という意味合いのものです。
“Target request failed: Parsing Error” というメッセージで出てくるのでどきっとしますね・・・。
うまくいっていると、Consoleウィンドウの部分に gdb が起動していることが見て取れます。
ここまでくると、C/C++のソースコードにブレークポイントを張ることが可能になります。さきほどのソースコードで, GlobalCounter++ としている部分にブレークポイントを設定し、onCreateで止めていた実行を再開させてみます。
すると、 GlobalCounter++ のブレークポイントまで実行され、停止します!
GlobalCounterの値が0であったり、env, thiz の値がなにやらとれていたり、コールスタックを確認することができたり。ソースコードレベルでのデバッグが可能となっていることが確認できると思います。
この先、ステップ実行(Step Over)をして、bufの値を確認できるところまでやってみましょう。
ステップ実行ではgdbが裏でガリガリと動いている様子がconsoleのウィンドウを見ても確認できると思います。それゆえ、結構重い処理となっています。return文のところまできたら、bufに値が格納されているのが無事確認できると思います。
まとめ
2回ほどの記事で、AndroidのNDK、C/C++の開発コードにおけるネイティブソースコードレベルデバッグの方法を説明してきました。これにより、今まではprintf文か、コンソールでgdbを実行しながら動作を確認する方法しかなかったのが、GUI上でソースコードを見ながらデバッグすることが可能となりました。
VisualStudioではWinGDBを入れれば今回のことと同様のことができますが、今説明した方法はすべてフリーの環境で構築されています。動作が遅い点はありますが、これはこれで非常に使えるものだと思っています。
既知の問題点とか
ステップインの実行がなにやら怪しいです。eclipseのデバッグ仕様なのか、引数評価、関数ジャンプのための準備、という単位でステップ実行をしているようです。そのため、関数の中に入りたいタイミングでステップインのボタンを押しても、なかなか目的の関数に入れないということが起こります。
対処法は、目的の関数に到達するまでステップインを実行し続ける、ということになります。あるいは、目的の関数先頭にブレークポイントを設定してしまい、実行を再開させる方法でもよいと思います。
フリーな環境構築ですし、ある程度不便なのは仕方ないのかもしれません。
次回以降のデバッグの方法
同じプロジェクトの2度目以降のデバッグ方法は以下の方法でできます。ちょっとは手間ですが、初回ほど大変ではありません。
- ビルドし直した後、onCreateで停止させる
- ndk-gdb-eclipse を起動させる
- Eclipseで C/C++用のデバッグ構成でデバッグを開始させる
- onCreateで止めていた実行を再開させる
別のプロジェクトの場合には、C/C++用のデバッグ構成を作成するあたりから、設定が必要になります。
参考文献
ここまで到達できるのに、いくつか参考にしたページがあります。この情報がなければここまで便利に使えるようにならなかったと思います。