如何使用jvmti获取方法局部变量和class变量的值

How to Get the values of method local variables and class variables using jvmti

我正在尝试使用 JVMTI 捕获变量值,当生成异常事件时,我浏览了 jvmti 文档,发现没有任何函数可以让我检索字段(变量)的值,如何这可以实现吗?

以下是代理代码:

#include<jni.h>
#include<jvmti.h>
#include<string.h>
#include<stdlib.h>
#include<stdbool.h>
typedef struct {
jvmtiEnv *jvmti;
jrawMonitorID lock;
} GlobalAgentData;

static GlobalAgentData *gdata;

static bool check_jvmti_error(jvmtiEnv *jvmti,jvmtiError errnum,const char *str){
if(errnum != JVMTI_ERROR_NONE){
    char *errnum_str;
    errnum_str = NULL;
    (void)(*jvmti)->GetErrorName(jvmti,errnum,&errnum_str);
    printf("ERROR: JVMTI: %d(%s): %s\n", errnum, 
    (errnum_str==NULL?"Unknown":errnum_str),
    (str==NULL?"":str));
    return false;
   }
   return true;
 }

static void deallocate(jvmtiEnv *jvmti,void *ptr){
jvmtiError error;
error = (*jvmti)->Deallocate(jvmti,ptr);
check_jvmti_error(jvmti,error,"Cannot deallocate memory");
}

static void allocate(jvmtiEnv *jvmti,jint len){
jvmtiError error;
void *ptr;
error = (*jvmti)->Allocate(jvmti,len,(unsigned char **)&ptr);
check_jvmti_error(jvmti,error,"Cannot allocate memory");
}


JNICALL jint objectCountingCallback(jlong class_tag,jlong size,jlong* tag_ptr,jint length,void* user_data){
    int* count = (int*)user_data;
    *count+=1;
    return JVMTI_VISIT_OBJECTS; 
}

JNIEXPORT jint JNICALL Java_Test_countInstances(JNIEnv *env,jclass thisClass,jclass klass){
    int count =0 ;
    jvmtiError error;
    jvmtiHeapCallbacks callbacks;
jvmtiEnv *jvmti;
    (void)memset(&callbacks,0,sizeof(callbacks));
    callbacks.heap_iteration_callback = &objectCountingCallback;
    jvmti = gdata->jvmti;
error = (*jvmti)->IterateThroughHeap(jvmti,0,klass,&callbacks,&count);
//  check_jvmti_error(*gdata->jvmti,error,"Unable to iterate through the heap");
    return count;
}

static void enter_critical_section(jvmtiEnv *jvmti){
jvmtiError error;
error = (*jvmti)->RawMonitorEnter(jvmti,gdata->lock);
check_jvmti_error(jvmti,error,"Cannot enter with raw monitor");
}

static void exit_critical_section(jvmtiEnv *jvmti){
jvmtiError error;
error = (*jvmti)->RawMonitorExit(jvmti,gdata->lock);
check_jvmti_error(jvmti,error,"Cannot exit with raw monitor");
}

static void JNICALL callbackVMInit(jvmtiEnv *jvmti,JNIEnv *env,jthread thread){
jvmtiError error;
//  enter_critical_section(jvmti);{ /* not needed since we are just setting event notifications */
printf("Initializing JVM\n");
error = (*jvmti)->SetEventNotificationMode(jvmti,JVMTI_ENABLE,JVMTI_EVENT_EXCEPTION,(jthread)NULL);
//  error = (*jvmti)->SetEventNotificationMode(jvmti,JVMTI_ENABLE,JVMTI_EVENT_METHOD_ENTRY,(jthread)NULL);
check_jvmti_error(jvmti,error,"Cannot set Exception Event notification");
//  } exit_critical_section(jvmti);
}

static void callbackMethodEntry(jvmtiEnv *jvmti,JNIEnv *env,jthread thread,jmethodID method){
jvmtiError error;
jvmtiLocalVariableEntry **table_ptr;
jint count,entry_count_ptr;
jobject value_ptr;
int j;
error = (*jvmti)->GetLocalVariableTable(jvmti,method,&entry_count_ptr,table_ptr);
if(check_jvmti_error(jvmti,error,"Cannot Get Local Variable table\n")){
     printf("local variable table entry size : %d %s\n",entry_count_ptr,(*table_ptr)[0].name);          
}
// for(j=0;j<*entry_count_ptr;j++){
 //     error = (*jvmti)->GetLocalObject(jvmti,thread,0,(*table_ptr)[j].slot,&value_ptr);
 //        printf("Field Name:%s\n",(*table_ptr)[j].name);
 //    } 
}


