Closed kylixs closed 1 month ago
Can you try to disable validation? Create your validators as:
new AgentBuilder.Default(new ByteBuddy().with(Validation.DISABLED)))
Other than that, ideally you use Advice
only and not delegation what avoids helper types which lead to these problems.
Can you try to disable validation? Create your validators as:
new AgentBuilder.Default(new ByteBuddy().with(Validation.DISABLED)))
Transform Error: interceptorClassName: methodInterceptorClass$sayHello$1, typeName: com.demo.BizFoo, classLoader: jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc, module: unnamed module @1937eaff, loaded: false
net.bytebuddy.pool.TypePool$Resolution$NoSuchTypeException: Cannot resolve type description for com.demo.BizFoo$auxiliary$uyBgM3WG
at net.bytebuddy.pool.TypePool$Resolution$Illegal.resolve(TypePool.java:191)
at net.bytebuddy.pool.TypePool$Default$LazyTypeDescription$TokenizedGenericType.toErasure(TypePool.java:6901)
at net.bytebuddy.pool.TypePool$Default$LazyTypeDescription$GenericTypeToken$Resolution$Raw$RawAnnotatedType.of(TypePool.java:3781)
at net.bytebuddy.pool.TypePool$Default$LazyTypeDescription$GenericTypeToken$Resolution$Raw$RawAnnotatedType$LazyRawAnnotatedTypeList.get(TypePool.java:3882)
at net.bytebuddy.pool.TypePool$Default$LazyTypeDescription$GenericTypeToken$Resolution$Raw$RawAnnotatedType$LazyRawAnnotatedTypeList.get(TypePool.java:3827)
at net.bytebuddy.pool.TypePool$Default$LazyTypeDescription$LazyMethodDescription$LazyParameterDescription.getType(TypePool.java:7663)
at net.bytebuddy.description.method.ParameterDescription$AbstractBase.asToken(ParameterDescription.java:186)
at net.bytebuddy.description.method.ParameterDescription$AbstractBase.asToken(ParameterDescription.java:135)
at net.bytebuddy.description.method.ParameterList$AbstractBase.asTokenList(ParameterList.java:96)
at net.bytebuddy.description.method.MethodDescription$AbstractBase.asToken(MethodDescription.java:892)
at net.bytebuddy.description.method.MethodDescription$AbstractBase.asToken(MethodDescription.java:447)
at net.bytebuddy.description.method.MethodList$AbstractBase.asSignatureTokenList(MethodList.java:112)
at net.bytebuddy.dynamic.scaffold.inline.RebaseDynamicTypeBuilder.toTypeWriter(RebaseDynamicTypeBuilder.java:226)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$UsingTypeWriter.make(DynamicType.java:4057)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:12225)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:12160)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access$1800(AgentBuilder.java:11869)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$Java9CapableVmDispatcher.run(AgentBuilder.java:12647)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$Java9CapableVmDispatcher.run(AgentBuilder.java:12579)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doPrivileged(AgentBuilder.java)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:12103)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$ByteBuddy$ModuleSupport.transform(Unknown Source)
at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188)
at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:563)
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:802)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:700)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:623)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
at com.demo.case1.AbstractInterceptTest.callBizFoo(AbstractInterceptTest.java:26)
Other than that, ideally you use
Advice
only and not delegation what avoids helper types which lead to these problems.
I want to bridge to origin ConstructorInter
via Advice
, how to pass context into ConstructorAdvice
?
builder
.constructor(ElementMatchers.any())
.intercept(Advice.to(ConstructorAdvice.class))
builder
.constructor(ElementMatchers.any())
.intercept(SuperMethodCall.INSTANCE.andThen(
MethodDelegation.withDefaultConfiguration().to(
new ConstructorInter(interceptorClassName, classLoader), fieldName)
));
And intercept constructor by Advice
also create a private constructor with helper class, same problem as MethodDelegation
:
public class BizFoo {
private String name;
public BizFoo() {
BizFoo bizFoo = this;
bizFoo((auxiliary.WTloNYVS)null);
System.out.println(String.format("ConstructorAdvice: method: %s, args: %s", "public com.demo.BizFoo()", Arrays.asList(new Object[0])));
}
private /* synthetic */ BizFoo(auxiliary.WTloNYVS wTloNYVS) {
/* 8*/ this("Tom");
}
public BizFoo(String string) {
BizFoo bizFoo = this;
String string2 = string;
bizFoo(string2, null);
System.out.println(String.format("ConstructorAdvice: method: %s, args: %s", "public com.demo.BizFoo(java.lang.String)",
Arrays.asList(string)));
}
new AgentBuilder.Default()
.with(AgentBuilder.DescriptionStrategy.Default.POOL_FIRST)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
//.with(ClassFileLocator.ForInstrumentation.fromInstalledAgent(InterceptTest1.class.getClassLoader()))
.type(ElementMatchers.named(className))
.transform((builder, typeDescription, classLoader, module, protectionDomain) -> {
return builder
.constructor(ElementMatchers.any())
.intercept(Advice.to(ConstructorAdvice.class));
}
)
The second agent with Advice
should declare disableClassFormatChanges()
, I think that should fix it.
The second agent with
Advice
should declaredisableClassFormatChanges()
, I think that should fix it.
Something not expected:
Intercept constructor with Advice
in first agent, and then intercept method with Advice
and disableClassFormatChanges()
in second agent will fail.
new AgentBuilder.Default()
.with(AgentBuilder.DescriptionStrategy.Default.POOL_FIRST)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.type(ElementMatchers.named(className))
.transform((builder, typeDescription, classLoader, module, protectionDomain) -> {
return builder
.constructor(ElementMatchers.any())
.intercept(Advice.to(ConstructorAdvice.class));
}
)
.installOn(ByteBuddyAgent.install());
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(AgentBuilder.DescriptionStrategy.Default.POOL_FIRST)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.type(ElementMatchers.named(className))
.transform((builder, typeDescription, classLoader, module, protectionDomain) -> {
return builder
.method(ElementMatchers.nameContainsIgnoreCase(methodName))
.intercept(Advice.to(InstMethodAdvice.class));
}
)
.installOn(ByteBuddyAgent.install());
Transform Error: interceptorClassName: methodInterceptorClass$sayHello$2, typeName: com.demo.BizFoo, classLoader: jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc, module: unnamed module @2c88a3e8, loaded: false
java.lang.IllegalStateException: Cannot call super (or default) method for public java.lang.String com.demo.BizFoo.sayHello(java.lang.String)
at net.bytebuddy.implementation.SuperMethodCall$Appender.apply(SuperMethodCall.java:133)
at net.bytebuddy.asm.Advice$Appender$EmulatingMethodVisitor.resolve(Advice.java:12120)
at net.bytebuddy.asm.Advice$Appender.apply(Advice.java:12073)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:730)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:715)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor$CodePreservingMethodVisitor.visitCode(TypeWriter.java:5502)
at net.bytebuddy.jar.asm.ClassReader.readMethod(ClassReader.java:1513)
at net.bytebuddy.jar.asm.ClassReader.accept(ClassReader.java:744)
at net.bytebuddy.jar.asm.ClassReader.accept(ClassReader.java:424)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:4014)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:2224)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$UsingTypeWriter.make(DynamicType.java:4057)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:4007)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:12225)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:12160)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access$1800(AgentBuilder.java:11869)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$Java9CapableVmDispatcher.run(AgentBuilder.java:12647)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$Java9CapableVmDispatcher.run(AgentBuilder.java:12579)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doPrivileged(AgentBuilder.java)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:12103)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$ByteBuddy$ModuleSupport.transform(Unknown Source)
at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188)
at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:563)
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:802)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:700)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:623)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
at com.demo.case1.AbstractInterceptTest.callBizFoo(AbstractInterceptTest.java:24)
at com.demo.case1.InterceptTest4.test4(InterceptTest4.java:20)
The top of the exception stack trace shows that the error comes from MethodRegistry#prepare
. This commit c66e3086eb075e attempted to fix this issue. Have you tried validating it with the latest version of ByteBuddy?
The above tests already use latest version:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.14.4</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<version>1.14.4</version>
</dependency>
</dependencies>
</dependencyManagement>
@raphw I found one way! The bytecode of the helper class can be successfully obtained in an asynchronous thread, avoiding the nested call the transformer. The reason is not clear. Is it related to the JVM retransform class logic?
public class MyClassFileLocator implements ClassFileLocator {
private final ForInstrumentation.ClassLoadingDelegate classLoadingDelegate;
private Instrumentation instrumentation;
private ClassLoader classLoader;
private ExecutorService executorService = Executors.newFixedThreadPool(1);
public MyClassFileLocator(Instrumentation instrumentation, ClassLoader classLoader) {
this.instrumentation = instrumentation;
this.classLoader = classLoader;
classLoadingDelegate = ForInstrumentation.ClassLoadingDelegate.ForDelegatingClassLoader.of(classLoader);
}
@Override
public Resolution locate(String name) throws IOException {
Future<Resolution> future = executorService.submit(() -> getResolution(name));
try {
return future.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private Resolution getResolution(String name) {
ExtractionClassFileTransformer classFileTransformer = new ExtractionClassFileTransformer(classLoader, name);
try {
instrumentation.addTransformer(classFileTransformer, true);
try {
instrumentation.retransformClasses(new Class[]{classLoadingDelegate.locate(name)});
} catch (Exception e) {
throw new RuntimeException(e);
}
} finally {
instrumentation.removeTransformer(classFileTransformer);
}
return classFileTransformer.getBinaryRepresentation() != null ?
new Resolution.Explicit(classFileTransformer.getBinaryRepresentation()) :
new Resolution.Illegal(name);
}
@Override
public void close() throws IOException {
}
}
AgentBuilder agentBuilder = new AgentBuilder.Default();
agentBuilder.with(AgentBuilder.DescriptionStrategy.Default.POOL_FIRST)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.type(ElementMatchers.named(className))
.transform((builder, typeDescription, classLoader, module, protectionDomain) -> {
return builder
.constructor(ElementMatchers.any())
.intercept(SuperMethodCall.INSTANCE.andThen(
MethodDelegation.withDefaultConfiguration().to(
new ConstructorInter(interceptorClassName, classLoader), fieldName)
));
}
)
.installOn(ByteBuddyAgent.install());
AgentBuilder agentBuilder = new AgentBuilder.Default();
agentBuilder.with(AgentBuilder.DescriptionStrategy.Default.POOL_FIRST)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(new MyClassFileLocator(ByteBuddyAgent.install(), AbstractInterceptTest.class.getClassLoader()))
.type(ElementMatchers.named(className))
.transform((builder, typeDescription, classLoader, module, protectionDomain) -> {
return builder
.method(ElementMatchers.nameContainsIgnoreCase(methodName))
.intercept(MethodDelegation.withDefaultConfiguration()
.to(new InstMethodsInter(interceptorClassName, classLoader), fieldName))
;
}
)
.installOn(ByteBuddyAgent.install());
ConstructorInterceptorClass: constructorInterceptorClass$1, target: com.demo.BizFoo@196ae579, args: [Tom]
ConstructorInterceptorClass: constructorInterceptorClass$1, target: com.demo.BizFoo@196ae579, args: []
InstMethodInterceptorClass: methodInterceptorClass$sayHello$1, target: com.demo.BizFoo@196ae579, args: [100], SuperCall: com.demo.BizFoo$auxiliary$Sv6rR26r@41853299, method: public int com.demo.BizFoo.sayHello(int), originResult: 100, finalResult: 101
101
ConstructorInterceptorClass: constructorInterceptorClass$1, target: com.demo.BizFoo@60d40ff4, args: [Smith]
InstMethodInterceptorClass: methodInterceptorClass$sayHello$1, target: com.demo.BizFoo@60d40ff4, args: [Joe], SuperCall: com.demo.BizFoo$auxiliary$xoRUjdbx@27755487, method: public java.lang.String com.demo.BizFoo.sayHello(java.lang.String), originResult: Hello to Joe from Smith, finalResult: Hello to John from Smith
Hello to John from Smith
Found interceptor: constructorInterceptorClass$1
Found interceptor: methodInterceptorClass$sayHello$1
Glad you solved it. Can we close this question then?
Getting bytecode in asynchronous threads doesn't seem to be perfect. Is there a better way?
What you mean by asynchronous threads and getting bytecode?
public Resolution locate(String name) throws IOException { Future<Resolution> future = executorService.submit(() -> getResolution(name)); try { return future.get(); } catch (Exception e) { throw new RuntimeException(e); } }
I worry that locate bytecode of class in single thread from multiple threads may blocking or deadlocking.
Well, this is unfortunately up to the class loader, but as it's a single monitor lookup, it should not cause a dead lock.
We were going to support multiple JavaAgents with ByteBuddy, but we ran into some trouble. First, installing a constructor interceptor on one AgentBuilder and then installing a method interceptor on another AgentBuilder was failed.
We need help.
https://github.com/apache/skywalking-java/pull/521#issuecomment-1533332570
Reproduce:
git clone git@github.com:kylixs/bytebuddy-intercept-demo.git
mvn clean test -Dtest=InterceptTest4
Test Code:
Error: