动态字节码检测失败,没有任何错误 [英] Dynamic Bytecode Instrumentation fails without any error

查看:118
本文介绍了动态字节码检测失败,没有任何错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用JVMTI代理进行动态字节码检测.我必须检测那些热"的方法,即调用JIT编译器的方法.为此,我听了CompiledLoadEvent并在其回调函数中调用RetransformClasses.反过来,这会在包含"hot"函数的类上调用ClassFileLoadHook,然后开始实际检测.

I'm doing dynamic bytecode instrumentation using a JVMTI agent. I have to instrument those methods which are "hot", that is, the methods which invoke JIT compiler. To do so I listen to a CompiledLoadEvent and inside its call back function, call RetransformClasses. This in turn invokes ClassFileLoadHook on the class containing "hot" function and actual instrumentation begins.

目前,我正在检测类以产生一些线程.我也听线程启动并在我的代理中打印它们.在类加载时使用简单的ClassFileLoadHook(没有RetransformClasses),我的工具可以完美地工作并产生新的线程.当ClassFileLoadHook在类加载时进行测试时,我得到以下输出:

Currently I'm instrumenting my class to spawn some threads. I also listen to thread starts and print them within my agent. With simple ClassFileLoadHook at class load time (without RetransformClasses), my instrumentation works perfectly and spawns new threads. I get following output when ClassFileLoadHook instruments at class load time:

Running Thread: Signal Dispatcher, Priority: 9, context class loader:Not Null
Running Thread: main, Priority: 5, context class loader:Not Null
Running Thread: Thread-0, Priority: 5, context class loader:Not Null
Running Thread: Thread-1, Priority: 5, context class loader:Not Null
Running Thread: Thread-2, Priority: 5, context class loader:Not Null
Running Thread: Thread-3, Priority: 5, context class loader:Not Null
Running Thread: Thread-4, Priority: 5, context class loader:Not Null
Running Thread: Thread-6, Priority: 5, context class loader:Not Null
Running Thread: Thread-5, Priority: 5, context class loader:Not Null
Running Thread: Thread-7, Priority: 5, context class loader:Not Null
Running Thread: DestroyJavaVM, Priority: 5, context class loader:: NULL

当我通过依次调用RetransformClassesClassFileLoadHook来检测类文件时,一切正常,但是没有生成任何线程,因此没有有效的检测发生. VM甚至要花费很长时间才能执行原始代码.

When I instrument the class file by invoking RetransformClasses and then ClassFileLoadHook, everything works fine but no threads are spawned and hence no effective instrumentation takes place. VM takes a long time even to execute the original code.

我使用-XX:+ TraceClassLoading再次检查了两种工具.在这两种情况下都将加载所有重新转换的类.即使我在运行时生成的类也被加载,但是没有任何检测发生.下面是类加载跟踪的输出:

I double checked both instrumentations using -XX:+TraceClassLoading. All the retransformed classes are loaded in both cases. Even the class I'm generating during runtime also gets loaded but no instrumentation happens. Below is the output of class loading trace:

[Loaded Test from __VM_RedefineClasses__]
[Loaded Test_Worker_main_0 from file:/home/saqib/workspace/test/bin]

我在运行时生成第二类,并且将其加载到VM中,但没有产生任何线程.

