查找Java程序使用反射加载的所有类的名称

时间:2009-08-29 10:12:54

标签: java reflection jvm transform instrumentation

为了支持静态分析工具,我想以这样的方式检测或监视Java程序,以便我可以确定每个反射调用(如Method.invoke(..)):

1。)调用此方法的C类,和 2.)哪个类加载器加载了这个C类。

理想情况下,我正在寻找一种不需要我静态修改Java运行时库的解决方案,即我正在寻找一个加载时解决方案。但是,该解决方案应该能够捕获所有反射调用,甚至是Java运行时库本身内发生的调用。 (我玩过ClassFileTransformer,但这似乎只适用于ClassFileTransformer本身不依赖的类。特别是,ClassFileTransfomer不适用于“Class”类。)

谢谢!

2 个答案:

答案 0 :(得分:1)

您在寻找可以在生产中运行的东西吗?或者是否足以检测在测试环境中运行的应用程序?如果是后者,可能需要考虑在分析工具下运行应用程序。我个人使用并建议使用JProfiler,它允许您调用跟踪并设置触发器以执行调用特定方法时的日志记录等操作。它不需要对托管程序进行任何修改,并且可以在Java运行时库上正常工作。还有一些开源工具,但我没有那么多成功的工作。

如果你需要一些可以在生产中运行的东西,你可能想要研究通过Javassist或CGLib实现你自己的自定义类加载器或字节码操作,也许使用AspectJ(AOP)。这显然是一个更复杂的解决方案,我不确定它是否可以在没有编译时支持的情况下工作,所以希望分析工具对您的情况是可行的。

答案 1 :(得分:0)

您可能关注的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;
}