Question

J'ai le code java suivant

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

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

J'ai un agent JVMTI 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 de jvmti.h je trouve 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 des valeurs de paramètres de mon rappel?

Était-ce utile?

La solution

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

Exemple 1:

HandleMethodEntry est la méthode de retour d'appel pour l'événement MethodEntry. Il enregistre des informations sur les paramètres de la méthode. GetLocalVariableTable récupère des informations variables locales, qui est utilisé par GetLocalObject. Cadre à la profondeur zéro est la trame en cours, le premier paramètre est à l'emplacement 0. Pour les trames non-statique, l'emplacement 0 contient l'objet « this ». Pour récupérer l'objet « ce » à partir d'images de méthodes natives, vous devez utiliser GetLocalInstance au lieu de GetLocalObject .

Le premier caractère de la signature est le type de valeur . 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é ici .

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 une question similaire , traitant en MethodEntry même rappel peut avoir des problèmes de preformance. Vous pouvez envisager l'approche de BCI. MTRACE_native_entry est une méthode native injectée début de chaque appel de méthode. Il est appelé à partir de la méthode de method_entry de MTRACE.

Dans MTRACE_native_entry, vous devez suivre retour à la méthode intrested à l'image 2 (cadre actuel de la méthode native d'exécution est à l'image 0). exemple similaire de trace param se trouve dans un autre projet stackparam dans GitHub. Cependant, les différences de performence de ces deux méthodes ne sont pas testées.

Code de cet exemple non représenté se trouve dans le document jdk dir demo / JVMTI / mtrace. L'étape principale consiste à injecter dans method_entry rappel d'événement ClassFileLoadHook utilisant java_crw_demo.

Cet exemple montre également comment obtenir la valeur du 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.

Autres conseils

Vous allez vouloir commencer par utiliser GetLocalObject . À cet égard, j'ai pu trouver le exemple qui devrait aider vous obtenez aller dans la bonne direction.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top