如何在 asm 代码中正确使用 Instrumentation.retransformClasses()?

Posted

技术标签:

【中文标题】如何在 asm 代码中正确使用 Instrumentation.retransformClasses()?【英文标题】:How do I use Instrumentation.retransformClasses() correctly from within asm code? 【发布时间】:2012-03-01 22:41:14 【问题描述】:

我正在使用 asm 库来执行一些 Java 字节码修改 - 特别是修改我的类以实现新接口和相关方法。我目前的方法是通过 javaagent 使用核心 asm API。我想保留这种动态方法,而不是静态修改 .class 文件。

在更高的层面上,我的问题是,如果我选择修改从 B 扩展而来的类 A,我还需要修改 B。(鉴于我对类在 JVM 中如何加载的理解,我认为 B 类总是会在 A 课之前交给变压器。(如果我错了,请纠正我)。鉴于这种假设,我认为我需要返回并重新转换 B. 我的方法在这段代码中被捕获:

public byte[] transform(ClassLoader l, String name, Class<?> clazz, ProtectionDomain d, byte[] b) 
      throws IllegalClassFormatException 
    // **1**
    System.out.println("--->>> " + name);

    if (interestingClass(name)) 
        try 
            ClassReader cr = new ClassReader(b);
            ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
            PyClassVisitorAdapter pv = new PyClassVisitorAdapter(cw, name);
            cr.accept(pv, 0);

            // **2** Retrieve the superclass and try to transform that
            if (! "Ljava/lang/Object;".equals(pv.getSuperName())) 
                String cName = classJvmToCanonical(pv.getSuperName());
                Class[] classes = inst.getAllLoadedClasses();
                for (Class c : classes) 
                    if (c.getName().equals(cName)) 
                        inst.retransformClasses(c);
                        break;
                    
                
            

            // Dump the transformed class
            ClassReader cr2 = new ClassReader(cw.toByteArray());
            ClassWriter cw2 = new ClassWriter(cr2, 0);
            TraceClassVisitor tcv = new TraceClassVisitor(cw2, new PrintWriter(System.out));
            cr2.accept(tcv, 0);

            return cw2.toByteArray();
         catch (Exception ex) 
            ex.printStackTrace();
            return null;
        
     else 
        return b;
    

instInstrumentation 的句柄,在构造函数中传入)

我遇到困难的部分是在 cmets 中用**2** 标记的块。让我们再说一遍 A 扩展了 B 并且我对转换 A 很“感兴趣”。我期待的是我会看到超类 (B) 的名称被打印在**1** 上(但因为我没有被转换'认为它在第一遍时并不有趣)然后,一旦我到达**2** 并发现 A 的超类是 B,我应该尝试重新转换 B。此时我期待再次调用此方法(通过inst.retransformClasses()),我会看到B在**1**打印。但是,我没有。 (我已经添加了打印语句,并且确信我已经到达了 retransform 调用。我还检查了 Instrumentation.isRetransformClassesSupported()Instrumentation.isModifiableClass(c) 都返回 true)。

我相信我已正确设置代理;在清单中将 Can-Retransform-Classes 和 Can-Redefine-Classes 都设置为 true。此外,当我在代理的 premain 方法中将变压器添加到 Instrumentation 时,我会这样做:

public static void premain(String agentArgs, Instrumentation inst) 
    inst.addTransformer(new PyClassFileTransformer(inst), true);

关于我在这里做错了什么有什么见解吗?谢谢。

【问题讨论】:

你的问题解决了吗? @Jens 你可以去here 给我一些建议吗? 【参考方案1】:

您可以更改字节码检测策略,因此当加载 B 类时,您会找到它的所有子类并在此时决定是否需要现在修改 B 类。这可以通过在内存中维护类元数据存储库或缓存(即关于类层次结构的信息)来优化,因此您不必每次都加载元数据。

【讨论】:

以上是关于如何在 asm 代码中正确使用 Instrumentation.retransformClasses()?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用“asm volatile”编写 btr 指令

使用 libtool 和 autoconf 构建 ASM 代码

您如何在运行时使用 GCC 和内联 asm 检测 CPU 架构类型?

如何正确操作装配中的堆栈?

扩展 GCC asm 中多个输入和输出操作数的正确用法是啥?

如何将 ASM obj 文件与 C++ 程序集成?