読者です 読者をやめる 読者になる 読者になる

DRY

Web関連の技術の事、食事/お酒の事、旅行の事など

Eclipseでandroid-ndk-r6のJNIをgdbを使ってデバッグする方法

Android

やっぱりマジメなJNI作ってるとブレークポイント張って中見ないとサッパリワカラナイんですよね。
でも、あんまりEclipseでやってるエントリが見つけられなくて、苦労したのですがJNI側のブレイクポイントまで飛んできたので方法をメモしておきます。

# 環境
Windows7
Eclipse 3.6 Helios
Cygwin
android-ndk-r6

プロジェクトは前回CDTを統合した時につかったプロジェクトをまんま使用します。
まずは該当プロジェクトの「AndroidManifest.xml」で「Debuggable」を忘れずに「true」にします。


ツールバーの「デバッグの構成」⇒「C/C++ Application」を選択し、開いているダイアログの左上の新規ボタンを押下します。


まずは「メイン」タブです。
名前は「〜デフォルト」だと分かり難いので、「IntegrateJni NDK」としました。
プロジェクトが該当のプロジェクトである事を確認します。
ビルド構成を「Use Active」に、下のラジオを「Disable auto build」に設定します。

Standard Create Process ランチャーの部分で「その他の選択」を押し、「構成固有の設定を使用」をチェックし「Standard Create Processランチャー」を選択します。


次に「デバッガー」タブですが
GDBデバッガーにはNDKの下にある「arm-linux-androideabi-gdb.exe」を指定します。私の場合は「C:\ndk\toolchains\arm-linux-androideabi-4.4.3\prebuilt\windows\bin\arm-linux-androideabi-gdb.exe」になります。

GDBコマンド・ファイルには「obj\local\armeabi」の下にある「gdb.setup」を設定しますが、実はこのファイルの中身を書き換えないと動かない&このファイルは都度上書きされてしまうので、「gdb2.setup」とします。
※この時点で実体が有る・無しは関係ないようです。

ですので、私は「D:\Develop\Eclipse\IntegrateJni\obj\local\armeabi\gdb2.setup」のように指定しました。
後、Windowsユーザは「ブレークポイントを設定するには絶対ファイル・パスを使用する」にチェックを入れてはいけないようなので注意して下さい。


で次に真ん中の「接続」タブをクリックし
型をTCPに、
ポートを5039に設定します。

これで、一旦設定をOKにします。

※実際また設定しないといけないのですが、何度やってもこの手順でしか必要なファイルが生まれないんです。
「app_process」というファイルなんですが、一度「ndk-gdb」をしないとどうしても出来ないんですね。
なので、あんまり正しい手順な気がしないのですが。。。
どなたか詳しい方、ご指摘頂けるとありがたいです。


NDKの直下にある、「ndk-gdb」の編集をします。
私のバージョンだと一番最後にある
$GDBCLIENT -x `native_path $GDBSETUP` をコメントアウトします。
##$GDBCLIENT -x `native_path $GDBSETUP`
(このファイルのバックアップは取っても取らなくても構いません)


ここで一度起動します。
でとりあえずループとかメンドイので、calc_javaのループを10回にしてcalc_nativeの頭でブレークポイントを張って起動します。

ここでブレークポイントを張るのを忘れずに行って下さい。
到達後にndk-gdbを立ち上げますので。


package jp.k16.integratejni;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;


public class IntegrateJni extends Activity
{
static
{
System.loadLibrary ("calculate");
};

public native long calculate ();

@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

calc_java ();
calc_native ();
}

private void calc_java ()
{
long start = System.currentTimeMillis ();
long count = 0;
//for(int i = 0; i < 100000000;i++)
for(int i = 0; i < 10;i++)
{
count++;
}
long stop = System.currentTimeMillis ();
Log.d ("TAG", "calc_java = " + String.valueOf (stop - start));
}

private void calc_native ()
{
long start = System.currentTimeMillis ();
calculate ();
long stop = System.currentTimeMillis ();
Log.d ("TAG", "calc_native = " + String.valueOf (stop - start));
}
}


Java側でブレークポイントに到達した時点で、Cygwinを起動して該当プロジェクトのTOPにcdします。
私の場合は「/cygdrive/d/Develop/Eclipse/IntegrateJni」です。

それでここで一度ndk-gdbします。--verbose入れるとログが出て分かりや易いです。
そうすると下記のようなログが出ると思います。