static void JNICALL callbackException(jvmtiEnv *jvmti,JNIEnv *env,jthread thread,jmethodID method,jlocation location,jobject exception,jmethodID catch_method,jlocation catch_location){
jvmtiFrameInfo frames[10];
jint count,entry_count_ptr;
int i,j;
jvmtiError error;
jobject* value_ptr;
char *name,*sig,*gsig;
jclass declaring_class_ptr;
jvmtiLocalVariableEntry *table_ptr;


error = (*jvmti)->GetStackTrace(jvmti,thread,0,10,frames,&count);
if(check_jvmti_error(jvmti,error,"Cannot Get Frame") && count >=1){
    char *methodName,*className;
    for(i=0;i<count;i++){
        error = (*jvmti)->GetMethodName(jvmti, frames[i].method,&methodName,&sig,&gsig);
        if(check_jvmti_error(jvmti,error,"Cannot Get method name")){
            error = (*jvmti)->GetMethodDeclaringClass(jvmti,frames[i].method,&declaring_class_ptr);
            check_jvmti_error(jvmti,error,"Cannot Get method declaring class");
            error = (*jvmti)->GetClassSignature(jvmti,declaring_class_ptr,&className,NULL);
            check_jvmti_error(jvmti,error,"Cannot get class signature");
            // printf("Got Exception in  Method: %s at Line: %ld with Signature:%s,%s within Class:%s\n",methodName,frames[i].location,sig,gsig,className);

            for(j=0;j<entry_count_ptr;j++){
                callbackMethodEntry(jvmti,env,thread,frames[j].method);
                error = (*jvmti)->GetLocalObject(jvmti,thread,i,table_ptr[j].slot,value_ptr);// change the value of the slot parameter
                printf("Field Name:%s\n",table_ptr[j].name);
             }

        }
    }
}

/*  error = (*jvmti)->GetMethodName(jvmti,method,&name,&sig,&gsig);
check_jvmti_error(jvmti,error,"Cannot Get Method name");
printf("Exception in Method: %s%s at line number: %ld\n",name,sig,location);*/
}

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm,char *options,void *reserved){
jvmtiEnv *jvmti;
jvmtiCapabilities capabilities;
jvmtiError error;
jint result;
jvmtiEventCallbacks callbacks;

result = (*jvm)->GetEnv(jvm,(void **)&jvmti,JVMTI_VERSION_1);
if(result!=JNI_OK){
    printf("Unable to access JVMTI! \n");
}
    gdata = (GlobalAgentData*)malloc(sizeof(GlobalAgentData));
    gdata->jvmti=jvmti;

(void)memset(&capabilities,0,sizeof(jvmtiCapabilities));
capabilities.can_tag_objects = 1;
capabilities.can_signal_thread=1;
capabilities.can_get_owned_monitor_info=1;
capabilities.can_generate_method_entry_events=1;
capabilities.can_generate_exception_events=1;
capabilities.can_tag_objects=1;
capabilities.can_access_local_variables=1;

error = (*(gdata->jvmti))->AddCapabilities(gdata->jvmti,&capabilities);
check_jvmti_error(gdata->jvmti,error,"Unable to set Capabilities");  

(void)memset(&callbacks,0,sizeof(callbacks));
callbacks.VMInit = &callbackVMInit;
callbacks.Exception = &callbackException;
//callbacks.MethodEntry = &callbackMethodEntry;

error = (*(gdata->jvmti))->SetEventCallbacks(gdata->jvmti,&callbacks,(jint)sizeof(callbacks));
check_jvmti_error(gdata->jvmti,error,"Cannot set event callbacks");

error = (*(gdata->jvmti))->SetEventNotificationMode(gdata->jvmti,JVMTI_ENABLE,JVMTI_EVENT_VM_INIT,(jthread)NULL);
check_jvmti_error(gdata->jvmti,error,"Cannot set event notification");

error = (*(gdata->jvmti))->CreateRawMonitor(gdata->jvmti,"agent data",&(gdata->lock));
check_jvmti_error(gdata->jvmti,error,"Cannot create raw monitor");

printf("A message from my custom super agent!!\n");
return JNI_OK;
}

下面是输出:

