2010-05-21 32 views
1

我有以下的Java代码如何获得的参数值在MethodEntry回调

public class Test { 
    public void sayHello(String msg) { 
     System.out.println(msg); 
    } 
} 

new Test().sayHello("Bonjour"); 

我有一个JVMTI代理连接到Java,我赶上函数调用。我想这是传递给我的方法(例如,“卓悦”)

static void JNICALL cbMethodEntry(jvmtiEnv *jvmti, 
         JNIEnv* jni_env, jthread thread, jmethodID method) { 
     // here I want to get a parameter value "Bonjour" 
     // which was passed to my method sayHello("Bonjour") 
    } 

    jvmtiEventCallbacks callbacks; 
    callbacks.MethodEntry = &cbMethodEntry; 

在回调本身我有一个线程和方法ID参数值。

寻找到一个jvmti.h头我发现只有这个结构处理参数,但没有值。

typedef struct { 
    char* name; 
    jvmtiParamKind kind; 
    jvmtiParamTypes base_type; 
    jboolean null_ok; 
} jvmtiParamInfo; 

如何从我的回调中获取参数值?

回答

1

我正在处理类似的任务。以下是用C++编写的两个代码示例。示例1显示如何使用GetLocalVariableTableGetLocalObject获取MethodEntry回调中的局部变量。示例2显示了如何使用BCI(字节码工具)进行预形变。

实施例1:

HandleMethodEntry为MethodEntry事件回呼方法。它记录有关方法参数的一些信息。 GetLocalVariableTable检索由GetLocalObject使用的局部变量信息。深度零处的帧是当前帧,第一个参数在时隙0处。对于非静态帧,时隙0包含“this”对象。要从本地方法框架中检索“this”对象,应该使用GetLocalInstance而不是GetLocalObject

签名的第一个字符是value type。这个例子只是检查一个jobject的标签。对于字符串值,您可以使用GetStringUTFChars。一个例子可以发现here

void JNICALL MethodTraceAgent::HandleMethodEntry(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID method) 
{ 
try { 
    jvmtiError error; 
    jclass clazz; 
    char* name; 
    char* signature; 

    // get declaring class of the method 
    error = m_jvmti->GetMethodDeclaringClass(method, &clazz); 
    Errors::Check(error); 
    // get the signature of the class 
    error = m_jvmti->GetClassSignature(clazz, &signature, 0); 
    Errors::Check(error); 
    // get method name 
    error = m_jvmti->GetMethodName(method, &name, NULL, NULL); 
    Errors::Check(error); 

    char tmp[1024]; 
    sprintf(tmp, "%s%s", signature, name); 
    if(pFilter->Match("method", tmp)) { // intrested method? 
     char out[1024]; 
     jint param_size = 0; 

     error = m_jvmti->GetArgumentsSize(method, &param_size); 
     int line_len = sprintf(out, "method_entry: %s%s%, param_size:%d", signature, name, param_size);    

     // visit local variable 
     jint entry_count = 0; 
     jvmtiLocalVariableEntry *table_ptr = NULL;  
     jlocation cur_loc; 

     // this call may return JVMTI_ERROR_ABSENT_INFORMATION, this error is avoided by initialize entry_count to 0 to escape the following for loop 
     error = m_jvmti->GetLocalVariableTable(method, &entry_count, &table_ptr); 
     error = m_jvmti->GetFrameLocation(thread, 0, NULL, &cur_loc); 
     for(int j=0; j<min(param_size, entry_count); j++) { 
      if(table_ptr[j].start_location > cur_loc) break; 
      if(table_ptr[j].signature[0] == 'L') { // fully-qualified-class 
       jobject param_obj; 
       jlong param_obj_tag = 0; 

       error = m_jvmti->GetLocalObject(thread, 0, table_ptr[j].slot, &param_obj); // frame at depth zero is the current frame     
       m_jvmti->GetTag(param_obj, &param_obj_tag); 
       if(param_obj_tag == 0) { 
        m_jvmti->SetTag(param_obj, theTag); 
        param_obj_tag = theTag; 
        ++theTag; 
       } 
       line_len += sprintf(out + line_len, ", param_obj_tag: %ld", param_obj_tag); 
       //line_len += sprintf(out+line_len, ", slot:%d, start:%ld, cur:%ld, param:%s%s", table_ptr[j].slot, table_ptr[j].start_location, cur_loc, table_ptr[j].signature, table_ptr[j].name); 

       jni->DeleteLocalRef(param_obj); 
       m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr[j].signature)); 
       m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr[j].name)); 
       m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr[j].generic_signature)); 
      } 

     } 

     error = m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr)); 
     Errors::Check(error); 


     // put to log list 
     logList.push_back(out); 

     printf("\r%-10d", logList.size()); 
    } 

    // release resources 
    error = m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(name)); 
    Errors::Check(error); 
    error = m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature)); 
    Errors::Check(error); 

} catch (AgentException& e) { 
    cout << "Error when enter HandleMethodEntry: " << e.what() << " [" << e.ErrCode() << "]" << endl; 
} 
} 

例2:

如在a similar question的答复中提到,它在MethodEntry甚至回调的处理可能有性能与问题。你可以考虑BCI方法。 MTRACE_native_entry是注入每个方法调用一开始的本地方法。它从MTrace的method_entry方法中调用。

在MTRACE_native_entry中,您需要追溯到第2帧处的intrested方法(执行本机方法的当前帧在第0帧处)。参数跟踪的类似示例可以在GitHub中的另一个项目stackparam中找到。但是,这两种方法的性能差异没有经过测试。