$ ndk-gdb --verbose
Android NDK installation path: /cygdrive/c/ndk
Using default adb command: /cygdrive/c/bin/android-sdk-windows/platform-tools/ad
b
ADB version found: Android Debug Bridge version 1.0.26
Using final ADB command: '/cygdrive/c/bin/android-sdk-windows/platform-tools/adb
'
Using auto-detected project path: .
Found package name: jp.k16.integratejni
ABIs targetted by application: armeabi
Device API Level: 10
Device CPU ABI: armeabi
Compatible device ABI: armeabi
Found debuggable flag: true
Found device gdbserver: /data/data/jp.k16.integratejni/lib/gdbserver
Using gdb setup init: /cygdrive/d/Develop/Eclipse/IntegrateJni/libs/armeabi/gdb.
setup
Using toolchain prefix: /cygdrive/c/ndk/toolchains/arm-linux-androideabi-4.4.3/p
rebuilt/windows/bin/arm-linux-androideabi-
Using app out directory: /cygdrive/d/Develop/Eclipse/IntegrateJni/obj/local/arme
abi
Found data directory: '/data/data/jp.k16.integratejni'
Found running PID: 503
Launched gdbserver succesfully.
Setup network redirection
## COMMAND: /cygdrive/c/bin/android-sdk-windows/platform-tools/adb forward tcp:5
039 localfilesystem:/data/data/jp.k16.integratejni/debug-socket
## COMMAND: /cygdrive/c/bin/android-sdk-windows/platform-tools/adb shell run-as
jp.k16.integratejni lib/gdbserver +debug-socket --attach 503
Attached; pid = 503
Listening on sockaddr socket debug-socket
## COMMAND: /cygdrive/c/bin/android-sdk-windows/platform-tools/adb pull /system/
bin/app_process D:/Develop/Eclipse/IntegrateJni/obj/local/armeabi/app_process
60 KB/s (5660 bytes in 0.092s)
Pulled app_process from device/emulator.
## COMMAND: /cygdrive/c/bin/android-sdk-windows/platform-tools/adb pull /system/
lib/libc.so D:/Develop/Eclipse/IntegrateJni/obj/local/armeabi/libc.so
222 KB/s (273868 bytes in 1.204s)
Pulled libc.so from device/emulator.
$

で当然Native側にブレークポイントを張っていたとしても飛ばないのですが、ここで
「D:\Develop\Eclipse\IntegrateJni\obj\local\armeabi」の下に「app_process」というファイルが出来ていると思います。

また「D:\Develop\Eclipse\IntegrateJni\obj\local\armeabi\gdb.setup」にポート:5039の設定とおぼしき物が追加されていると思います。
こんな感じです。

set solib-search-path D:/Develop/Eclipse/IntegrateJni/obj/local/armeabi
directory C:/ndk/platforms/android-9/arch-arm/usr/include C:/ndk/sources/cxx-stl/system/include D:/Develop/Eclipse/IntegrateJni/jni
file D:/Develop/Eclipse/IntegrateJni/obj/local/armeabi/app_process
target remote :5039


ここで先ほど指定したgdb2.setupをコピーして作っておきます。
D:\Develop\Eclipse\IntegrateJni\obj\local\armeabi\gdb2.setup
「target remote :5039」の記述を消します。
つまり「gdb2.setup」はこうなります。

set solib-search-path D:/Develop/Eclipse/IntegrateJni/obj/local/armeabi
directory C:/ndk/platforms/android-9/arch-arm/usr/include C:/ndk/sources/cxx-stl/system/include D:/Develop/Eclipse/IntegrateJni/jni
file D:/Develop/Eclipse/IntegrateJni/obj/local/armeabi/app_process

ここで「gdb.setup」の中身はどうでもいいです。


さらに、デバックの構成をもう一度指定し、「メイン」タブの「C/C++ アプリケーション」に先ほど出来上がった「app_process」を指定します。


以上で設定はすべて終わりです。

後は以下の手順で立ち上げます。

1.System.loadLibrary ()が終わっていて、JNI関数が呼ばれる前のJava側でブレークポイントを張る
 ⇒今回私は「calc_native ()」の直後「long start = System.currentTimeMillis ();」に張りました。

2.JNI側にもブレークポイントを張る。これも直後で良いかと。

3.まずはAndroidプロジェクトをいつものように実行する。
 (エミュレータの起動がまだであればしばし待つ)

4.Java側のブレークポイント(1で設定した場所)に到達する

5.Cygwinで該当プロジェクトのTOPに行き、「ndk-gdb 」をたたく。

6.GDBの起動が確認できたら、Eclipseにてデバッグアイコンより先ほど作成した、JNI用のデバッグ構成「Integrate Jni NDK」を起動する
※どうやmain関数が無いと下記のようにエラーがでるが無視で問題ない。
エラーログ的にもそういってるし

42-break-insert -t main
&"Function \"main\" not defined.\n"
Function "main" not defined.

7.これでコンソール画面の動きが止まったら準備完了なので、「F8」などで先に進める

8.そうするとコンソールで.soを読んでる風のログが出て、JNI側のブレークポイントの箇所に到達する


変数タブを見て貰うとわかるように、ちゃんと中身も追えてるよ



と言う訳で中身を見れる環境は整いました。ですが幾つか。。。
●JNI側でステップを進めると都度コンソールが激しく動いて、遅いんだけどこんなもんかな?
●「app_process」はほんとに?って感じなんですが。。。

まあとりあえず出来たから良しとします。
あと、ここに到達するまで結構ndk-gdbでエラー出たのですが、すみません。内容メモるのを忘れてしまいました。
ただ、やっぱり基本は「デバッグの構成」の設定だったはずです。


あと、今回はこちらのサイトに相当お世話になりましたので、リンクを張らせて頂きます。