简体   繁体   中英

How to use ByteBuddy delegation for intercepting bootstrap class method

I'm using java agent and bytebuddy to intercept the "read" and "write" methods in FileIOStreams. A feature to implement is "to call the original methods under certain circumstances, else pass". Due to this, I need to have full control of the invoking flow using method delegation instead of wrapping it with Advice.

The method interception works fine when @Morph is not there, but it does not work when I add @Morph to parameters. I have tested with some other annotations:

adding @AllArguments, @This will not block the delegation, the method will run as my interceptor;

adding @Morph, @SuperCall will block the delegation. No exception will be thrown: the original method will run as what it used to be.

Here is the code I want to implement:

public static void mountAgent(Instrumentation inst) {

        new AgentBuilder.Default()
                .with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
                .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
                .with(AgentBuilder.TypeStrategy.Default.REDEFINE)
                .ignore(new AgentBuilder.RawMatcher.ForElementMatchers(nameStartsWith("net.bytebuddy.").or(isSynthetic()), any(), any()))
                .with(new AgentBuilder.Listener.Filtering(
                        new StringMatcher("java.io.FileInputStream", StringMatcher.Mode.EQUALS_FULLY)
                                .or(new StringMatcher("java.io.FileOutputStream", StringMatcher.Mode.EQUALS_FULLY)),
                        AgentBuilder.Listener.StreamWriting.toSystemOut()))
                .type(named("java.io.FileOutputStream"))
                .transform(new AgentBuilder.Transformer() {
                    @Override
                    public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder,
                                                            TypeDescription typeDescription,
                                                            ClassLoader classLoader,
                                                            JavaModule module) {
                        return builder.method(named("write").and(not(isNative())).and(takesArgument(0, byte[].class)))
                                .intercept(MethodDelegation
                                        .withDefaultConfiguration()
                                        .withBinders(Morph.Binder.install(Morphing.class))
                                        .to(WriteInterceptor.class));
                    }})
                .installOn(inst);
    }

(Code for appending interceptors to BootstrapClassLoaderSearch is skipped)

And following is my interceptors:

public interface Morphing<T> {
        T Object invoke(Object[] agrs);
    }

@SuppressWarnings("unused")
public static class WriteInterceptor {
    @RuntimeType
    public static void write(
//change the input here
            byte[] bytes,
            @AllArguments Object[] args,
            @Morph Morphing<Void> morphing
    ) throws Exception {
        if (true) {
            morphing.invoke(args);
        }
        else {
            // do something
            throw new Exception();
        }
    }
}

If the input of intercepting function is empty or only byte[] bytes, the delegation will work and Exception is thrown:

[Byte Buddy] IGNORE java.io.FileInputStream [null, module java.base, loaded=true]
[Byte Buddy] COMPLETE java.io.FileInputStream [null, module java.base, loaded=true]
[Byte Buddy] DISCOVERY java.io.FileOutputStream [null, module java.base, loaded=true]
[Byte Buddy] TRANSFORM java.io.FileOutputStream [null, module java.base, loaded=true]
[Byte Buddy] COMPLETE java.io.FileOutputStream [null, module java.base, loaded=true]

Exception: java.lang.Exception thrown from the UncaughtExceptionHandler in thread "main"

If the input is

byte[] bytes, @AllArguments Object[] args, @Morph Morphing morphing

or

@AllArguments Object[] args, @Morph Morphing morphing

the built-in write function is called and the output is

[Byte Buddy] IGNORE java.io.FileInputStream [null, module java.base, loaded=true]
[Byte Buddy] COMPLETE java.io.FileInputStream [null, module java.base, loaded=true]
[Byte Buddy] DISCOVERY java.io.FileOutputStream [null, module java.base, loaded=true]
[Byte Buddy] TRANSFORM java.io.FileOutputStream [null, module java.base, loaded=true]
[Byte Buddy] COMPLETE java.io.FileOutputStream [null, module java.base, loaded=true]

What's the reason that the delegation does not work after adding @Morph, but bytebuddy still says transform is completed? How to get the correct morphing for this case? Thank you!

I assume that your retransformation is already failing. Did you try to add a listener to the retransformation process. What does Exception: java.lang.Exception thrown from the UncaughtExceptionHandler in thread "main" mean?

One thing that I noticed is that you do not adjust the module graph. The java.base module will not be able to see your interceptor which is most likely loaded in the unnamed module of the bootstrap loader. Did you try to add assureReadEdgeTo in your transformation where you point to your interceptor class?

Also, please note that Advice does allow you to skip or even repeat a method execution. Have a look at the javadoc of the enter or exit methods. Typically, when instrumenting bootstrap classes advice tends to be more reliable.

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