本示例的未显示代码可以在jdk文档dir demo/jvmti/mtrace中找到。核心步骤是使用java_crw_demo在ClassFileLoadHook事件回调中注入method_entry。

本例还显示了如何获取param对象的字段值。

void JNICALL MethodTraceAgent::MTRACE_native_entry(JNIEnv *jni, jclass klass, jthread thread, jint cnum, jint mnum) 
{ 

/* It's possible we get here right after VmDeath event, be careful */ 
if (!pTheAgent->vmInitialized || pTheAgent->vmDead || thread == NULL) 
    return; 


jvmtiError error; 
char out[1024]; 
int line_len = 0; 
jvmtiFrameInfo frames[3]; 
jint cframe; 

error = m_jvmti->GetStackTrace(thread, 0, 3, frames, &cframe); 
Errors::Check(error); 

if(cframe < 3) 
    return; 

jmethodID method = frames[2].method; 
jclass dec_cls; 
char *mtd_name, *dec_cls_sig; 

m_jvmti->GetMethodDeclaringClass(method, &dec_cls); 
m_jvmti->GetClassSignature(dec_cls, &dec_cls_sig, NULL); 
m_jvmti->GetMethodName(method, &mtd_name, NULL, NULL); 


jboolean isNative = false; 
m_jvmti->IsMethodNative(method, &isNative); 
if(isNative) 
    return; 

line_len += sprintf(out + line_len, "m_en: %s%s", dec_cls_sig, mtd_name); 

// operate tags 
jint param_size = 0; 
jint entry_count = 0; 
jvmtiLocalVariableEntry *table_ptr = NULL;  

error = m_jvmti->GetArgumentsSize(method, &param_size); 
error = m_jvmti->GetLocalVariableTable(method, &entry_count, &table_ptr); 
Errors::Check(error); 

line_len += sprintf(out + line_len, ", %d, %d", param_size, entry_count); 

for(int j=0; j<min(param_size, entry_count); j++) { 
    jobject param_obj = 0; 

    if(j==0 && strcmp(table_ptr[0].name, "this") == 0) { // this instance 
     error = m_jvmti->GetLocalInstance(thread, 2, &param_obj); 
     if(thiso == 0) thiso = param_obj; 
     else { 
      line_len += sprintf(out + line_len, ", same_this: %d", jni->IsSameObject(thiso, param_obj)); 
     } 

     jfieldID field = jni->GetFieldID(dec_cls, "a", "I"); 
     jint a = jni->GetIntField(param_obj, field); 
     line_len += sprintf(out + line_len, ", a: %d", a); 

     Errors::Check(error); 
    } 
    else if(table_ptr[j].signature[0] == 'L') { // object 
     error = m_jvmti->GetLocalObject(thread, 2, table_ptr[j].slot, &param_obj); // frame at depth zero is the current frame  
     Errors::Check(error); 
    } 

    if(param_obj != 0) { 

     //line_len += sprintf(out + line_len, ", modi: %d, this: %d, same: %d", modied, param_obj, jni->IsSameObject(param_obj, modied)); 

     jlong param_obj_tag = 0; 
     m_jvmti->GetTag(param_obj, &param_obj_tag); 
     if(param_obj_tag == 0) { 
      error = m_jvmti->SetTag(param_obj, pTheAgent->ctag); 
      Errors::Check(error); 
      param_obj_tag = pTheAgent->ctag; 
      ++pTheAgent->ctag; 
     } 
     line_len += sprintf(out + line_len, ", param_obj_tag: %ld", param_obj_tag); 
     //line_len += sprintf(out+line_len, ", slot:%d, start:%ld, cur:%ld, param:%s%s", table_ptr[j].slot, table_ptr[j].start_location, cur_loc, table_ptr[j].signature, table_ptr[j].name); 

     jni->DeleteLocalRef(param_obj); 
     m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr[j].signature)); 
     m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr[j].name)); 
     m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr[j].generic_signature)); 
    } 

} 

error = m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr)); 
Errors::Check(error); 


m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(dec_cls_sig)); 
m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(mtd_name)); 



logList.push_back(out); 

} 

用于注入类方法:

public class MTrace { 

private static int engaged = 0; 

/* At the very beginning of every method, a call to method_entry() 
*  is injected. 
*/ 

private static native void _method_entry(Object thread, int cnum, int mnum); 
public static void method_entry(int cnum, int mnum) 
{ 
    if (engaged != 0) { 
     _method_entry(Thread.currentThread(), cnum, mnum); 
    } 
} 

/* Before any of the return bytecodes, a call to method_exit() 
*  is injected. 
*/ 

private static native void _method_exit(Object thread, int cnum, int mnum); 
public static void method_exit(int cnum, int mnum) 
{ 
    if (engaged != 0) { 
     _method_exit(Thread.currentThread(), cnum, mnum); 
    } 
} 
} 

注意,这两个例子是为测试目的编写的,不是所有的JVMTI函数的返回值被检查。其他一些问题也可能存在。

+0

谢谢你回答这个问题。现在,在我问这个问题7年后,我无法测试你的答案。但它似乎是完整的,可能会帮助其他人。所以我将其标记为接受的答案 – 2018-01-24 16:00:13

1

您将要开始使用GetLocalObject。在这方面,我能够找到以下example,这应该有助于你朝着正确的方向前进。

+0

亚历克斯,它的工作原理。非常感谢!!! – 2010-05-22 06:40:26

+0

@OlegPavliv示例链接似乎已过期,请您再次与我分享链接 – kumarD 2017-02-14 09:07:53

+0

@kumarD该示例由回答问题的人共享。我没有 – 2017-02-14 09:13:31