我正在尝试使用 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): %sn", 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 JVMn");
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 tablen")){
printf("local variable table entry size : %d %sn",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:%sn",(*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:%sn",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:%sn",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: %ldn",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): %sn", 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:%sn",
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:%sn", 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;
}