This is an automated email from the ASF dual-hosted git repository. wusheng pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/skywalking-java.git
The following commit(s) were added to refs/heads/main by this push: new 529d6d7eb3 fix parent class enhance issue (#757) 529d6d7eb3 is described below commit 529d6d7eb31db1306081d078a0689e84201cb3c4 Author: tjuwangning <78459090+tjuwangn...@users.noreply.github.com> AuthorDate: Wed May 28 11:26:05 2025 +0800 fix parent class enhance issue (#757) --- CHANGES.md | 1 + .../plugin/AbstractClassEnhancePluginDefine.java | 8 +++ .../enhance/ClassEnhancePluginDefine.java | 4 ++ .../enhance/v2/ClassEnhancePluginDefineV2.java | 4 ++ .../apm/agent/bytebuddy/biz/ChildBar.java | 27 ++++++++++ .../apm/agent/bytebuddy/biz/ParentBar.java | 26 +++++++++ .../bytebuddy/cases/AbstractInterceptTest.java | 27 +++++++++- .../agent/bytebuddy/cases/ReTransform4Test.java | 62 ++++++++++++++++++++++ 8 files changed, 157 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8affde9592..8e58c3459d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,7 @@ Release Notes. * Agent kernel services could be not-booted-yet as ServiceManager#INSTANCE#boot executed after agent transfer initialization. Delay so11y metrics#build when the services are not ready to avoid MeterService status is not initialized. +* Fix retransform failure when enhancing both parent and child classes. All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/236?closed=1) diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/AbstractClassEnhancePluginDefine.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/AbstractClassEnhancePluginDefine.java index c53e54f0fa..f0ab14a75b 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/AbstractClassEnhancePluginDefine.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/AbstractClassEnhancePluginDefine.java @@ -51,6 +51,14 @@ public abstract class AbstractClassEnhancePluginDefine { * New field name. */ public static final String CONTEXT_ATTR_NAME = "_$EnhancedClassField_ws"; + /** + * Getter method name. + */ + public static final String CONTEXT_GETTER_NAME = "getSkyWalkingDynamicField"; + /** + * Setter method name. + */ + public static final String CONTEXT_SETTER_NAME = "setSkyWalkingDynamicField"; /** * Main entrance of enhancing the class. diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/ClassEnhancePluginDefine.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/ClassEnhancePluginDefine.java index b595d89e92..ec6628fe95 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/ClassEnhancePluginDefine.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/ClassEnhancePluginDefine.java @@ -19,6 +19,7 @@ package org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance; import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.implementation.FieldAccessor; @@ -102,6 +103,9 @@ public abstract class ClassEnhancePluginDefine extends AbstractClassEnhancePlugi newClassBuilder = newClassBuilder.defineField( CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE) .implement(EnhancedInstance.class) + .defineMethod(CONTEXT_GETTER_NAME, Object.class, Visibility.PUBLIC) + .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME)) + .defineMethod(CONTEXT_SETTER_NAME, void.class, Visibility.PUBLIC).withParameters(Object.class) .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME)); context.extendObjectCompleted(); } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/ClassEnhancePluginDefineV2.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/ClassEnhancePluginDefineV2.java index 8ea6cdfb99..3423ac1392 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/ClassEnhancePluginDefineV2.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/v2/ClassEnhancePluginDefineV2.java @@ -18,6 +18,7 @@ package org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2; import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.implementation.FieldAccessor; @@ -135,6 +136,9 @@ public abstract class ClassEnhancePluginDefineV2 extends AbstractClassEnhancePlu newClassBuilder = newClassBuilder.defineField( CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE) .implement(EnhancedInstance.class) + .defineMethod(CONTEXT_GETTER_NAME, Object.class, Visibility.PUBLIC) + .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME)) + .defineMethod(CONTEXT_SETTER_NAME, void.class, Visibility.PUBLIC).withParameters(Object.class) .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME)); context.extendObjectCompleted(); } diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ChildBar.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ChildBar.java new file mode 100644 index 0000000000..e7ac8d49ff --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ChildBar.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.bytebuddy.biz; + +public class ChildBar extends ParentBar { + + public String sayHelloChild() { + return "Joe"; + } + +} diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ParentBar.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ParentBar.java new file mode 100644 index 0000000000..6113b4c41a --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ParentBar.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.bytebuddy.biz; + +public class ParentBar { + + public String sayHelloParent() { + return "Joe"; + } +} diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/AbstractInterceptTest.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/AbstractInterceptTest.java index ba9cf4d372..627fc8888d 100644 --- a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/AbstractInterceptTest.java +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/AbstractInterceptTest.java @@ -24,6 +24,7 @@ import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.agent.builder.SWAgentBuilderDefault; import net.bytebuddy.agent.builder.SWDescriptionStrategy; import net.bytebuddy.agent.builder.SWNativeMethodStrategy; +import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.implementation.FieldAccessor; import net.bytebuddy.implementation.MethodDelegation; import net.bytebuddy.implementation.SWImplementationContextFactory; @@ -38,6 +39,7 @@ import org.apache.skywalking.apm.agent.bytebuddy.SWAsmVisitorWrapper; import org.apache.skywalking.apm.agent.bytebuddy.SWAuxiliaryTypeNamingStrategy; import org.apache.skywalking.apm.agent.bytebuddy.SWClassFileLocator; import org.apache.skywalking.apm.agent.bytebuddy.biz.BizFoo; +import org.apache.skywalking.apm.agent.bytebuddy.biz.ChildBar; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.junit.Assert; import org.junit.BeforeClass; @@ -58,11 +60,15 @@ import java.util.List; import static net.bytebuddy.jar.asm.Opcodes.ACC_PRIVATE; import static net.bytebuddy.jar.asm.Opcodes.ACC_VOLATILE; import static org.apache.skywalking.apm.agent.core.plugin.AbstractClassEnhancePluginDefine.CONTEXT_ATTR_NAME; +import static org.apache.skywalking.apm.agent.core.plugin.AbstractClassEnhancePluginDefine.CONTEXT_GETTER_NAME; +import static org.apache.skywalking.apm.agent.core.plugin.AbstractClassEnhancePluginDefine.CONTEXT_SETTER_NAME; public class AbstractInterceptTest { public static final String BIZ_FOO_CLASS_NAME = "org.apache.skywalking.apm.agent.bytebuddy.biz.BizFoo"; public static final String PROJECT_SERVICE_CLASS_NAME = "org.apache.skywalking.apm.agent.bytebuddy.biz.ProjectService"; public static final String DOC_SERVICE_CLASS_NAME = "org.apache.skywalking.apm.agent.bytebuddy.biz.DocService"; + public static final String PARENT_BAR_CLASS_NAME = "org.apache.skywalking.apm.agent.bytebuddy.biz.ParentBar"; + public static final String CHILD_BAR_CLASS_NAME = "org.apache.skywalking.apm.agent.bytebuddy.biz.ChildBar"; public static final String SAY_HELLO_METHOD = "sayHello"; public static final int BASE_INT_VALUE = 100; public static final String CONSTRUCTOR_INTERCEPTOR_CLASS = "constructorInterceptorClass"; @@ -85,6 +91,20 @@ public class AbstractInterceptTest { } }; + protected static void callBar(int round) { + Log.info("-------------"); + Log.info("callChildBar: " + round); + // load target class + String strResultChild = new ChildBar().sayHelloChild(); + Log.info("result: " + strResultChild); + + String strResultParent = new ChildBar().sayHelloParent(); + Log.info("result: " + strResultParent); + + Assert.assertEquals("String value is unexpected", "John", strResultChild); + Assert.assertEquals("String value is unexpected", "John", strResultParent); + } + protected static void callBizFoo(int round) { Log.info("-------------"); Log.info("callBizFoo: " + round); @@ -115,7 +135,7 @@ public class AbstractInterceptTest { protected static void checkInterface(Class testClass, Class interfaceCls) { Assert.assertTrue("Check interface failure, the test class: " + testClass + " does not implement the expected interface: " + interfaceCls, - EnhancedInstance.class.isAssignableFrom(BizFoo.class)); + EnhancedInstance.class.isAssignableFrom(testClass)); } protected static void checkErrors() { @@ -195,6 +215,9 @@ public class AbstractInterceptTest { builder = builder.defineField( CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE) .implement(EnhancedInstance.class) + .defineMethod(CONTEXT_GETTER_NAME, Object.class, Visibility.PUBLIC) + .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME)) + .defineMethod(CONTEXT_SETTER_NAME, void.class, Visibility.PUBLIC).withParameters(Object.class) .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME)); } return builder; @@ -223,7 +246,7 @@ public class AbstractInterceptTest { ClassFileTransformer classFileTransformer = new ClassFileTransformer() { @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { - if (className.endsWith("BizFoo") || className.endsWith("ProjectService") || className.endsWith("DocService")) { + if (className.endsWith("BizFoo") || className.endsWith("ProjectService") || className.endsWith("DocService") || className.endsWith("ChildBar") || className.endsWith("ParentBar")) { Log.error(msg + className); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ClassReader cr = new ClassReader(classfileBuffer); diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/ReTransform4Test.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/ReTransform4Test.java new file mode 100644 index 0000000000..877dd416e7 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/ReTransform4Test.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.agent.bytebuddy.cases; + +import net.bytebuddy.agent.ByteBuddyAgent; +import org.apache.skywalking.apm.agent.bytebuddy.biz.ChildBar; +import org.apache.skywalking.apm.agent.bytebuddy.biz.ParentBar; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.junit.Test; + +import java.lang.instrument.Instrumentation; + +public class ReTransform4Test extends AbstractReTransformTest { + + @Test + public void testInterceptConstructor() throws Exception { + Instrumentation instrumentation = ByteBuddyAgent.install(); + + // install transformer + installMethodInterceptor(PARENT_BAR_CLASS_NAME, "sayHelloParent", 1); + installMethodInterceptor(CHILD_BAR_CLASS_NAME, "sayHelloChild", 1); + // implement EnhancedInstance + installInterface(PARENT_BAR_CLASS_NAME); + installInterface(CHILD_BAR_CLASS_NAME); + + // call target class + callBar(1); + + // check interceptors + checkInterface(ParentBar.class, EnhancedInstance.class); + checkInterface(ChildBar.class, EnhancedInstance.class); + checkErrors(); + + installTraceClassTransformer("Trace class: "); + + // do retransform + reTransform(instrumentation, ChildBar.class); + + // check interceptors + checkInterface(ParentBar.class, EnhancedInstance.class); + checkInterface(ChildBar.class, EnhancedInstance.class); + checkErrors(); + } + +} +