Найти имена всех классов, которые Java-программа загружает, используя отражение - PullRequest
3 голосов
/ 29 августа 2009

Для поддержки инструмента статического анализа я хочу установить или контролировать Java-программу таким образом, чтобы я мог определить для каждого отражающего вызова (например, Method.invoke (..)):

1.) На какой класс C вызывается этот метод, и 2.) какой загрузчик классов загрузил этот класс C.

В идеале я ищу решение, которое не требует от меня статической модификации библиотеки времени выполнения Java, то есть я ищу решение во время загрузки. Однако решение должно иметь возможность перехватывать все отражающие вызовы, даже такие вызовы, которые происходят внутри самой библиотеки времени выполнения Java. (Я играл с ClassFileTransformer, но это, кажется, применяется только к классам, от которых сам ClassFileTransformer не зависит. В частности, ClassFileTransfomer не применяется к классу "Class".)

Спасибо!

Ответы [ 2 ]

1 голос
/ 31 августа 2009

Вы ищете что-то, что может работать в производстве? Или этого достаточно, чтобы инструмент работал в тестовой среде? Если это последнее, возможно, стоит рассмотреть возможность запуска приложения под инструментом профилирования. Я лично использовал и рекомендую JProfiler , который позволяет вам выполнять трассировку вызовов и настраивать триггеры для выполнения таких действий, как ведение журнала при вызове определенных методов. Он не требует каких-либо изменений в размещенной программе и прекрасно работает в библиотеке времени выполнения Java. Также есть инструменты с открытым исходным кодом, но я не добился такого большого успеха, чтобы заставить их работать.

Если вам нужно что-то, что будет работать в рабочей среде, вы можете захотеть исследовать реализацию своего собственного Classloader или манипулирования байтовым кодом через Javassist или CGLib, возможно, с использованием AspectJ (AOP). Это, очевидно, более сложное решение, и я не уверен, что оно будет работать без поддержки во время компиляции, поэтому, надеюсь, инструмент профилирования выполним в вашей ситуации.

0 голосов
/ 31 августа 2009

API, к которому вы, вероятно, обращаетесь: JVMTI . JVMTI позволяет регистрировать обратные вызовы для большинства событий, происходящих в JVM, включая MethodEntry, MethodExit. Вы слушаете эти события и извлекаете события 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...