简体   繁体   中英

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. 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:

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. After debugging the code i realized everything after "ClassPool.getDefault()" will not be reached but i dont know why. 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). 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 .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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