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

2024-06-18

客观的

我正在使用 JVMTI 代理进行动态字节码检测。我必须检测那些“热”的方法,即调用 JIT 编译器的方法。为此,我听了一个CompiledLoadEvent并在其回调函数内调用RetransformClasses。这反过来又调用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

当我通过调用来检测类文件时RetransformClasses进而ClassFileLoadHook,一切工作正常,但没有生成任何线程,因此没有发生有效的检测。 VM甚至需要很长时间才能执行原始代码。

我使用 -XX:+TraceClassLoading 仔细检查了两个仪器。在这两种情况下都会加载所有重新转换的类。即使我在运行时生成的类也会被加载,但不会发生检测。以下是类加载跟踪的输出:

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

我在运行时生成第二个类并将其加载到虚拟机中,但我没有生成任何线程。

问题

  • 鉴于我对问题的理解(很有可能 我错了),为什么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++;
    }
}

Here javab_main返回已检测的char *数组是正确的。检测后的数组存储在全局变量中new_class_ptr被复制到new_class_data。为了调试仪器的输出,我还在名为 Test_debug 的文件中打印了仪器类并调用javap就可以产生想要的结果。

完整的代理文件如下:Agent.c https://gist.github.com/saqibahmed515/9864ec018edeca14e14ce518131c753c

原始代码:

            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)

我主要根据评论来构建这个答案。虽然还有一些未解之谜,但主要问题已经解决了。字节码检测没有失败就我而言。它实际上永远不会发生。根据理论,

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

动态字节码检测失败且没有任何错误 的相关文章

随机推荐

  • 向无锁队列添加阻塞函数

    我有一个基于循环缓冲区的无锁多生产者 单消费者队列 到目前为止 它只有非阻塞push back and pop front 来电 现在我想添加这些调用的阻塞版本 但我想尽量减少这对使用非阻塞版本的代码性能的影响 也就是说 它不应该将它们变成
  • 用反射“铸造”

    考虑以下示例代码 class SampleClass public long SomeProperty get set public void SetValue SampleClass instance decimal value valu
  • ABPeoplePickerNavigationController - 在不使用私有方法/属性的情况下删除“取消”按钮?

    我正在使用 ABPeoplePickerNavigationController 它是 UINavigationController 的子类 在我使用它的上下文中 右侧的默认导航栏按钮 取消 没有任何意义 我找不到禁用或隐藏它的方法 并且无
  • 隐藏特定用户的报告

    我做了一个向下钻取报告包含 5子报告 所有这些都使用连接超级链接部分 我希望用户只查看主要内容向下钻取报告 我的意思是第一个标签向下钻取 如果我设置仅执行的许可ROLE USER 该特定报告不起作用 抛出类似错误Access Denied
  • 附加到已经运行的 JVM

    有没有办法附加到已经运行的 JVM 例如 在 JNI 中您可以使用JNI CreateJavaVM创建一个虚拟机并运行一个 jar 并检查它的所有类 但是 如果 jar 已经在运行 我找不到附加到其 JVM 并与其类通信或获取其的方法env
  • Jetty - 找不到 Servlet

    我是 Servlet 新手 想通过使用 Eclipse 的 Jetty 插件来调用一个简单的 Servlet 我可以调用 index html 但是当尝试访问 Servlet 时 我得到 HTTP 错误 404访问 ProjectServl
  • 如何在vb.net中实现事务方式?

    我使用 VB net 200 开发一个连接到 MS Access 数据库的应用程序 我使用 TableAdapter 和 Dataset 连接到 Access DB 文件 我需要实现一个简单的事务方法 提交 回滚 来保存到数据库 有没有一种
  • libstdc++新标准库功能支持表

    是否有一个表格指定新标准 11 和 14 支持previousGNU libstdc 的版本 我找到了这样的表compiler https gcc gnu org projects cxx0x html并为当前的库状态 https gcc
  • 是否可以将一个 CSS 类的优先级设置为高于另一个 CSS 类的优先级?

    假设我有一个 div 它使用两个 css 类 这两个类都使用文本对齐 但一个居中 另一个右对齐 是否可以指定一个类别优先于另一个类别的内容 指定更具体的选择器 例如在其前面添加 ID 前缀或在类前面添加节点名 将其分配在其他班级之后 如果两
  • 在 JBoss EAP 5.1 中为特定 MDB 配置 maxSession 属性

    如何使 MDB 的 maxSession 值可由用户配置 有一个 MDB 侦听来自特定队列的消息 它被定义为注释 ActivationConfigProperty propertyName maxSession propertyValue
  • 如何在 grails 域类中设置默认值

    有没有办法为域类属性设置默认值 我有一堂课叫PayMethod 我想要的地方name属性默认为 Cash 我在创建此表时想要这个默认值 这可以使用约束吗 package abc import util UserUtil import emb
  • 如何得到决策树的ROC曲线?

    我正在努力寻找ROC曲线 and AUROC曲线对于决策树 我的代码是这样的 clf fit x y y score clf fit x y decision function test col pred clf predict proba
  • 如何获得打开另一个活动的按钮?

    我已在活动 XML 文件中添加了一个按钮 但无法用它来打开我的其他活动 有人可以逐步告诉我如何做到这一点吗 A 确保您的其他活动已在清单中声明
  • 检查 Future 是否完成

    在 m3 之前 您可以使用 completer future isComplete 检查未来是否已完成 但这似乎已经消失了 有替代品吗 或者我需要自己保存它 似乎在 CompleterImpl 内部仍然有一个字段 isComplete 但它
  • 由于找不到“setup.py”或“pyproject.toml”,pip 安装失败

    我有一个 sh 脚本行 作为 Jenkinsfile groovy 脚本的一部分 它确实 sh python3 m venv venv sh source venv bin activate withCredentials username
  • OpenCv 与 Android studio 1.3+ 使用新的 gradle - 未定义的参考

    我在使用原生 OpenCv 2 4 11 3 0 0 也可以 和 Android Studio 1 3 以及新的 ndk 支持时遇到问题 所有关于 mk 文件的教程 但我想将它与新的实验性 gradle 一起使用 使用 Kiran 答案An
  • 在mysql中的单个查询中更新多个表

    我有三个查询 我想要一个 这是我的查询 UPDATE tab1 SET a WHERE id 3 UPDATE tab2 SET b WHERE id 9 UPDATE tab3 SET c WHERE id 5 您可以尝试下面的代码 UP
  • 使用jquery和javascript水平滑动div

    我正在尝试在 关于我们 页面中显示我的推荐 我在那里用 3divs水平样式可同时显示 3 个推荐 这里我想做的是我想添加一些滑动效果与查询消失 1st 3divs然后需要加载2nd 3divs与不同的推荐 等等 我做了HTML和CSS 但不
  • 包含 ASP.Net Identity 2.0 UserManager.Users.ToListAsync 和 UserManager.FindByIdAsync 上的属性

    我正在尝试实现 Asp Net Identity 2 0 到目前为止 在以下人员的帮助下我做得很好这个博客 http typecastexception com post 2014 06 22 ASPNET Identity 20 Cust
  • 动态字节码检测失败且没有任何错误

    客观的 我正在使用 JVMTI 代理进行动态字节码检测 我必须检测那些 热 的方法 即调用 JIT 编译器的方法 为此 我听了一个CompiledLoadEvent并在其回调函数内调用RetransformClasses 这反过来又调用Cl