Java - JNI - C/C++ デバッグ入門

明けましておめでとうございます。今年も宜しくお願いします。



というわけで昨年末に調べていたJNIプログラムデバッグ方法のまとめ。

これが一番参考になった。Debugging integrated Java and C/C++ code

やりたい事

  • JNIプログラムのデバッグをしたい(javaからJNI経由で呼ばれたC/C++プログラムのデバッグをしたい)
  • CUI環境で完結したい(対象マシンはサーバで遠隔地にありGUIは使用不可)

まとめ

こんな感じ。

  • javac -g
  • gcc -g
  • JPDA(java)
  • jdb -attach
  • gdb --pid=

※JPDAはJava Platform Debugger Architectureの略、JVMの機能の一つ。jdbはJDK付属のJava用デバッガ。gdbはいつものGNU Debugger。

senna-javaでの例

JavaプログラムとC/C++プログラムをデバッグビルド。

Javaプログラムではantを使っているのでbuild.xmlにて設定。

  <property name="debug" value="true" />

C/C++プログラムはMakefileを設定。

OPT = -Wall -fPIC -g

リビルド

ant clean test

JPDAを使ってjavaを実行。

LD_LIBRARY_PATH="." \
java -Xdebug -Xnoagent -Djava.compiler=none \
  -Xrunjdwp:transport=dt_socket,server=y,suspend=y \
  -cp "lib/junit.jar:senna-java-0.01.jar:classes:tests" \
  junit.textui.TestRunner senna.SnippetTest

こんな感じで待ちうけポート番号が示される。

Listening for transport dt_socket at address: 34436

jdbを実行。ポート番号を指定する。

tritonn@dev32:/srv/senna-java/trunk$ jdb -sourcepath "src/java" -attach 34436
uncaught java.lang.Throwable を設定しました
保留した uncaught java.lang.Throwable を設定しました
jdb の初期化中です...
>
VM が起動しました: 現行の呼び出しスタックにはフレームがありません

main[1]

まだjavaプログラムが開始されていない = System.loadLibrary()が呼ばれていない = C/C++プログラム(共有ライブラリ)もロードされていない状態。

javaプログラム側の良さそうな場所にjdbでブレークポイントを入れる。

main[1] stop in senna.SnippetTest.testSnippet
ブレークポイント senna.SnippetTest.testSnippet を保留しています。
クラスがロードされた後に設定されます。
main[1]

そこまで実行。設定したところで停止する。

main[1] cont
> 保留した ブレークポイント senna.SnippetTest.testSnippet を設定しました

ブレークポイントのヒット: "スレッド=main", senna.SnippetTest.testSnippet(), line=40 bci=0

main[1]

gdbjavaプロセスにアタッチ。

tritonn@dev32:/srv/senna-java/trunk/src/jni$ gdb --pid=`pgrep -x java`
GNU gdb Red Hat Linux (6.3.0.0-1.143.el4rh)
Copyright 2004 Free Software Foundation, Inc.
..(中略)...
Loaded symbols for /lib/libgcc_s.so.1
0x006367a2 in _dl_sysinfo_int80 ()
   from /lib/ld-linux.so.2
(gdb)

System.loadLibrary()が呼ばれた後なら、ここで自由にbreakpointを設定できる。(呼ばれる前にも"予約"は可能ですが)

(gdb) b Java_senna_Snippet_exec
Breakpoint 1 at 0xb39b259e: file senna_Snippet.c, line 107.
(gdb) c
Continuing.

breakpointを設定したらcontinueしておく。

jdb側でもcontinueする。

main[1] cont
>

gdb側のbreakpointがヒット。後はお好きに。

(gdb) c
Continuing.
[Switching to Thread -1208027456 (LWP 648)]

Breakpoint 1, Java_senna_Snippet_exec (env=0x8057b0c, obj=0xbfffcec4, jstr=0xbfffcec0)
    at senna_Snippet.c:107
107         snip = this_sen_snip(env, obj);
(gdb) list
102         sen_rc rc;
103         jclass string_class;
104         jobjectArray array;
105         char **results;
106
107         snip = this_sen_snip(env, obj);
108         string = (*env)->GetStringUTFChars(env, jstr, NULL);
109         string_len = (*env)->GetStringUTFLength(env, jstr);
110         SEN_LOG(sen_log_debug, "sen_snip_exec: snip=%p, string=%s, string_len=%d",
111                 snip,string,string_len);
(gdb)

※もっと手軽な方法を募集中です(CUI完結可能なものに限る)