around advice for method calls

979 views
Skip to first unread message

EmGi

unread,
Mar 6, 2016, 8:16:50 AM3/6/16
to Byte Buddy
Hi All,

I tried two different ways to build an around advice for method calls but none of them works.

[1] I tried to intercept a call and invoke it in the interceptor:
new AgentBuilder.Default().type(ElementMatchers.any())
         
.transform(new AgentBuilder.Transformer() {

                   
@Override
                   
public Builder<?> transform(Builder<?> builder,
                           
TypeDescription arg1, ClassLoader arg2) {

                       
return builder
                               
.method(ElementMatchers.any())
                               
.intercept(MethodDelegation.to(MethodProfiler.class));
                   
}

         
}).installOn(ByteBuddyAgent.install());

the interceptor is
public static void intercept(@Origin Method m, @SuperCall Callable<?> zuper) throws Exception {
       
System.out.println("before " + m.getName());
        zuper
.call();
       
System.out.println("after " + m.getName());
}

[2] I also tried with the following code:
new AgentBuilder.Default().type(ElementMatchers.any())
       
.transform(new AgentBuilder.Transformer() {

                   
@Override
                   
public Builder<?> transform(Builder<?> builder,
                           
TypeDescription arg1, ClassLoader arg2) {

                       
return builder
                               
.method(ElementMatchers.any())
                               
.intercept(MethodDelegation.to(MethodProfiler.class)
                               
.filter(ElementMatchers.named("before"))
                               
.andThen(SuperMethodCall.INSTANCE
                               
.andThen(MethodDelegation.to(MethodProfiler.class)
                               
.filter(ElementMatchers.named("after"))
                               
)));
                   
}
        installOn
(ByteBuddyAgent.install());

and before and after interceptors are:
public static void before(@Origin Method method) {
       
System.out.println("intercepted before " + method.getName());
}

public static void after(@Origin Method method) {
       
System.out.println("intercepted after " + method.getName());
}


Does anyone know whats is the problem?


Rafael Winterhalter

unread,
Mar 6, 2016, 4:16:13 PM3/6/16
to EmGi, Byte Buddy
Hello,
you need to return the original value in order to make this work. For
your interceptor, this would mean to write an interceptor such as:

@RuntimeType
public static Object intercept(@Origin Method m, @SuperCall
Callable<?> zuper) throws Exception {
System.out.println("before " + m.getName());
try {
return zuper.call();
} finally {
System.out.println("after " + m.getName());
}
}

The RuntimeType annotation tells Byte Buddy to cast the return type to
the method's return type (including boxing). Since you call the
original method from your interceptor, you have an implicit guarantee
that this works.

For the fact that you do not return an appripriate return type from
your "after" interceptor, the second example does not work either.
Your interceptor only supports methods that return void.

Does this answer your question? Feel free to get back to me if you
have any further questions.
Best regards, Rafael
> --
> You received this message because you are subscribed to the Google Groups
> "Byte Buddy" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to byte-buddy+...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

EmGi

unread,
Mar 6, 2016, 5:16:52 PM3/6/16
to Byte Buddy
Thanks Rafael! Yes, it is working but it also raises the following exception:

[Byte Buddy] TRANSFORM Calculator
[Byte Buddy] COMPLETE Calculator
[Byte Buddy] IGNORE Calculator$auxiliary$iomGvCCq
[Byte Buddy] COMPLETE Calculator$auxiliary$iomGvCCq
[Byte Buddy] IGNORE Calculator$auxiliary$dlCUpsmI
[Byte Buddy] COMPLETE Calculator$auxiliary$dlCUpsmI
[Byte Buddy] IGNORE Calculator$auxiliary$nuZtDMa1
[Byte Buddy] COMPLETE Calculator$auxiliary$nuZtDMa1
[Byte Buddy] IGNORE Calculator$auxiliary$mwTSS44U
[Byte Buddy] COMPLETE Calculator$auxiliary$mwTSS44U
[Byte Buddy] IGNORE Calculator$auxiliary$v46iNdxd
[Byte Buddy] COMPLETE Calculator$auxiliary$v46iNdxd
before sum
this is Sum
after sum
sum
is: 9
[Byte Buddy] ERROR java.lang.Shutdown
java
.lang.IllegalStateException: Injecting classes into the bootstrap class loader was not enabled
    at net
.bytebuddy.agent.builder.AgentBuilder$Default$BootstrapInjectionStrategy$Disabled.make(AgentBuilder.java:3790)
    at net
.bytebuddy.agent.builder.AgentBuilder$Default$Transformation$Simple$Resolution$BootstrapClassLoaderCapableInjectorFactory.resolve(AgentBuilder.java:4324)
    at net
.bytebuddy.agent.builder.AgentBuilder$InitializationStrategy$SelfInjection$Dispatcher$Split.register(AgentBuilder.java:1100)
    at net
.bytebuddy.agent.builder.AgentBuilder$Default$Transformation$Simple$Resolution.apply(AgentBuilder.java:4240)
    at net
.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:4536)
    at sun
.instrument.TransformerManager.transform(TransformerManager.java:188)
    at sun
.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:424)
[Byte Buddy] COMPLETE java.lang.Shutdown
[Byte Buddy] ERROR java.lang.Shutdown$Lock
java
.lang.IllegalStateException: Injecting classes into the bootstrap class loader was not enabled
    at net
