在JAVA单元测试中,需要监控单元测试的覆盖率,记录JAVA代码的执行过程,或者是需要动态插装字节码的应用程序中(例如AOP/IOC技术, 动态插装字节码的技术我将在下一篇的博客中介绍),我们可以使用JVM TI接口进行监控,本程序介绍一种监控JAVA代码的方法,注意:本程序不适合代码量很大的项目,会严重影响程序性能,如果需要开发高性能的监控代码,请参考我的另一篇博客
#include "stdafx.h" #include <jni.h> #include <jni_md.h> #include "windows.h" #include <jvmti.h> #include <string> #include <cstring> #include <iostream> #include <list> #include <map> #include <set> #include <stdlib.h> #include <jni_md.h> int _tmain(int argc, _TCHAR* argv[]) { return 0; } typedef jint (WINAPI *_CreateJavaVM)(JavaVM **, void **, void *); static jvmtiEnv *gb_jvmti = NULL; static jvmtiCapabilities gb_capa; static jrawMonitorID gb_lock; static void enter_critical_section(jvmtiEnv *jvmti) { jvmtiError error; error = jvmti->RawMonitorEnter(gb_lock); } static void exit_critical_section(jvmtiEnv *jvmti) { jvmtiError error; error = jvmti->RawMonitorExit(gb_lock); } static void JNICALL callbackException(jvmtiEnv *jvmti_env, JNIEnv* env, jthread thr, jmethodID method, jlocation location, jobject exception, jmethodID catch_method, jlocation catch_location) { enter_critical_section(gb_jvmti); { char *name,*sig,*gsig; jvmtiError error = gb_jvmti->GetMethodName(method, &name, &sig, &gsig); if (error != JVMTI_ERROR_NONE) { printf("ERROR:GetMethodName!\n"); return; } printf("In Agent: Got an exception from Method: %s\n" ,name ); // if(strcmp(name,"main")==0) // { // } } exit_critical_section(gb_jvmti); } JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { jvmtiError error; jvmtiEventCallbacks callbacks; jint result = jvm->GetEnv((void **) &gb_jvmti, JVMTI_VERSION_1_0); if(result != JNI_OK || gb_jvmti==NULL) { printf("ERROR: Unable to access JVMTI!"); return JNI_ERR; } memset(&gb_capa,0,sizeof(jvmtiCapabilities)); gb_capa.can_signal_thread = 1; gb_capa.can_get_owned_monitor_info = 1; gb_capa.can_generate_method_entry_events = 1; gb_capa.can_generate_exception_events = 1; gb_capa.can_generate_vm_object_alloc_events = 1; gb_capa.can_tag_objects = 1; //error = gb_jvmti->AddCapabilities(&gb_capa); /* if(error != JVMTI_ERROR_NONE) { printf("ERROR: Can't get JVMTI capabilities"); return JNI_ERR; }*/ jclass *classes; jint count; error = gb_jvmti->GetLoadedClasses(&count, &classes); if (error) { printf("ERROR: JVMTI GetLoadedClasses failed!\n"); } for (int i = 0; i < count; i++) { char *sig; gb_jvmti->GetClassSignature(classes[i], &sig, NULL); if(strcmp(sig,"com/test/VMAttacher")>0) { printf("cls sig=%s\n", sig); } } memset(&callbacks,0,sizeof(jvmtiEventCallbacks)); callbacks.Exception = &callbackException; error = gb_jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks)); if(error != JVMTI_ERROR_NONE) { printf("ERROR: Can't set jvmti callback!"); return JNI_ERR; } error = gb_jvmti->CreateRawMonitor("agent data", &gb_lock); if(error != JVMTI_ERROR_NONE) { printf("ERROR: Can't create raw monitor!"); return JNI_ERR; } // error = gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_VM_INIT, (jthread)NULL); error = gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_EXCEPTION, (jthread)NULL); return JNI_OK; } JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) { // }
将以上代码编译DLL控件后,再使用javapath或者javalib参数指定该DLL控件作为JAVA Agent启动即可