JVMTIを使ってみる。


初歩的なところで詰まってしまってなかなか先に進むことが出来ていなかったんですが、ひとまずやっと動いてくれましたので忘れる前に、自分の頭の整理も兼ねてエントリーを。

今回の環境

JVMTI(Java Virtual Machine Tool Interface)

Java仮想マシン上のプログラムの実行状態を取得する事ができるインタフェースなんだとか。詳しくは公式のドキュメント*1を。ちなみに、J2SE等に標準で付随しているhprofもこの機能を利用しているんだとか。最終的な目標としては、内部の情報を逐一取り出したいんですが、それは今後のお話ということで。

エージェントの作成

JVMTIを使用するためには、C又はC++で記述したエージェントをVMの起動時以前に呼び出しておくことで、VMの情報を取得出来るようです。ここでは、実際に何かを取得するのではなく、起動時にエージェントが問題なく呼び出せているのかどうかの確認のために最も初歩的なエージェントを作成します。
今回はこちらのサイト*2のエージェントを利用させて頂きました。

  • agent.c
/**
 * HelloAgent.c
 * 極簡単JVMTIエージェント
 */
#include <jvmti.h>
#include <stdio.h>

/**
 * JVMTIエージェントが含まれたライブラリがロードされた際に呼び出される。
 * options に'abort'が指定されると、JNI_ERRを返却する
 */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* \
    reserved) {
  printf("Hello, JVMTI. option '%s' specified\n", options);
  if (options != NULL && strcmp("abort", options) == 0) {
    return JNI_ERR;
  } else {
    return JNI_OK;
  }
}

/**
 * JavaVMが終了する際に呼び出される。
 */
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM* vm) {
  printf("Good-bye, JVMTI.\n");
}

$ gcc -g -Wall -fPIC -c agent.c -I /usr/lib/jvm/java-6-oracle/include/ -I /usr/lib/jvm/java-6-oracle/include/linux/
agent.c: 関数 ‘Agent_OnLoad’ 内:
agent.c:15:3: 警告: 関数 ‘strcmp’ の暗黙的な宣言です [-Wimplicit-function-declaration]
$ gcc -shared -o agent.so agent.o

これは本当になんでもいいのですが一番シンプルな定番なHelloWorldを用意し、コンパイルしておきます。

public class Hello {
 public static void main(String[] args) {
  System.out.println("Hello World!");        
 }
}

実行

$ java -agentpath:/"絶対パス"/agent.so Hello
Hello, JVMTI. option '(null)' specified
Hello World!
Good-bye, JVMTI.

$ java -agentpath:/"絶対パス"/agent.so=bastard Hello
Hello, JVMTI. option 'bastard' specified
Hello World!
Good-bye, JVMTI.

サンプルのエージェントでは引数あり・なしによる挙動の変化を試すことが出来るようになっていましたので、2回実行しています。

おわりに

まだまだ何か出来たという訳ではないんですが、ひとまず動いたということで今後のためにも書き残しておきたいと思います。