.bytebuddy.agent.builder.AgentBuilder$Default$BootstrapInjectionStrategy$Disabled.make(AgentBuilder.java:3790)
    at net
.bytebuddy.agent.builder.AgentBuilder$Default$Transformation$Simple$Resolution$BootstrapClassLoaderCapableInjectorFactory.resolve(AgentBuilder.java:4324)
    at net
.bytebuddy.agent.builder.AgentBuilder$InitializationStrategy$SelfInjection$Dispatcher$Split.register(AgentBuilder.java:1100)
    at net
.bytebuddy.agent.builder.AgentBuilder$Default$Transformation$Simple$Resolution.apply(AgentBuilder.java:4240)
    at net
.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:4536)
    at sun
.instrument.TransformerManager.transform(TransformerManager.java:188)
    at sun
.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:424)
    at java
.lang.Shutdown.<clinit>(Shutdown.java:61)
[Byte Buddy] COMPLETE java.lang.Shutdown$Lock

Do you have any idea that what might be wrong?

Rafael Winterhalter

unread,
Mar 7, 2016, 4:33:30 AM3/7/16
to EmGi, Byte Buddy
As the error message suggests: you did not enable bootstrap injection.
Injecting code into the bootstrap class loader is a bit more tricky
than when using normal class loaders. (The bootstrap class loader is
represented by "null"). You can therefore:

1. Limit interception to non-bootstrap classes. For this, simply
specify a second argument: "type(ElementMatchers.any(),
ElementMatchers.not(ElementMatchers.isBootstrapClassLoader()))

2. Enable bootstrap injection what is part of the AgentBuilder API.

The question is: Do you really want to instrument classes like
java.lang.Shutdown? This can change your application's behavior
drastically.

Best regards, Rafael

mark.w...@gmail.com

unread,
May 10, 2016, 9:21:55 AM5/10/16
to Byte Buddy, ghafa...@gmail.com
Hi Rafael,
I'm just getting started with ByteBuddy.

Can you tell me a little about whether it's possible to perform method interception in a zero-garbage manner?

I assume the Callable<?> passed to the interceptor is created for each invocation?

Also, if my intercepted method returns a primitive type, will the return value be boxed/unboxed during the interception?

Thanks


Mark

Rafael Winterhalter

unread,
May 10, 2016, 9:35:12 AM5/10/16
to mark.w...@gmail.com, Byte Buddy, Mohammad Ghafari
Hei Mark,

if you really only want to do around advice, you can use the newly added Advice component (look up the javadoc and have a look at the test cases for examples).
This way, you can inline the byte code of your advice methods directly into the instrumented method where it is easily possible to avoid object allocation altogether.

However, if you want to use the (more powerful) interception API, I would not worry about these allocations too much. Those objects are extremely short lived and only exist locally. The JVM is very good at figuring this out and will erase the object allocation alltogether. The byte code is not really executed by the JVM, it applies many optimizations of "inefficient code" and this one is an easy target for modern VMs.

Cheers, Rafael

mark.w...@gmail.com

unread,
May 17, 2016, 4:18:48 AM5/17/16
to Byte Buddy, mark.w...@gmail.com, ghafa...@gmail.com
Hi Rafael,
I'm aware of the opportunities for escape analysis, and the invariants that must hold true to stack allocation to take place. I guess I'll have to try it out and look at the compiler logs to make sure.

In low-latency systems, we do our best to avoid allocations altogether. I'll see how I get on with interceptions, and I expect I'll probably just end up using ASM to write the bytecode manually.



Thanks for your answer.


Mark
Reply all
Reply to author
Forward
0 new messages