2010-05-21 18 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