JVM Tool Interfaceを少しさわってみたメモ
ふと、JVM(TM) Tool Interface 1.2.3 に触れたことがないことに気がつき、少しさわってみたのでメモ。
OpenJDKのソースコードの jdk/src/share/demo/jvmti の下に幾つか例があるので、それを試してみることに。ちなみに概要は上記URLのドキュメントの各APIの説明の直前まで眺めれば良さそう。APIの種類が多いので少し圧倒されるけど、実際に何か作る際に目的に合ったものを見れば問題無いかと。
今回見てみたのは versionCheck / mtrace の二つ。
- versionCheck
Agent_OnLoad()でJVMTI_EVENT_VM_INITイベントにコールバック関数をセットして、その中でjvmtiEnvから引っこ抜いたバージョン番号をstdoutに表示するだけのシンプルなもの
Mac OS X上でのビルドは以下のような感じ。ちなみにagent_util/agent_util.cはjdk/src/share/demo/jvmtiが依存しているヘルパー関数群。
$ pwd /Users/komamitsu/src/openjdk/jdk/src/share/demo/jvmti/versionCheck $ clang -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin -I../agent_util -c ../agent_util/agent_util.c *.c $ clang -dynamiclib -L${JAVA_HOME} -o libversionCheck.so *.o
これを利用する場合は以下のように指定する
$ java -agentpath:/Users/komamitsu/src/openjdk/jdk/src/share/demo/jvmti/versionCheck/libversionCheck.so Main Compile Time JVMTI Version: 1.2.1 (0x30010201) Run Time JVMTI Version: 1.2.3 (0x30010203) Hello world
- mtrace
Javaのメソッド呼び出し回数をトレースするもの。Agent_OnLoad()で色々コールバックを設定するが、面白いのはJVMTI_EVENT_CLASS_FILE_LOAD_HOOKで都度ロードされたクラスファイルの各メソッドを、同梱されているMtrace.javaのメソッドを最初に呼ぶように書き換えている(と思う)。Mtrace.javaのメソッドは当該Cライブラリファイルの関数(この中でメソッド呼び出し回数をカウント)をnative methodとして呼ぶように紐づけられているが、その際、JNIのFindClass()とRegisterNative()を使っている。ちなみにjava_crw_demo/java_crw_demo.c はクラスファイル操作系ヘルパー関数群。
$ pwd /Users/komamitsu/src/openjdk/jdk/src/share/demo/jvmti/mtrace $ clang -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin -I../agent_util -I../java_crw_demo -c ../agent_util/agent_util.c ../java_crw_demo/java_crw_demo.c *.c $ clang -dynamiclib -L${JAVA_HOME} -o libmtrace.so *.o $ mkdir -p classes $ javac -d classes Mtrace.java $ jar cf mtrace.jar *
使い方で少しハマった点としては、FindClass()でMtrace classを探す際、ロードされる前に探しに行くとJVMがクラッシュしてしまう(FindClass()の戻り値がNULLになることを期待してたけど...)。なので -Xbootclasspath/a でmtrace.jarを先にロードする必要あり
$ java -Xbootclasspath/a:/Users/komamitsu/src/openjdk/jdk/src/share/demo/jvmti/mtrace/classes/mtrace.jar -agentpath:/Users/komamitsu/src/openjdk/jdk/src/share/demo/jvmti/mtrace/libmtrace.so Main : Class java/lang/String 8084 calls Method charAt (I)C 3941 calls 3941 returns Method length ()I 832 calls 832 returns Method equals (Ljava/lang/Object;)Z 543 calls 543 returns :
なお、途中で、調査のため-Xcheck:jni -verboseを有効にしたら捗った。特に-verbose指定時に以下のエラーが出たのでJDKのバージョン違いに気がつけた(途中JDK8 -> 7にして試してた)
[Loaded java.lang.ClassFormatError from /Library/Java/JavaVirtualMachines/jdk1.7.0_45.jdk/Contents/Home/jre/lib/rt.jar] [Loaded java.lang.UnsupportedClassVersionError from /Library/Java/JavaVirtualMachines/jdk1.7.0_45.jdk/Contents/Home/jre/lib/rt.jar]