如何解决使用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 举报,一经查实,本站将立刻删除。