ERROR: JVMTI: 101(JVMTI_ERROR_ABSENT_INFORMATION): Cannot Get Local Variable table

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007f3faab0cefa, pid=14869, tid=0x00007f3fad251700
#
# JRE version: Java(TM) SE Runtime Environment (8.0_111-b14) (build 1.8.0_111-b14)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.111-b14 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# C  [liblearnAgent.so+0xefa]  callbackException+0x277
#
# Core dump written. Default location: /home/kumard/Desktop/core or core.14869
#
# An error report file with more information is saved as:
# /home/kumard/Desktop/hs_err_pid14869.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
[1]    14869 abort (core dumped)  java -agentlib:learnAgent SimpleThread

这不是完整的答案,但它确实解决了您的代理中的一些问题,并在出现异常时打印出基本数据类型的值。我们已经解决了评论聊天中的问题。所以我只是在这里发布代码。任何有兴趣了解更多信息的人都可以看到聊天。也非常欢迎您对问题发表您的见解。

#include <jni.h>
#include <jvmti.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct {
    jvmtiEnv *jvmti;
    jrawMonitorID lock;
} GlobalAgentData;

static GlobalAgentData *gdata;

static bool check_jvmti_error(jvmtiEnv *jvmti, jvmtiError errnum,
        const char *str) {
    if (errnum != JVMTI_ERROR_NONE) {
        char *errnum_str;
        errnum_str = NULL;
        (void) (*jvmti)->GetErrorName(jvmti, errnum, &errnum_str);
        printf("ERROR: JVMTI: %d(%s): %s\n", errnum,
                (errnum_str == NULL ? "Unknown" : errnum_str),
                (str == NULL ? "" : str));
        return false;
    }
    return true;
}

