2010-05-21 10 views
1

Je le code java suivantComment obtenir les valeurs des paramètres dans un rappel MethodEntry

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

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

je JVMTI un agent attaché à Java où je surprends les appels de fonction. Je veux obtenir une valeur de paramètre qui a été passé à ma méthode (par exemple « 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; 

Dans le rappel lui-même, je possède un numéro de fil et méthode.

En regardant dans un en-tête jvmti.h je n'ai trouvé que cette structure traitant des paramètres mais il n'y a pas de valeurs.

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

Comment puis-je obtenir les valeurs des paramètres de mon rappel?

Répondre

1

Je travaille sur des tâches similaires. Voici deux exemples de code écrits en C++. L'exemple 1 montre comment obtenir des variables locales dans le rappel MethodEntry en utilisant GetLocalVariableTable et GetLocalObject. L'exemple 2 montre comment le préformer en utilisant BCI (Bytecode Instrumentation).

Exemple 1:

HandleMethodEntry est le rappel procédé pour l'événement MethodEntry. Il enregistre des informations sur les paramètres de la méthode. GetLocalVariableTable récupère les informations de variables locales, qui sont utilisées par GetLocalObject. Le cadre à la profondeur zéro est la trame courante, le premier paramètre est à l'emplacement 0. Pour les images non statiques, l'emplacement 0 contient l'objet "this". Pour récupérer "cet" objet des trames de méthode natives, vous devez utiliser GetLocalInstance au lieu de GetLocalObject.

Le premier caractère de la signature est le value type. Cet exemple vérifie simplement l'étiquette d'un jobject. Pour les valeurs de chaîne, vous pouvez utiliser GetStringUTFChars. Un exemple peut être trouvé 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; 
} 
} 

Exemple 2:

Comme mentionné dans la réponse de a similar question, traitant dans MethodEntry même rappel peut avoir des problèmes de preformance. Vous pouvez considérer l'approche BCI. MTRACE_native_entry est une méthode native injectée au tout début de chaque appel de méthode. Il est appelé depuis la méthode method_entry de MTrace.

Dans MTRACE_native_entry, vous devez repérer la méthode intrested à l'image 2 (l'image actuelle de la méthode native en cours d'exécution est à l'image 0). Un exemple similaire de trace de param peut être trouvé dans un autre projet stackparam dans GitHub. Cependant, les différences de performance de ces deux méthodes ne sont pas testées.

Le code non affiché de cet exemple peut être trouvé dans le document jdk dir demo/jvmti/mtrace. L'étape principale consiste à injecter method_entry dans le rappel d'événement ClassFileLoadHook en utilisant java_crw_demo.

Cet exemple montre également comment obtenir la valeur d'un champ d'un objet 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); 

} 

La méthode de classe utilisée pour injecter:

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); 
    } 
} 
} 

Notez que ces deux exemples sont écrits à des fins de test, pas toute la valeur de retour des fonctions JVMTI sont vérifiées. D'autres problèmes peuvent également exister.

+0

Merci d'avoir répondu à la question. Maintenant, 7 ans après que j'ai posé cette question, je ne peux pas tester votre réponse. Mais il semble être complet et peut aider d'autres personnes. Donc, je le marque comme la réponse acceptée –

1

Vous allez vouloir commencer par utiliser GetLocalObject. À cet égard, j'ai été en mesure de trouver le example qui devrait vous aider à aller dans la bonne direction.

+0

Alex, ça marche. Merci beaucoup!!! –

+0

@OlegPavliv Le lien de l'exemple semble avoir expiré, pouvez-vous s'il vous plaît me partager le lien à nouveau – kumarD

+0

@kumarD l'exemple a été partagé par une personne qui a répondu à la question. Je ne l'ai pas –