ブレークポイントを設定する
JVMTIを使ってJVMエージェントを作ってみる その1、 JVMTIを使ってJVMエージェントを作ってみる その2に引き続き、今度はmainメソッドの先頭にブレークポイントを設定して、その時点のスレッドを表示してみよう。ポイントはClassPrepareの中でSetBreakpointを使って、Breakpoint関数をmainメソッドの先頭(インデックス:0)のイベントハンドラとして登録しているところ。
ブレークポイントを設定するソースコード
jvmtitest.c#include <stdio.h> #include <string.h> #include <jvmti.h> static jvmtiEnv *jvmti = 0; static void JNICALL ClassPrepare(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread, jclass klass); static void JNICALL Breakpoint(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread, jmethodID method, jlocation location); static jstring getClassName(JNIEnv *env, jclass klass); JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { (*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION); jvmtiCapabilities capabilities = { 0 }; capabilities.can_generate_breakpoint_events = 1; (*jvmti)->AddCapabilities(jvmti, &capabilities); jvmtiEventCallbacks callbackTable = { 0 }; callbackTable.ClassPrepare = ClassPrepare; callbackTable.Breakpoint = Breakpoint; (*jvmti)->SetEventCallbacks(jvmti, &callbackTable, sizeof(callbackTable)); (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL); (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_BREAKPOINT, NULL); printf("agent is loading...\n"); return 0; } JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) { (*jvmti)->DisposeEnvironment(jvmti); printf("agent is unloading...\n"); } static void JNICALL ClassPrepare(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread, jclass klass) { jstring strObj = getClassName(jni_env, klass); const char *str = (*jni_env)->GetStringUTFChars(jni_env, strObj, NULL); if (strcmp(str, "local.Main") == 0) { printf("local.Main prepared.\n"); jmethodID mid = (*jni_env)->GetStaticMethodID(jni_env, klass, "main", "([Ljava/lang/String;)V"); (*jvmti_env)->SetBreakpoint(jvmti_env, mid, 0); } (*jni_env)->ReleaseStringUTFChars(jni_env, strObj, str); } static void JNICALL Breakpoint(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread, jmethodID method, jlocation location) { printf("breakpoint.\n"); jint tc; jthread *threads; (*jvmti_env)->GetAllThreads(jvmti_env, &tc, &threads); for (int i = 0; i < tc; i++) { jvmtiThreadInfo info; jthread thd = threads[i]; (*jvmti_env)->GetThreadInfo(jvmti_env, thd, &info); printf("%s:%d\n", info.name, info.is_daemon); (*jvmti_env)->Deallocate(jvmti_env, (void*)info.name); (*jni_env)->DeleteLocalRef(jni_env, thd); } (*jvmti_env)->Deallocate(jvmti_env, (void*)threads); } static jstring getClassName(JNIEnv *env, jclass klass) { jclass cls = (*env)->GetObjectClass(env, klass); jmethodID mid = (*env)->GetMethodID(env, cls, "getName", "()Ljava/lang/String;"); jstring strObj = (jstring)(*env)->CallObjectMethod(env, klass, mid); return strObj; }
Breakpoint関数ではGetAllThreadsを使って、JVM中の全てのスレッドを取得して、各スレッドの情報をGetThreadInfoを使って取得している。
コンパイル&実行
これをコンパイルして、実行すると以下になる。※local/Main.javaの内容は
$ gcc -Wall -std=c99 -shared -fPIC -D_REENTRANT -o libjvmtitest.so -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux jvmtitest.c $ java -agentpath:./libjvmtitest.so local.Main agent is loading... local.Main prepared. breakpoint. Signal Dispatcher:1 Finalizer:1 Reference Handler:1 main:0 Hello, JVMTI in Java. agent is unloading...
mainメソッド呼び出し時にすでにmain以外に"Signal Dispatcher","Finalizer","Reference Handler"という3つのデーモンスレッドが起動していることがわかる。
0 件のコメント:
コメントを投稿