简体   繁体   English

Java字节码检测代理问题

[英]Problems with Java Agent for Bytecode Instrumentation

Its is my first time implementing a java agent and im trying to learn something about bytecode instrumentation. 这是我第一次实现Java代理,并尝试学习有关字节码检测的知识。 After reading several introductions and tutorials i coded a small Application with two classes (Summer and Application). 阅读了几篇介绍和教程后,我编写了一个带有两个类(夏季和应用程序)的小型应用程序。 Now i want to run a java agent via premain method to show the execution path using the following code: 现在我想通过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;
                    }
            }
        });
    }
}

I started the Agent via java -javaagent{Agent JAR} -jar {Application Jar} but it did not print anything of the inserted messages. 我通过java -javaagent{Agent JAR} -jar {Application Jar}启动了代理,但是它没有显示任何插入的消息。 After debugging the code i realized everything after "ClassPool.getDefault()" will not be reached but i dont know why. 调试代码后,我意识到“ ClassPool.getDefault()”之后的所有内容都无法实现,但我不知道为什么。 Can someone help me? 有人能帮我吗?

The transformer is supposed to transform the class being passed as parameter, not some arbitrary class you like. 转换器应该转换作为参数传递的类,而不是您喜欢的任意类。 And after registration, it will be called for all classes that are loaded, including the classes you are using yourself ( ClassPool at first). 注册后,将为所有已加载的类(包括您自己使用的类)(第一个是ClassPool )调用此ClassPool So you are creating a circular dependency. 因此,您正在创建循环依赖项。

You have to check the class name argument and wait until your method has been called for the class you want to transform. 您必须检查类名参数,然后等待直到要转​​换的类的方法被调用为止。 For all other classes, just return null . 对于所有其他类,只需返回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;
                }
            }
        });
    }
}

Note that returning null is preferred to returning the original array if you didn't change anything as then the JVM can immediately recognize that you didn't change anything, without looking into the array contents. 请注意,如果您不进行任何更改,则返回null胜于返回原始数组,因为JVM可以立即识别出您没有进行任何更改,而无需查看数组内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM