当前位置: 首页 > 知识库问答 >
问题:

动态字节码检测失败而没有任何错误

尤飞尘
2023-03-14

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

目前,我正在安装我的类来生成一些线程。我也听线程启动和打印他们在我的代理。在类加载时使用简单的ClassFileLoadHook(不使用RetransformClasses),我的插装可以完美地工作并生成新的线程。当ClassFileLoadHook在类加载时仪器时,我得到以下输出:

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即使执行原始代码也需要很长的时间。

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

我在运行时生成第二个类,它加载到VM,但我没有得到任何线程产生。

  • 鉴于我对这个问题的理解(很有可能我错了),为什么ClassFileLoadHook在加载期间成功地重新转换了类,但在调用JIT时却不能正确地运行?
  • 仅编写retransformClasses函数,并使用空的ClassFileLoadHook回调,也需要大量时间而不会导致任何类型的错误。什么需要时间?
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");
}
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会产生所需的结果。

完整的代理文件如下所示: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) {
                }
            }
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)

共有1个答案

晏晨朗
2023-03-14

我主要从评论中构造这个答案。还有一些谜团没有解开,但主要的问题已经解决了。字节码检测在我的情况下不会失败。实际上从来没有发生过。该理论认为,

执行函数的动态字节码检测在函数的后续调用中进行。如果一个函数只有一次调用,则在使用JVM中当前的hotswap技术执行时不能对其进行检测。

我试着给一个只有一个功能的类装乐器,即main。我试图在运行时调试它。这是主要的问题。为了检查这个参数的有效性,我尝试将我的代码放在另一个函数中,并在一个循环中从main调用它。它被仪器化了,一切都工作了。代理中没有什么需要改变的。

 类似资料:
  • 我对头衔认证有异议 这是我的密码 以下是传递的错误:Launchbrowser失败:titlevarification组织。testng。TestNgeException:无法将@Test注释的方法[TitlePrification]与[interface org.openqa.selenium.WebDriver]一起注入。有关本机依赖项注入的更多信息,请参阅https://testng.org

  • 问题内容: 我读取了大约1000个文件名,其中一些文件以UTF8编码,而某些文件为CP1252。 我想将它们全部解码为Unicode,以便在脚本中进行进一步处理。有没有一种方法可以使源编码正确解码为Unicode? 例: 问题答案: 如果您的文件位于和中,则有一种简单的方法。 否则,有一个字符集检测库。 Python-检测字符集并转换为utf-8 https://pypi.python.org/p

  • 一个“通过”测试但配置失败的示例。 失败的配置:@afterclass tearDown java.lang.assertionerror:java.lang.assertionerror:expected[true],但在)在org.testng.internal.MethodInvocationHelper.invokeMethodCommissioningTimeout(methodInvo

  • 我昨天在Ubuntu上从1.5升级到了Android Studio 2.0。当我在Android Studio 2.0中进行单元测试时,它会显示终止的测试状态,即使所有测试都通过了。有时它只显示一些通过的测试。我很确定我的代码运行正常,测试正确,因为我在Android Studio 1.5上有相同的设置。 例如,我有22个测试。有时它显示所有22个测试都通过了,在下一次运行时,它显示22个测试中的

  • 我正在尝试使用来生成网页、JS脚本和其他东西。看起来像这样: 在大多数情况下,它是有效的。Chrome成功从该服务器接收页面。 但是今天我注意到,如果有人足够快地发出大量请求,服务器就会开始拒绝连接。所以Chrome给了我这个: 在服务器端,我看不到我的try-catch中的任何错误。 我做错了什么?

  • 我刚刚更新到Xcode 6 beta 6,我的代码有以下问题,以前我的代码可以正常工作 提醒子对象的类型为AnyObject?!当我试图将其向下转换为字符串时,它会崩溃,并显示“Swift dynamic cast failure”消息。println打印的值是:可选(嘿,它的测试!) 所以问题是如何正确地投射任何对象?!斯威夫特?