Frage

Ich habe folgende Java-Code

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

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

Ich habe ein JVMTI Agent Java angebracht, wo ich Funktion Anrufe fangen. Ich mag Parameterwert erhalten, die zu meiner Methode übergeben wurde (zum Beispiel „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;

In der Callback selbst ich einen Thread und Verfahren ID haben.

Ein Blick in einen jvmti.h Header fand ich nur diese Struktur mit Parametern zu tun, aber es gibt keine Werte.

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

Wie kann ich die Parameterwerte von meinem Rückruf erhalten?

War es hilfreich?

Lösung

Ich arbeite an ähnlichen Aufgaben. Hier sind zwei Codebeispiele in C ++ geschrieben. Beispiel 1 zeigt, wie lokale Variablen in dem MethodEntry Rückruf erhalten mit GetLocalVariableTable und GetLocalObject . Beispiel 2 zeigt, wie dies mit BCI (Bytecode Instrumentation) vorzuformen.

Beispiel 1:

HandleMethodEntry ist der Rückruf-Methode für MethodEntry Ereignis. Es protokolliert einige Informationen über die Parameter der Methode. GetLocalVariableTable ruft lokale Variable Informationen, die durch GetLocalObject verwendet wird. Frame in der Tiefe null ist der aktuelle Rahmen der erste Parameter auf Schlitz 0. Für nicht-statischen Rahmen, Schlitz 0 enthält das „dieses“ Objekt. Um „dieses“ Objekt aus nativen Methode Rahmen abrufen, sollten Sie GetLocalInstance statt GetLocalObject .

Das erste Zeichen der Signatur ist die Werttyp . In diesem Beispiel wird einfach überprüft den Tag eines jobject. Für String-Werte, können Sie GetStringUTFChars . Ein Beispiel finden Sie hier .

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

Beispiel 2:

Wie bereits erwähnt in der Antwort von eine ähnliche Frage , mit ihm in MethodEntry tun kann sogar Rückruf preformance Problem. Sie können den BCI Ansatz in Betracht ziehen. MTRACE_native_entry ist eine native Methode sehr injiziert jeder Methodenaufruf beginnt. Es ist aus MTRACE der method_entry Methode aufgerufen.

In MTRACE_native_entry, müssen Sie bei Frame 2 zu dem Interessenten Verfahren verfolgen zurück (aktuellen Frame der Vollstreckungs nativen Methode ist bei Frame 0) gewonnen. Ähnliches Beispiel param Trace kann in einem anderen Projekt stackparam in GitHub zu finden. Allerdings performence Unterschiede dieser beiden Methoden wird nicht getestet.

nicht gezeigter Code dieses Beispiels kann in dem jdk Dokument dir demo / JVMTI / mtrace finden. Der Kern Schritt ist einzuspritzen method_entry in ClassFileLoadHook Ereignisrückruf mit java_crw_demo.

Dieses Beispiel zeigt auch, wie ein param Objektfeldwert erhalten.

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

}

Die Klassenmethode verwendet zu injizieren:

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

Beachten Sie, dass diese beiden Beispiele für Testzwecke geschrieben ist, nicht alle der Rückgabewert von JVMTI Funktionen geprüft werden. Einige andere Probleme können auch vorhanden sein.

Andere Tipps

Sie werden mithilfe von GetLocalObject beginnen zu wollen, . In dieser Hinsicht war ich in der Lage, die folgenden Beispiel , die helfen sollten finden erhalten Sie in die richtige Richtung gehen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top