static void JNICALL callbackException(jvmtiEnv *jvmti, JNIEnv *env,
        jthread thread, jmethodID method, jlocation location, jobject exception,
        jmethodID catch_method, jlocation catch_location) {
    jvmtiFrameInfo frames[10];
    jint count, entry_count_ptr;
    int i, j;
    jvmtiError error;
    char *sig, *gsig;
    jclass declaring_class_ptr;
    jvmtiLocalVariableEntry *table_ptr;

    error = (*jvmti)->GetStackTrace(jvmti, thread, 0, 10, frames, &count);
    if (check_jvmti_error(jvmti, error, "Cannot Get Frame") && count >= 1) {
        char *methodName, *className;
        for (i = 0; i < count; i++) {
            error = (*jvmti)->GetMethodName(jvmti, frames[i].method,
                    &methodName, &sig, &gsig);
            if (check_jvmti_error(jvmti, error, "Cannot Get method name")) {

                error = (*jvmti)->GetMethodDeclaringClass(jvmti,
                        frames[i].method, &declaring_class_ptr);
                check_jvmti_error(jvmti, error,
                        "Cannot Get method declaring class");

                error = (*jvmti)->GetClassSignature(jvmti, declaring_class_ptr,
                        &className, NULL);
                check_jvmti_error(jvmti, error, "Cannot get class signature");

                error = (*jvmti)->GetLocalVariableTable(jvmti, frames[i].method,
                        &entry_count_ptr, &table_ptr);
                check_jvmti_error(jvmti, error,
                        "Cannot Get Local Variable Table");

                printf(
                        "Got Exception in  Method: %s at Line: %ld with Signature:%s,%s within Class:%s\n",
                        methodName, frames[i].location, sig, gsig, className);

                if (strstr(className, "java") == NULL
                        && strstr(className, "javax") == NULL
                        && strstr(className, "sun") == NULL) {
                    for (j = 0; j < entry_count_ptr; j++) {

                        switch (*(table_ptr[j].signature)) {
                        case 'B': {
                            jint value_ptr;
                            error = (*jvmti)->GetLocalInt(jvmti, thread, i,
                                    table_ptr[j].slot, &value_ptr);
                            check_jvmti_error(jvmti, error,
                                    "Cannot Get Local Variable Byte");

                            printf("Value of Field %s is %d.\n", table_ptr[j].name, (jbyte)value_ptr);
                            break;
                        }

                        case 'C': {
                            jint value_ptr;
                            error = (*jvmti)->GetLocalInt(jvmti, thread, i,
                                    table_ptr[j].slot, &value_ptr);
                            check_jvmti_error(jvmti, error,
                                    "Cannot Get Local Variable Char");

                            printf("Value of Field %s is %c.\n", table_ptr[j].name, (jchar)value_ptr);
                            break;
                        }
                        case 'D': {
                            jdouble value_ptr;
                            error = (*jvmti)->GetLocalDouble(jvmti, thread, i,
                                    table_ptr[j].slot, &value_ptr);
                            check_jvmti_error(jvmti, error,
                                    "Cannot Get Local Variable Double");

                            printf("Value of Field %s is %f.\n", table_ptr[j].name, value_ptr);
                            break;
                        }
                        case 'F': {
                            jfloat value_ptr;
                            error = (*jvmti)->GetLocalFloat(jvmti, thread, i,
                                    table_ptr[j].slot, &value_ptr);
                            check_jvmti_error(jvmti, error,
                                    "Cannot Get Local Variable Float");

                            printf("Value of Field %s is %f.\n", table_ptr[j].name, value_ptr);
                            break;
                        }
                        case 'I': {
                            jint value_ptr;
                            error = (*jvmti)->GetLocalInt(jvmti, thread, i,
                                    table_ptr[j].slot, &value_ptr);
                            check_jvmti_error(jvmti, error,
                                    "Cannot Get Local Variable Integer");

                            printf("Value of Field %s is %d.\n", table_ptr[j].name, value_ptr);
                            break;
                        }
                        case 'J': {
                            jlong value_ptr;
                            error = (*jvmti)->GetLocalLong(jvmti, thread, i,
                                    table_ptr[j].slot, &value_ptr);
                            check_jvmti_error(jvmti, error,
                                    "Cannot Get Local Variable Long");

                            printf("Value of Field %s is %ld.\n", table_ptr[j].name, value_ptr);
                            break;
                        }
                        case 'S':{
                            jint value_ptr;
                            error = (*jvmti)->GetLocalInt(jvmti, thread, i,
                                    table_ptr[j].slot, &value_ptr);
                            check_jvmti_error(jvmti, error,
                                    "Cannot Get Local Variable Short");

                            printf("Value of Field %s is %d.\n", table_ptr[j].name, (jshort)value_ptr);
                            break;
                        }
                        case 'Z':{
                            jint value_ptr;
                            error = (*jvmti)->GetLocalInt(jvmti, thread, i,
                                    table_ptr[j].slot, &value_ptr);
                            check_jvmti_error(jvmti, error,
                                    "Cannot Get Local Variable Boolean");

                            printf("Value of Field %s is %d.\n", table_ptr[j].name, (jboolean)value_ptr);
                            break;
                        }
                        case 'L':{
                            jobject value_ptr;
                            error = (*jvmti)->GetLocalObject(jvmti, thread, i,
                                    table_ptr[j].slot, &value_ptr);
                            check_jvmti_error(jvmti, error,
                                    "Cannot Get Local Variable Object");

                            printf("Value of Field %s is .\n", table_ptr[j].name);
                            break;
                        }
                        default:
                            printf("Can't get %s type.\n",
                                    table_ptr[j].signature);
                        }


                        printf("Field Signature:%s\n", table_ptr[j].signature);

                    }
                }

            }
        }
    }

}

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
    jvmtiEnv *jvmti;
    jvmtiCapabilities capabilities;
    jvmtiError error;
    jint result;
    jvmtiEventCallbacks callbacks;

    result = (*jvm)->GetEnv(jvm, (void **) &jvmti, JVMTI_VERSION_1);
    if (result != JNI_OK) {
        printf("Unable to access JVMTI! \n");
    }

    gdata = (GlobalAgentData*) malloc(sizeof(GlobalAgentData));
    gdata->jvmti = jvmti;

    (void) memset(&capabilities, 0, sizeof(jvmtiCapabilities));
    capabilities.can_generate_exception_events = 1;
    capabilities.can_access_local_variables = 1;

    error = (*(gdata->jvmti))->AddCapabilities(gdata->jvmti, &capabilities);
    check_jvmti_error(gdata->jvmti, error, "Unable to set Capabilities");

    error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
            JVMTI_EVENT_EXCEPTION, (jthread) NULL);
    check_jvmti_error(jvmti, error, "Cannot set Exception Event notification");

    (void) memset(&callbacks, 0, sizeof(callbacks));
    callbacks.Exception = &callbackException;

    error = (*(gdata->jvmti))->SetEventCallbacks(gdata->jvmti, &callbacks,
            (jint) sizeof(callbacks));
    check_jvmti_error(gdata->jvmti, error, "Cannot set event callbacks");

    error = (*(gdata->jvmti))->CreateRawMonitor(gdata->jvmti, "agent data",
            &(gdata->lock));
    check_jvmti_error(gdata->jvmti, error, "Cannot create raw monitor");

    printf("A message from my custom super agent!!\n");
    return JNI_OK;
}