Sumit Maheshwari
Sumit Maheshwari

Reputation: 1

Guidelines on how to use ByteBuddy correctly and efficiently

I looked at ByteBuddy documentation and have also looked at few of the Java Agent implementations that are using ByteBuddy. But I am still not very clear on what is the right way to use the ByteBuddy agent.

This is the code I used to bootstrap my agent:

public class ByteBuddyAgent {

    public static void premain(String arguments, Instrumentation instrumentation) {

        System.out.println("Bootstrapping ByteBuddy Agent");


        new AgentBuilder.Default()
                .with(new AgentBuilder.InitializationStrategy.SelfInjection.Eager())
                .type(ElementMatchers.hasSuperClass(ElementMatchers.nameContains("Level1")))
                .transform(new ByteBuddyTransformer()
                .installOn(instrumentation);

    }

    private static class ByteBuddyTransformer implements AgentBuilder.Transformer{

        @Override
        public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
                                                ClassLoader classLoader, JavaModule module) {

            return builder.method(ElementMatchers.named("printLevel"))
                    .intercept(Advice.to(MethodTracker.class));
        }
    }
}

This is my Advice class where I have tried different annotations supported by Advice and it worked fine.

public class MethodTracker {

    @Advice.OnMethodEnter
    public static Object onMethodBegin(@Advice.This Object invokedObject, @Advice.AllArguments Object[] arguments,
                                       @Advice.FieldValue("name") Object fieldValue, @Advice.Origin Object origin,
                                       @Advice.Local("localVariable") Object localVariable) {
        System.out.println("=======on Method Begin Running with ByteBuddy=======, " + invokedObject);
        System.out.println("======Printing arguments=======");
        for(Object obj: arguments){
            System.out.println("Argument:: " + obj);
        }
        localVariable = "Gunika";

        System.out.println("FieldValue:: " + fieldValue);
        System.out.println("Origin:: " + origin);
        return "ReturningStateFromOnMethodBegin";
    }

    @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
    public static void onMethodEnd(@Advice.This Object invokedObject, @Advice.Return Object returnValue,
                                   @Advice.FieldValue("name") Object fieldValue, @Advice.Enter Object state,
                                   @Advice.Local("localVariable") Object localVariable){
        System.out.println("=======on Method End Running with ByteBuddy======= " + invokedObject);

        System.out.println("Return value is " + returnValue);
        System.out.println("FieldValue:: " + fieldValue);
        System.out.println("FieldValue:: " + fieldValue);
        System.out.println("State:: " + state);
        System.out.println("LocalVariable:: " + localVariable);
    }
}

The questions that I have are as follows:

Upvotes: 0

Views: 2427

Answers (1)

Rafael Winterhalter
Rafael Winterhalter

Reputation: 44042

Advice.to(MethodTracker.class) is pretty expensive. If you instrument many classes and want to spend the memory, I'd recommend you to reuse the instance, it is immutable.

Other than that, @Advice.AllArguments is more expensive than using specialized advice classes that read particular arguments, if you can afford it. This is however rather a JVM question than a Byte Buddy-specific one. You can use -Dnet.bytebuddy.dump=/some/folder to see what byte code is generated. Expensiveness often lies burried what constructs you go for.

Upvotes: 0

Related Questions