3

静的分析ツールをサポートするために、リフレクション呼び出し (Method.invoke(..) など) ごとに判断できるように、Java プログラムを計測または監視したいと考えています。

1.) このメソッドが呼び出されるクラス C、および 2.) このクラス C をロードしたクラスローダ。

理想的には、Java ランタイム ライブラリを静的に変更する必要のないソリューション、つまりロード時のソリューションを探しています。ただし、このソリューションは、Java ランタイム ライブラリ自体で発生するような呼び出しであっても、すべてのリフレクション呼び出しをキャプチャできる必要があります。(ClassFileTransformer をいじってみましたが、これは ClassFileTransformer 自体が依存していないクラスにのみ適用されるようです。特に ClassFileTransfomer はクラス「Class」には適用されません。)

ありがとう!

4

2 に答える 2

0

おそらくあなたが求めているAPIはJVMTIです。JVMTIを使用すると、MethodEntry、MethodExitなど、JVM内で発生するイベントの大部分のコールバックを登録できます。それらのイベントをリッスンし、Method.invokeイベントを引き出します。特定のクラスのクラスローダーを取得するためのAPI呼び出しがあります。ただし、ツールはCまたはC++で作成する必要があります。

これは、java.lang.reflect.Method.invoke呼び出しからフィルターを取り出して出力する例です。呼び出されたオブジェクトに関する詳細を取得するには、おそらくスタックフレームを確認する必要があります。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <jvmti.h>

static jvmtiEnv *jvmti = NULL;
static jvmtiCapabilities capa;

static jint check_jvmti_error(jvmtiEnv *jvmti,
                              jvmtiError errnum,
                              const char *str) {

    if (errnum != JVMTI_ERROR_NONE) {
        char *errnum_str;
        errnum_str = NULL;
        (void) (*jvmti)->GetErrorName(jvmti, errnum, &errnum_str);
        printf("ERROR: JVMTI: %d(%s): %s\n",
               errnum,
               (errnum_str == NULL ? "Unknown" : errnum_str),
               (str == NULL ? "" : str));
        return JNI_ERR;
    }
    return JNI_OK;
}

void JNICALL callbackMethodEntry(jvmtiEnv *jvmti_env,
                                 JNIEnv* jni_env,
                                 jthread thread,
                                 jmethodID method) {

    char* method_name;
    char* method_signature;
    char* generic_ptr_method;
    char* generic_ptr_class;
    char* class_name;
    jvmtiError error;
    jclass clazz;

    error = (*jvmti_env)->GetMethodName(jvmti_env,
                                        method,
                                        &method_name,
                                        &method_signature,
                                        &generic_ptr_method);
    if (check_jvmti_error(jvmti_env, error, "Failed to get method name")) {
        return;
    }

    if (strcmp("invoke", method_name) == 0) {

        error
                = (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, method,
                        &clazz);
        if (check_jvmti_error(jvmti_env, error,
                "Failed to get class for method")) {
            (*jvmti_env)->Deallocate(jvmti_env, method_name);
            (*jvmti_env)->Deallocate(jvmti_env, method_signature);
            (*jvmti_env)->Deallocate(jvmti_env, generic_ptr_method);
            return;
        }

        error = (*jvmti_env)->GetClassSignature(jvmti_env, clazz, &class_name,
                &generic_ptr_class);
        if (check_jvmti_error(jvmti_env, error, "Failed to get class signature")) {
            (*jvmti_env)->Deallocate(jvmti_env, method_name);
            (*jvmti_env)->Deallocate(jvmti_env, method_signature);
            (*jvmti_env)->Deallocate(jvmti_env, generic_ptr_method);
            return;
        }

        if (strcmp("Ljava/lang/reflect/Method;", class_name) == 0) {
            printf("Method entered: %s.%s.%s\n", class_name, method_name,
                    method_signature);
        }
        (*jvmti_env)->Deallocate(jvmti_env, class_name);
        (*jvmti_env)->Deallocate(jvmti_env, generic_ptr_class);
    }

    (*jvmti_env)->Deallocate(jvmti_env, method_name);
    (*jvmti_env)->Deallocate(jvmti_env, method_signature);
    (*jvmti_env)->Deallocate(jvmti_env, generic_ptr_method);
}

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {

    jint result;
    jvmtiError error;
    jvmtiEventCallbacks callbacks;

    result = (*jvm)->GetEnv(jvm, (void**) &jvmti, JVMTI_VERSION_1_0);
    if (result != JNI_OK || jvmti == NULL) {
        printf("error\n");
        return JNI_ERR;
    } else {
        printf("loaded agent\n");
    }

    (void) memset(&capa, 0, sizeof(jvmtiCapabilities));
    capa.can_generate_method_entry_events = 1;

    error = (*jvmti)->AddCapabilities(jvmti, &capa);
    if (check_jvmti_error(jvmti, error, "Unable to set capabilities") != JNI_OK) {
        return JNI_ERR;
    }

    (void) memset(&callbacks, 0, sizeof(callbacks));
    callbacks.MethodEntry = &callbackMethodEntry;
    error = (*jvmti)->SetEventCallbacks(jvmti,
                                        &callbacks,
                                        (jint) sizeof(callbacks));
    if (check_jvmti_error(jvmti, error, "Unable to set callbacks") != JNI_OK) {
        return JNI_ERR;
    }

    error = (*jvmti)->SetEventNotificationMode(jvmti,
                                               JVMTI_ENABLE,
                                               JVMTI_EVENT_METHOD_ENTRY,
                                               (jthread) NULL);
    if (check_jvmti_error(jvmti, error,
            "Unable to set method entry notifications") != JNI_OK) {
        return JNI_ERR;
    }

    return JNI_OK;
}
于 2009-08-31T07:31:17.977 に答える