byte-buddy icon indicating copy to clipboard operation
byte-buddy copied to clipboard

Advice can't work with constructor method in javaagent

Open linlinisme opened this issue 2 years ago • 3 comments

I try to use the advice to intercept all the methods excluding constructor method, the code below works well

 public static void main(String[] args) {
        ByteBuddyAgent.install();
        new ByteBuddy().redefine(OrderController.class)
                .visit(Advice.to(MethodTimeCostAdvice.class).on(isDeclaredBy(OrderController.class)
                        .and(not(isConstructor()))))
                .make().load(ClassLoader.getSystemClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
        new OrderController().hello3();
    }

the OrderController:

public class OrderController {

    Object lock1 = new Object();
    Object lock2 = new Object();

    public  String hello1() throws InterruptedException {
        synchronized (lock1) {
            Thread.sleep(10000);
            synchronized (lock2) {
                return "hello1, it is nice to meet you !";
            }
        }
    }

    public  String hello2() throws InterruptedException {
        synchronized (lock2) {
            Thread.sleep(10000);
            synchronized (lock1) {
                return "hello1, it is nice to meet you !";
            }
        }
    }


    public String hello3() {
        System.out.println("hello3");
        return "hello3";
    }
    
}

but I change it to

 public static void main(String[] args) {
        new OrderController().hello3();
    }

and add the VM options to run : -javaagent:target/spring-object-trace-agent-1.0.jar. I use the IDE debug find there is a error: java.lang.IllegalStateException: Cannot represent static com.example.orderdemo.controller.OrderController() as given method constant
here is the agent code and interceptor code:

public class MethodTimeCostAdvice {

   @Advice.OnMethodEnter
   public static long enter(){
      return System.currentTimeMillis();
   }

   @Advice.OnMethodExit
   public static void exit(@Advice.Enter long startTime,
                           @Advice.Origin Method method,
                           @Advice.AllArguments Object[] args,
                           @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result,
                           @Advice.This Object instance){
      long cost = System.currentTimeMillis() - startTime;
      String methodSignature = getMethodSignature(instance, method, args);
      System.out.println(String.format("方法: %s \n耗时: %dms \n入参: %s \n返回结果: %s", methodSignature, cost, getArgs(args),result));
   }

   public static String getArgs(Object[] args){
      if(args.length == 0){
         return "void";
      }
      return Arrays.stream(args).map(Object::toString).collect(Collectors.joining(","));
   }

   public static String getMethodSignature(Object instance, Method method, Object[] parameters) {
      StringBuilder builder = new StringBuilder();
      builder.append(instance.getClass().getCanonicalName());
      builder.append(".");
      builder.append(method.getName());
      builder.append("(");
      for (int i = 0; i < parameters.length; i++) {
         String[] split = parameters[i].getClass().getCanonicalName().split("\\.");
         builder.append(split[split.length - 1]);
         if (i != parameters.length - 1) {
            builder.append(", ");
         }
      }
      builder.append(")");
      return builder.toString();
   }
}
public class SpringObjectTraceAgent {
    public static void premain(String args, Instrumentation instrument) {
        System.out.println("SpringObjectTraceAgent start.... ");
        new AgentBuilder.Default()
                .with(AgentBuilder.TypeStrategy.Default.REDEFINE)
                .type(ElementMatchers.nameStartsWith("com.example"))
                .transform(new AgentBuilder.Transformer() {
                    @Override
                    public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule) {
                           return builder
                                   .visit(Advice.to(MethodTimeCostAdvice.class)
                                           .on(isDeclaredBy(typeDescription)
                                                   .and(not(isConstructor()))));
                    }
                }).installOn(instrument);
    }
}

I don't know why in this way it can't work.

My local env config:

JDK 1.8

  <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>1.12.10</version>
        </dependency>


        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy-agent</artifactId>
            <version>1.12.10</version>
        </dependency>

linlinisme avatar May 28 '22 15:05 linlinisme

You are requesting a Method which cannot represent a Constructor. Rather use an Executable, or create two advices and match them accordingly.

raphw avatar May 28 '22 17:05 raphw

Okay, thanks! Still, there is a question I want to ask: byteBuddy will catch the exception when redefining a class is not okay and it just give up the operation. for example:

 public static void main(String[] args) {
        premain(null, ByteBuddyAgent.install());
        OrderController orderController = new OrderController();
        orderController.hello3();
    } 

Actually, I want to throw the exception when I am testing the code so I can know what is wrong quickly. Now I get what error messages are through the IDE debug tool, which is very inefficient for me.

linlinisme avatar May 29 '22 01:05 linlinisme

It's the instrumentation API that catches the exception. Byte Buddy cannot do anything here.

You can however register a listener and get the exception from there. You would then need to make sure to fail your test.

raphw avatar May 29 '22 06:05 raphw

You are requesting a Method which cannot represent a Constructor. Rather use an Executable, or create two advices and match them accordingly.

Sorry to bother but how to correct matcher in the demo, I have met the same problem.

all4you avatar Aug 28 '23 02:08 all4you