我有以下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;

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

有帮助吗?

解决方案

我正在处理类似的任务。这是用C ++编写的两个代码示例。示例1显示了如何使用MethodEntry回调中获取本地变量 getlocalvariabletablegetLocalObject. 。示例2显示了如何使用BCI(BYTECODE仪器)进行预成式。

示例1:

handlemethodentry是methodentry事件的回电方法。它记录了有关该方法参数的一些信息。 getLocalvariabletable检索了GetLocalObject使用的本地变量信息。深度为零的帧是当前帧,第一个参数为插槽0。对于非静态帧,插槽0包含“此”对象。要从本机方法帧中检索“此”对象,您应该使用 getlocalinstance 代替 getLocalObject.

签名的第一个字符是 值类型. 。此示例只需检查局限的标签即可。对于字符串值,您可以使用 getTringutfchars. 。可以找到一个例子 这里.

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:

如答案所述 一个类似的问题, ,在MethodEntry中处理它,甚至回调可能会有预性问题。您可以考虑BCI方法。 mtrace_native_entry是一种本机方法,注入了每个方法调用的一开始。它是从mtrace的method_entry方法中称为的。

在mtrace_native_entry中,您需要在框架2处跟踪回到感兴趣的方法(执行本机方法的当前帧位于帧0)。在GitHub的另一个项目stackparam中可以找到类似的参数跟踪示例。但是,未测试这两种方法的性能差异。

可以在JDK文档dir dir dir/jvmti/mtrace中找到该示例的未展示代码。核心步骤是使用Java_crw_demo将Method_entry注入classFileleLoadhook事件回调。

此示例还显示了如何获取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功能的所有返回值。也可能存在其他一些问题。

其他提示

您将想开始使用 getLocalObject. 。在这方面,我能够找到以下内容 例子 这应该有助于您朝着正确的方向前进。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top