微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用Byte-Buddy拦截java.lang.Object waitlong本机方法

如何解决使用Byte-Buddy拦截java.lang.Object waitlong本机方法

我正在尝试记录“ Object.wait”方法调用的计数。截取“公共最终本机无效等待(长超时)”对ByteBuddy无效。我做了一些测试:

test1,test2打印错误java.lang.IllegalStateException:无法为公共调用超级(或认)方法 最后的void java.lang.Object.wait(long,int)抛出java.lang.InterruptedException; java.lang.IllegalStateException:无法为公共最终原生void java.lang.Object.wait(long)调用超级(或认)方法抛出java.lang.InterruptedException

test3无效,什么也不打印。

test4成功。

这是我的测试代码

Javaagent:


final public class AgentBootstrap {

     public static class TestAdvice {
        @Advice.OnMethodEnter
        public static void before(@Advice.Origin String methodIns) {
            System.out.println("Byte-Buddy enter:" + methodIns);
        }

        @Advice.OnMethodExit
        public static void after(@Advice.Origin String methodIns) {
            System.out.println("Byte-Buddy after:" + methodIns);
        }
      }
    
     public static void premain(String agentArgs,Instrumentation inst) throws Exception{
         AgentBuilder agentBuilder = new AgentBuilder.Default()
                .with(AgentBuilder.RedeFinitionStrategy.RETRANSFORMATION)
                .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
                .with(AgentBuilder.Typestrategy.Default.REBASE)
                .enableNativeMethodPrefix("$$mynative_")
                .with(AgentBuilder.Listener.StreamWriting.toSystemError().withErrorsOnly())
                .with(AgentBuilder.InstallationListener.StreamWriting.toSystemError())
                .ignore(nameStartsWith("net.bytebuddy."));
        // test1(agentBuilder,inst);
        // test2(agentBuilder,inst);
        // test3(agentBuilder,inst);
        // test4(agentBuilder,inst);
        
    }
     
    /**
     * intercept method: public final native void wait(long timeout)
     * print error: java.lang.IllegalStateException: Cannot call super (or default) method for public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
     */
    private static void test1(AgentBuilder agentBuilder,Instrumentation inst) throws Exception {
        agentBuilder.disableClassFormatChanges()
        .type(is(Object.class))
        .transform(new Transformer() {
            @Override
            public Builder<?> transform(Builder<?> builder,TypeDescription typeDescription,ClassLoader classLoader,JavaModule module) {
              return builder.method(named("wait").and(takesArguments(1))).intercept(Advice.to(TestAdvice.class));
            }
        })
        .installOn(inst);
        inst.retransformClasses(Object.class);
    }
    /**
     * intercept method: public final void wait(long timeout,int nanos)
     * print error: java.lang.IllegalStateException: Cannot call super (or default) method for public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
     */
    private static void test2(AgentBuilder agentBuilder,JavaModule module) {
              return builder.method(named("wait").and(takesArguments(2))).intercept(Advice.to(TestAdvice.class));
            }
        })
        .installOn(inst);
        inst.retransformClasses(Object.class);
    }
    
    /**
     * intercept method: public final native void wait(long timeout)
     * invalid,print nothing
     */
    private static void test3(AgentBuilder agentBuilder,JavaModule module) {
                return builder.visit(Advice.to(TestAdvice.class).on(named("wait").and(takesArguments(1))));
            }
        })
        .installOn(inst);
        inst.retransformClasses(Object.class);
    }
    /**
     * intercept method: public final void wait(long timeout,int nanos)
     * success
     */
    private static void test4(AgentBuilder agentBuilder,Instrumentation inst) throws Exception {
        agentBuilder.type(is(Object.class))
        .transform(new Transformer() {
            @Override
            public Builder<?> transform(Builder<?> builder,JavaModule module) {
                return builder.visit(Advice.to(TestAdvice.class).on(named("wait").and(takesArguments(2))));
            }
        })
        .installOn(inst);
    }
     
}

测试:

public static void main(String[] args) throws Exception {
    new Thread(() -> {
        Object obj = new Object();
        while (true){
            try {
                synchronized (obj) {
                    obj.wait(1000);
                    obj.wait(1000,1);
                }
            }catch (Exception e) {
                e.printstacktrace();
            }
        }
    }).start();
}

:((Test4代码适用于“ public final void wait()”方法,它可以工作,但不适用于“ public final native void wait()”方法。我想知道如何截取“ public final void原生void wait()”方法

解决方法

为此,Byte Buddy版本1.10.14实际上存在一个错误。重新创建的方法必须具有修饰符private final,而不仅仅是private,否则,JVM将不会接受重新创建的方法。

该修补程序已经在主机上,如果您自己构建Byte Buddy,则现在应该可以运行代理。下一版本1.10.15将包含此修复程序。

但是请注意,JVM不再支持从版本13开始添加私有(最终)方法。可以使用 -XX:+AllowRedefinitionToAddDeleteMethods重设旧行为。但是,该选项已被弃用,并且在以后的JVM版本中将不再存在。如果没有这样的可能性来重命名本机方法,则无论有没有字节好友,这种行为在JVM上都不再可能。

(为公开起见,我是Byte Buddy的维护者。)

,

免责声明:这不是最终答案,只是讨论所需的初步版本。

实际上,我们所谈论的是BB的一部分,我也不知道,因为我也不是专家。但是我很好奇,尝试过类似的

.transform((builder,typeDescription,classLoader,module) ->
  builder
    .method(named("wait").and(takesArguments(long.class)))
    .intercept(MethodDelegation.to(WaitAdvice.class))
)

结合

public class WaitAdvice {
  public static long COUNT = 0;

  public static void waitX(long millis) throws InterruptedException {
    System.out.println("Before wait -> " + millis);
    COUNT++;
  }
}

如果我将WaitAdvice放入单独的JAR并将其放在JVM的引导类路径中,则此方法有效。这里的缺点是我不知道如何从覆盖的方法中调用真正的目标方法,即wait(long)实际上不会被执行,只会被替换,这可能不是您想要的。我可能需要仔细研究大量的BB源代码,单元和集成测试(因为本教程不是很有帮助,并且也已经过时),GitHub问题和StackOverflow问题,以便为此找到解决方案,但是到目前为止没有发现任何有用的信息,没有立即引起后续问题。即使我可以回答您之前的(简单的)问题,我仍然对BB菜鸟还是很感兴趣。

也许BB维护人员Rafael Winterhalter对您有一个想法。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。