【问题标题】:Problems with Java Agent for Bytecode InstrumentationJava 字节码检测代理的问题
【发布时间】:2017-07-07 15:26:59
【问题描述】:

这是我第一次实现 Java 代理,我正在尝试学习一些关于字节码检测的知识。在阅读了几个介绍和教程后,我编写了一个包含两个类(夏季和应用程序)的小型应用程序。现在我想通过 premain 方法运行一个 java 代理来显示使用以下代码的执行路径:

public class TestJavaAgent {
    public static void premain(String agentArgument,
                               Instrumentation instrumentation){
        instrumentation.addTransformer(new ClassFileTransformer() {

            @Override
            public byte[] transform(ClassLoader classLoader, String s, Class<?> aClass, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {

                ClassPool cp = ClassPool.getDefault();
                    try {
                        CtClass cc = cp.get("Summer");
                        CtMethod methods [] = cc.getMethods();

                        for( CtMethod method : methods){
                            System.out.println("Entering "+method.getName());
                            method.addLocalVariable("elapsedTime", CtClass.longType);
                            method.insertBefore("elapsedTime = System.currentTimeMillis();");
                            method.insertAfter("{elapsedTime = System.currentTimeMillis() - elapsedTime;"
                                    + "System.out.println(\"Method Executed in ms: \" + elapsedTime);}");
                        }
                        return cc.toBytecode();

                    } catch (Exception ex) {
                        return bytes;
                    }
            }
        });
    }
}

我通过java -javaagent{Agent JAR} -jar {Application Jar} 启动了代理,但它没有打印任何插入的消息。调试代码后,我意识到“ClassPool.getDefault()”之后的所有内容都无法到达,但我不知道为什么。有人可以帮我吗?

【问题讨论】:

    标签: java bytecode instrumentation agent


    【解决方案1】:

    转换器应该转换作为参数传递的类,而不是您喜欢的任意类。并且在注册后,它将为加载的所有类调用,包括你自己使用的类(最初是ClassPool)。所以你正在创建一个循环依赖。

    您必须检查类名参数并等到您的方法已为您要转换的类调用。对于所有其他类,只需返回 null

    public class TestJavaAgent {
        public static void premain(String agentArgument, Instrumentation instrumentation) {
            instrumentation.addTransformer(new ClassFileTransformer() {
    
                @Override
                public byte[] transform(ClassLoader classLoader,
                    String className, Class<?> aClass, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {
    
                    if(!className.equals("Summer")) return null;
    
                    ClassPool cp = ClassPool.getDefault();
                    try {
                        // use the class bytes your received as parameter
                        cp.insertClassPath(new ByteArrayClassPath(className, bytes));
    
                        CtClass cc = cp.get("Summer");
                        CtMethod[] methods = cc.getMethods();
    
                        for( CtMethod method : methods){
                            System.out.println("Entering "+method.getName());
                            method.addLocalVariable("elapsedTime", CtClass.longType);
                            method.insertBefore("elapsedTime = System.currentTimeMillis();");
                            method.insertAfter("{elapsedTime = System.currentTimeMillis() - elapsedTime;"
                                    + "System.out.println(\"Method Executed in ms: \" + elapsedTime);}");
                        }
                        return cc.toBytecode();
    
                    } catch (Exception ex) {
                        return null;
                    }
                }
            });
        }
    }
    

    请注意,如果您没有更改任何内容,则返回 null 比返回原始数组更可取,因为这样 JVM 可以立即识别您没有更改任何内容,而无需查看数组内容。

    【讨论】:

      猜你喜欢
      • 2016-10-12
      • 2011-11-11
      • 1970-01-01
      • 1970-01-01
      • 2021-05-18
      • 2018-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多