NVIDIA Nsight Tegra 検証の第2弾です。
前回はほぼデフォルトで作成される状態のサンプルプログラムを実行させての動作確認だったので、今回はもう少し実際にあり得るケースで正常にデバッグが出来るのか、を試してみたいと思います。
今回のケース想定
2つのスタティックライブラリを使用して、.so を作成し、Javaから利用する。
要は libXXXX.a を複数リンクして、アプリケーション本体となる.soを作成、Javaはその呼出までの役割に過ぎない、というケースです。
ほぼ定番のケースだと思います。これで実用になるのか、試してみたいと思います。
testTwoLibs <-- ここにVCのソリューションを配置 + lib1 + lib2 + AppMain
こんなフォルダ構成で作成します。AppMainは Java+Native の「Android Application with Native code」タイプで作成します。
NVIDIA Nsightでスタティックライブラリ用プロジェクトはどう作成するか、ここが問題です。
現時点では、ウィザード等では設定できないようだったので、やや強引ですが既存のプロジェクトタイプで作成した後、設定を変更して対応することにします。
スタティックライブラリ用プロジェクトの作成方法
最初のプロジェクト作成で 「Android Native Application」を選択します。これはNativeActivity用の設定ですが、
ファイルの削除と設定変更だけで割と簡単にネイティブのスタティックライブラリ用に出来るので利用させて貰うことにします。
パッケージ名を聞かれますが、スタティックライブラリには不要なので適当に入力しておきます(例:org.aaa)。
プロジェクトが作成できたら、下記画面の通り、native_app_glue 関連のコードを削除し、デフォルトで作成される main.c の名前を変更しておきます。また、main.c の中身も全部削除して、動作検証用のコードにしておきます。
すでに既存のソースコードを持っているなら、main.cを削除して、それを登録しておくのでも問題ないでしょう。
そして、出力するタイプを "Make Application" から "Static Libaray(.a)" に変更します。
ここまでの作業を同じように繰り返して、lib2を作成します。
lib1のソースコード内容例
#includeextern "C" int mylib1Test( int x, int y ) { int r = 0; r = x * 10 + 5 * y; return r; } extern "C" void mylib1Test2( char* s, const char* a, const char* b ) { sprintf( s, "a = %s, b = %b", a, b ); }
lib2のソースコード内容例
#includeextern "C" float mylib2Test( int a, int b, int c ) { float r = c; r = r * a + 2.0f * b; return r; }
続いて、「Android Application with Native Code」タイプで AppMain を作成します。
AppMain.java の onCreateメソッド内で、既に作成されている appmainNative() を呼び出すようにしておきます。
その後、AppMainのネイティブコード部分(AppMain.c)で、今用意したlib1,lib2の関数を呼出して、ブレークポイントがはれるかを確認してみます。
作成したら、AppMainのプロジェクトをスタートアッププロジェクトに指定しておきます。
AppMain.cのコード内容例
/********************************** Java Native Interface library **********************************/ #include#include #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "testTwoLibs", __VA_ARGS__)) int mylib1Test( int x, int y ); void mylib1Test2( char* s, const char* a, const char* b ); float mylib2Test( int a, int b, int c ); void Java_org_sample_twoappmain_AppMain_appmainNative( JNIEnv* env, jobject thiz ) { int t0, t1, r0; float r1; char buf[128]; char* a = "Foo"; char* b = "Bar"; t0 = 10; t1 = 4; r0 = mylib1Test( t0, t1 ); mylib1Test2( buf, a, b ); LOGI( buf ); r1 = mylib2Test( t0, t1, r0 ); }
コードを作成したら、プロジェクトの設定を開いてで liblib1.a と liblib2.a をリンクするように修正します。
今回のフォルダ構成では、testTwoLibsフォルダ以下に Android/Debug というフォルダが作成され、この中に .a のファイルが出力されるようになっています。
"構成プロパティ" / Linker / Additional Library Directories の部分にこのフォルダを指定します。
そして、リンクするライブラリの指定は、"構成プロパティ" / Linker / Additional Dependencies の部分に指定します。
このときに、従来のVCではライブラリファイル名を設定していましたが、Nsight Tegraの場合では、-l"(ライブラリ名)" と指定するようです。(このやり方がわかる以前は、CommandLineで直接設定してました・・・)
うまくいかない場合には、CommandLineで直接指定するなどで対処できそうです。
gcc環境であることもポイントで、ライブラリファイル名が、libhoge.a の場合、上記のライブラリ名では hoge と指定する点に注意してください。
今回の例では、-l"lib1" -l"lib2"を指定しておきます。
続いて、ブレークポイントを下記のように設定しておきます。今までの経験から Java - C の接続部である上記のコードにブレークポイントが張れて機能することはわかっているので、次の2点を確認したいと思います。
- 外部ライブラリへのステップインは出来るか?
- 直接外部ライブラリのブレークポイント設定は有効になるか?
ここで、外部ライブラリとは AppMain.so が利用している .a のファイルを指しています。
実行して、上記の1つめのブレークポイントで停止した後は、ステップインで lib1 のソースコード内へ突入できるかを確認してみます。
F5で実行して、mylib1Test関数にステップイン(F11)してみると、下記のように正常にステップインできました。
さらにこの後、F5で実行を継続させて様子を見ていると、下記のように正常に lib2 の関数内でブレーク発動できました。
まとめ
複数のスタティックライブラリを使って、soファイルを生成するタイプのアプリケーション開発では、今回試した内容からほぼストレス無く開発が出来るのでは無いかと思います。
また、記事で詳細に触れてはいないですが、lib1,lib2をソリューションから除外して、AppMainだけをリビルドして同様のことを試してみたところ、こちらも正常に関数内へステップインできました。
Nsight Tegraの出来の良さに感激です。
気になった点とか躓いた点とか
上記のテストで、アプリケーションが起動できない、妙な動きをするような場合は以下の点を確認してみてください。
自分でも陥ったポイントです。他にも発見&解決できた人は是非コメントくださいませ。
アプリケーションが起動できない(onCreate以降すすまないっぽい)
純粋にApplication with Native Code のプロジェクトを作成して、それが起動できるかどうかを確認。うまくいけばそれ以降の部分で問題があることになります。この後確認するのは lib1, lib2 が 正しく .a を出力する設定になっているかどうか、です。
自分の場合には、変更することを忘れて、ここを .so のままにしていたことがありました。このとき問題発生でアプリケーション終了という流れで、すごく悩みました。
他には、スタートアッププロジェクトをlib1やlib2のままになっていないかを確認。元々がNativeActivity用のプロジェクトを流用しているので、実は実行させようとすればできちゃうようで・・・。