代码之家  ›  专栏  ›  技术社区  ›  Oleg Pavliv

如何在methodEntry回调中获取参数值

  •  2
  • Oleg Pavliv  · 技术社区  · 16 年前

    我有下面的Java代码

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

    我有一个JVMTI代理,它连接到Java,在那里我捕获函数调用。我想得到传递给我的方法的参数值(例如“bonjour”)。

      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;
    

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

    2 回复  |  直到 8 年前
        1
  •  2
  •   megre    8 年前

    我在做类似的工作。下面是C++编写的两个代码示例。示例1演示了如何使用 GetLocalVariableTable 和 GetLocalObject . 示例2展示了如何使用BCI(字节码检测)来执行此操作。

    例1:

    handleMethodEntry是methodEntry事件的回调方法。它记录了一些关于方法参数的信息。GetLocalVariableTable检索由GetLocalObject使用的本地变量信息。深度为零的帧是当前帧,第一个参数位于槽0。对于非静态帧,槽0包含“this”对象。要从本机方法框架中检索“this”对象,应使用 GetLocalInstance 而不是 获取本地对象 .

    签名的第一个字符是 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的方法_Entry方法调用的。

    在mtrace_native_条目中,您需要跟踪到第2帧处的intrested方法(正在执行的native方法的当前帧位于第0帧)。在GitHub的另一个项目stackParam中可以找到类似的param跟踪示例。然而,这两种方法的性能差异并没有得到检验。

    这个例子的非自治代码可以在jdk文档dir demo/jvmti/mtrace中找到。核心步骤是使用java_crw_demo在ClassFileLoadHook事件回调中注入方法_项。

    此示例还显示了如何获取参数对象的字段值。

    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函数的返回值都被检查。也可能存在其他一些问题。

        2
  •  1
  •   Alex Winston    16 年前

    你要从使用 GetLocalObject . 在这方面,我发现了 example 这有助于你朝正确的方向前进。