I'm generating second class during runtime and it loads into VM but I don't get any thread spawning.

  • 根据我对问题的理解(极有可能 我会错的),为什么ClassFileLoadHook重新转换类 在加载期间成功完成,但由于某种原因无法正常运行 何时调用JIT?
  • 只需编写RetransformClasses函数,为空 ClassFileLoadHook回叫,也需要很多时间 导致任何类型的错误.可能要花些时间?
  • Given my understanding of the problem (There is a high probability that I'd be wrong), why ClassFileLoadHook retransforms the class successfully during load time, but somehow doesn't behave correctly when JIT is invoked?
  • Just writing the RetransformClasses function, with empty ClassFileLoadHook call back, also takes a lot of time without incurring any sort of error. What could be taking time?
static int x = 1;
void JNICALL
compiled_method_load(jvmtiEnv *jvmti, jmethodID method, jint code_size,
        const void* code_addr, jint map_length, const jvmtiAddrLocationMap* map,
        const void* compile_info) {
    jvmtiError err;
    jclass klass;

    char* name = NULL;
    char* signature = NULL;
    char* generic_ptr = NULL;

    err = (*jvmti)->RawMonitorEnter(jvmti, lock);
    check_jvmti_error(jvmti, err, "raw monitor enter");

    err = (*jvmti)->GetMethodName(jvmti, method, &name, &signature,
            &generic_ptr);
    check_jvmti_error(jvmti, err, "Get Method Name");

    printf("\nCompiled method load event\n");
    printf("Method name %s %s %s\n\n", name, signature,
            generic_ptr == NULL ? "" : generic_ptr);

    if (strstr(name, "main") != NULL && x == 1) {
        x++;
        err = (*jvmti)->GetMethodDeclaringClass(jvmti, method, &klass);
        check_jvmti_error(jvmti, err, "Get Declaring Class");

        err = (*jvmti)->RetransformClasses(jvmti, 1, &klass);
        check_jvmti_error(jvmti, err, "Retransform class");

    }

    if (name != NULL) {
        err = (*jvmti)->Deallocate(jvmti, (unsigned char*) name);
        check_jvmti_error(jvmti, err, "deallocate name");
    }
    if (signature != NULL) {
        err = (*jvmti)->Deallocate(jvmti, (unsigned char*) signature);
        check_jvmti_error(jvmti, err, "deallocate signature");
    }
    if (generic_ptr != NULL) {
        err = (*jvmti)->Deallocate(jvmti, (unsigned char*) generic_ptr);
        check_jvmti_error(jvmti, err, "deallocate generic_ptr");
    }

    err = (*jvmti)->RawMonitorExit(jvmti, lock);
    check_jvmti_error(jvmti, err, "raw monitor exit");
}

Class File Load Hook

void JNICALL
Class_File_Load_Hook(jvmtiEnv *jvmti_env, JNIEnv* jni_env,
        jclass class_being_redefined, jobject loader, const char* name,
        jobject protection_domain, jint class_data_len,
        const unsigned char* class_data, jint* new_class_data_len,
        unsigned char** new_class_data) {
    jvmtiError err;
    unsigned char* jvmti_space = NULL;

    if (strstr(name, "Test") != NULL && x == 2) {
        char* args = "op";

        javab_main(2, args, class_data, class_data_len);

        err = (*jvmti_env)->Allocate(jvmti_env, (jlong)global_pos, &jvmti_space);
        check_jvmti_error(jvmti_env, err, "Allocate new class Buffer.");

        (void)memcpy((void*)jvmti_space, (void*)new_class_ptr, (int)global_pos);

        *new_class_data_len = (jint)global_pos;
        *new_class_data = jvmti_space;

        if ( new_class_ptr != NULL ) {
            (void)free((void*)new_class_ptr);
        }

#if DEBUG
        printf("Size of the class is: %d\n", class_data_len);
        for (int i = 0; i < class_data_len; i += 4) {
            if (i % 16 == 0)
                printf("\n");

            printf("%02x%02x  %02x%02x  ", new_class_data[i],
                    new_class_data[i + 1], new_class_data[i + 2],
                    new_class_data[i + 3]);
        }
        printf("\n");
        system("javap -c -v Test_debug");
#endif
        x++;
    }
}

此处javab_main返回正确的检测的char *数组.检测到的数组存储在全局变量new_class_ptr中,该变量被复制到new_class_data中.为了调试检测的输出,我还将检测的类打印在一个名为Test_debug的文件中,并在其上调用javap会产生所需的结果.

Here javab_main returns the instrumented char * array which is correct. The instrumented array is stored in a global variable new_class_ptr which is copied into new_class_data. To debug the output of the instrumentation, I also printed the instrumented class in a file called Test_debug and invoking javap on it produces desired result.

完整的代理文件在此处给出: Agent.c

The complete agent file is given here: Agent.c

            for (int i = 0; i < s; i++)
                for (int j = 0; j < s; j++) {
                    c2[i][j] = 0;
                    for (int k = 0; k < s; k++)
                        c2[i][j] += a[i][k] * b[k][j];
                }

仪器代码:(等效)

Thread[] threads = new Thread[NTHREADS];    
            for (int i = 0; i < NTHREADS ; i++) {
                final int lb = i * SIZE/NTHREADS;
                final int ub = (i+1) * SIZE/NTHREADS;
                threads[i] = new Thread(new Runnable() {

                    public void run() {
                        for (int i = lb; i < ub; i++)
                            for (int j = 0; j < SIZE; j++) {
                                c2[i][j] = 0;
                                for (int k = 0; k < SIZE; k++)
                                    c2[i][j] += a[i][k] * b[k][j];
                            }
                    }
                });
                threads[i].start();
            }

            // wait for completion
            for (int i = 0; i < NTHREADS; i++) {
                try {
                    threads[i].join();
                } catch (InterruptedException ignore) {
                }
            }

Java版本

openjdk version "1.8.0-internal-debug"
OpenJDK Runtime Environment (build 1.8.0-internal-debug-saqib_2016_12_26_10_52-b00)
OpenJDK 64-Bit Server VM (build 25.71-b00-debug, mixed mode)

推荐答案

我主要根据评论来构建此答案.仍有一些谜团尚未解决,但主要问题已解决.在我的情况下,字节码检测不会失败.实际上,它从未发生.根据理论,

I'm constructing this answer mainly from the comments. There are still some riddles unsolved but the main question has been resolved. Bytecode instrumentation does not fail in my case. It actually never happens. According to the theory,

执行功能的动态字节码检测 在函数的后续调用中.如果一个函数只有一个调用,则在使用JVM中的当前热交换技术执行该函数时将无法对其进行检测.

Dynamic bytecode instrumentation of an executing function takes place at subsequent call of the function. If a function has only one invocation, it cannot be instrumented while execution using current hotswap techniques in JVM.

我正在尝试检测仅具有一个功能的类,即main.我试图在运行时进行检测.这是主要问题.为了检查此参数的有效性,我尝试将代码放入另一个函数中,并从main循环调用它.它已安装完毕,并且一切正常.代理中无需进行任何更改.

I was trying to instrument a class which had only one function, i.e. main. I was trying to instrument that during runtime. This was the main problem. To check the validity of this argument, I tried to put my code in another function and called it from main in a loop. It got instrumented and everything worked. Nothing has to be changed in the agent.

这篇关于动态字节码检测失败,没有任何错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