Optimize bytebuddy type description performance (#637)
### Improve the performance of type description of byte-buddy The goal is to get the original class description at re-transform, so as to generate consistent results when the Skywalking agent is enhanced again (including implementing the EnhancedInstance interface, auxiliary fields and methods, etc.) The previous type description used the `AgentBuilder.DescriptionStrategy.Default.POOL_FIRST` policy to get origin type description, which slows down the application startup, due to heavy I/O operations and parsing bytecode. New way is to remove dynamic fields, methods and interfaces generated by SkyWalking Agent from `TypeDescription`, and **make it as origin type descripton**. **Key feature** : * No need to cache `TypeDescription` objects, less memory used. * It only applies to the re-transform class processing flow and does not affect the startup process. **Process flow:** 1. Find `TypeDescription` from commonly used type cache, such as primitive class. 2. Delegate to `AgentBuilder.DescriptionStrategy.Default.HYBRID` 3. Wrap `TypeDescription` by `SWTypeDescriptionWrapper` , remove fields, methods, interface generated by SkyWalking. **Relative Issue:** https://github.com/apache/skywalking/issues/11460
This commit is contained in:
parent
2721438820
commit
db54c65d09
|
|
@ -19,7 +19,7 @@ Release Notes.
|
|||
* Upgrade netty-codec-http2 to 4.1.100.Final
|
||||
* Add a netty-http 4.1.x plugin to trace HTTP requests.
|
||||
* Fix Impala Jdbc URL (including schema without properties) parsing exception.
|
||||
|
||||
* Optimize byte-buddy type description performance.
|
||||
|
||||
#### Documentation
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,6 @@ public class Constants {
|
|||
/**
|
||||
* The name trait for auxiliary type names, field names and method names which generated by ByteBuddy.
|
||||
*/
|
||||
public static final String NAME_TRAIT = "sw$";
|
||||
public static final String NAME_TRAIT = "$sw$";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,11 @@ import java.security.ProtectionDomain;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
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.NamedElement;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
|
|
@ -156,9 +158,8 @@ public class SkyWalkingAgent {
|
|||
.with(new SWAuxiliaryTypeNamingStrategy(NAME_TRAIT))
|
||||
.with(new SWImplementationContextFactory(NAME_TRAIT));
|
||||
|
||||
SWNativeMethodStrategy nativeMethodStrategy = new SWNativeMethodStrategy(NAME_TRAIT);
|
||||
return new SWAgentBuilderDefault(byteBuddy, nativeMethodStrategy)
|
||||
.with(AgentBuilder.DescriptionStrategy.Default.POOL_FIRST);
|
||||
return new SWAgentBuilderDefault(byteBuddy, new SWNativeMethodStrategy(NAME_TRAIT))
|
||||
.with(new SWDescriptionStrategy(NAME_TRAIT));
|
||||
}
|
||||
|
||||
private static class Transformer implements AgentBuilder.Transformer {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
* 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 net.bytebuddy.agent.builder;
|
||||
|
||||
import net.bytebuddy.description.annotation.AnnotationList;
|
||||
import net.bytebuddy.description.field.FieldDescription;
|
||||
import net.bytebuddy.description.field.FieldList;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.method.MethodList;
|
||||
import net.bytebuddy.description.type.*;
|
||||
import net.bytebuddy.dynamic.TargetType;
|
||||
import net.bytebuddy.implementation.bytecode.StackSize;
|
||||
import net.bytebuddy.pool.TypePool;
|
||||
import net.bytebuddy.utility.JavaModule;
|
||||
import net.bytebuddy.utility.nullability.MaybeNull;
|
||||
import org.apache.skywalking.apm.agent.core.plugin.AbstractClassEnhancePluginDefine;
|
||||
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A DescriptionStrategy to get the original class description by removing dynamic field and method tokens
|
||||
* generated by SkyWalking.
|
||||
*/
|
||||
public class SWDescriptionStrategy implements AgentBuilder.DescriptionStrategy {
|
||||
|
||||
/**
|
||||
* A cache of type descriptions for commonly used types to avoid unnecessary allocations.
|
||||
*/
|
||||
private static final Map<Class<?>, TypeDescription> TYPE_CACHE;
|
||||
|
||||
/*
|
||||
* Initializes the type cache.
|
||||
*/
|
||||
static {
|
||||
TYPE_CACHE = new HashMap<Class<?>, TypeDescription>();
|
||||
TYPE_CACHE.put(TargetType.class, new TypeDescription.ForLoadedType(TargetType.class));
|
||||
TYPE_CACHE.put(Class.class, new TypeDescription.ForLoadedType(Class.class));
|
||||
TYPE_CACHE.put(Throwable.class, new TypeDescription.ForLoadedType(Throwable.class));
|
||||
TYPE_CACHE.put(Annotation.class, new TypeDescription.ForLoadedType(Annotation.class));
|
||||
TYPE_CACHE.put(Object.class, new TypeDescription.ForLoadedType(Object.class));
|
||||
TYPE_CACHE.put(String.class, new TypeDescription.ForLoadedType(String.class));
|
||||
TYPE_CACHE.put(Boolean.class, new TypeDescription.ForLoadedType(Boolean.class));
|
||||
TYPE_CACHE.put(Byte.class, new TypeDescription.ForLoadedType(Byte.class));
|
||||
TYPE_CACHE.put(Short.class, new TypeDescription.ForLoadedType(Short.class));
|
||||
TYPE_CACHE.put(Character.class, new TypeDescription.ForLoadedType(Character.class));
|
||||
TYPE_CACHE.put(Integer.class, new TypeDescription.ForLoadedType(Integer.class));
|
||||
TYPE_CACHE.put(Long.class, new TypeDescription.ForLoadedType(Long.class));
|
||||
TYPE_CACHE.put(Float.class, new TypeDescription.ForLoadedType(Float.class));
|
||||
TYPE_CACHE.put(Double.class, new TypeDescription.ForLoadedType(Double.class));
|
||||
TYPE_CACHE.put(void.class, new TypeDescription.ForLoadedType(void.class));
|
||||
TYPE_CACHE.put(boolean.class, new TypeDescription.ForLoadedType(boolean.class));
|
||||
TYPE_CACHE.put(byte.class, new TypeDescription.ForLoadedType(byte.class));
|
||||
TYPE_CACHE.put(short.class, new TypeDescription.ForLoadedType(short.class));
|
||||
TYPE_CACHE.put(char.class, new TypeDescription.ForLoadedType(char.class));
|
||||
TYPE_CACHE.put(int.class, new TypeDescription.ForLoadedType(int.class));
|
||||
TYPE_CACHE.put(long.class, new TypeDescription.ForLoadedType(long.class));
|
||||
TYPE_CACHE.put(float.class, new TypeDescription.ForLoadedType(float.class));
|
||||
TYPE_CACHE.put(double.class, new TypeDescription.ForLoadedType(double.class));
|
||||
}
|
||||
|
||||
private AgentBuilder.DescriptionStrategy delegate = Default.HYBRID;
|
||||
|
||||
private String nameTrait;
|
||||
|
||||
public SWDescriptionStrategy(String nameTrait) {
|
||||
this.nameTrait = nameTrait;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoadedFirst() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeDescription apply(String name,
|
||||
@MaybeNull Class<?> type,
|
||||
TypePool typePool,
|
||||
AgentBuilder.CircularityLock circularityLock,
|
||||
@MaybeNull ClassLoader classLoader,
|
||||
@MaybeNull JavaModule module) {
|
||||
// find from type cache
|
||||
if (type != null) {
|
||||
TypeDescription typeDescription = TYPE_CACHE.get(type);
|
||||
if (typeDescription != null) {
|
||||
return typeDescription;
|
||||
}
|
||||
}
|
||||
// wrap result
|
||||
return new SWTypeDescriptionWrapper(delegate.apply(name, type, typePool, circularityLock, classLoader, module), nameTrait);
|
||||
}
|
||||
|
||||
/**
|
||||
* A TypeDescription wrapper to remove fields, methods, interface generated by SkyWalking.
|
||||
*/
|
||||
static class SWTypeDescriptionWrapper extends TypeDescription.AbstractBase implements Serializable {
|
||||
|
||||
/**
|
||||
* The class's serial version UID.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final List<String> IGNORED_INTERFACES = Arrays.asList(EnhancedInstance.class.getName());
|
||||
|
||||
private static final List<String> IGNORED_FIELDS = Arrays.asList(AbstractClassEnhancePluginDefine.CONTEXT_ATTR_NAME);
|
||||
|
||||
private static final List<String> IGNORED_METHODS = Arrays.asList("getSkyWalkingDynamicField", "setSkyWalkingDynamicField");
|
||||
|
||||
private MethodList<MethodDescription.InDefinedShape> methods;
|
||||
|
||||
private FieldList<FieldDescription.InDefinedShape> fields;
|
||||
|
||||
private final String nameTrait;
|
||||
|
||||
private TypeList.Generic interfaces;
|
||||
|
||||
private TypeDescription delegate;
|
||||
|
||||
public SWTypeDescriptionWrapper(TypeDescription delegate, String nameTrait) {
|
||||
this.delegate = delegate;
|
||||
this.nameTrait = nameTrait;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeList.Generic getInterfaces() {
|
||||
if (this.interfaces == null) {
|
||||
TypeList.Generic allInterfaces = delegate.getInterfaces();
|
||||
if (allInterfaces.stream().anyMatch(s -> IGNORED_INTERFACES.contains(s.getTypeName()))) {
|
||||
// remove interfaces added by SkyWalking
|
||||
List<Generic> list = allInterfaces.stream()
|
||||
.filter(s -> !IGNORED_INTERFACES.contains(s.getTypeName()))
|
||||
.collect(Collectors.toList());
|
||||
this.interfaces = new TypeList.Generic.Explicit(list);
|
||||
} else {
|
||||
this.interfaces = allInterfaces;
|
||||
}
|
||||
}
|
||||
return this.interfaces;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldList<FieldDescription.InDefinedShape> getDeclaredFields() {
|
||||
if (this.fields == null) {
|
||||
FieldList<FieldDescription.InDefinedShape> declaredFields = delegate.getDeclaredFields();
|
||||
if (declaredFields.stream()
|
||||
.anyMatch(f -> f.getName().contains(nameTrait) || IGNORED_FIELDS.contains(f.getName()))) {
|
||||
// Remove dynamic field tokens generated by SkyWalking
|
||||
fields = new FieldList.Explicit<>(declaredFields.stream()
|
||||
.filter(f -> !f.getName().contains(nameTrait) && !IGNORED_FIELDS.contains(f.getName()))
|
||||
.collect(Collectors.toList()));
|
||||
} else {
|
||||
fields = declaredFields;
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodList<MethodDescription.InDefinedShape> getDeclaredMethods() {
|
||||
if (this.methods == null) {
|
||||
MethodList<MethodDescription.InDefinedShape> declaredMethods = delegate.getDeclaredMethods();
|
||||
if (declaredMethods.stream()
|
||||
.anyMatch(m -> m.getName().contains(nameTrait) || IGNORED_METHODS.contains(m.getName()))) {
|
||||
// Remove dynamic method tokens generated by SkyWalking
|
||||
methods = new MethodList.Explicit<>(declaredMethods.stream()
|
||||
.filter(m -> !m.getName().contains(nameTrait) && !IGNORED_METHODS.contains(m.getName()))
|
||||
.collect(Collectors.toList()));
|
||||
} else {
|
||||
methods = declaredMethods;
|
||||
}
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssignableTo(Class<?> type) {
|
||||
// ignore interface added by SkyWalking
|
||||
if (IGNORED_INTERFACES.contains(type.getName())) {
|
||||
return false;
|
||||
}
|
||||
return delegate.isAssignableTo(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccessibleTo(TypeDescription typeDescription) {
|
||||
// ignore interface added by SkyWalking
|
||||
if (IGNORED_INTERFACES.contains(typeDescription.getName())) {
|
||||
return false;
|
||||
}
|
||||
return delegate.isAccessibleTo(typeDescription);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecordComponentList<RecordComponentDescription.InDefinedShape> getRecordComponents() {
|
||||
return delegate.getRecordComponents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeDescription getComponentType() {
|
||||
return delegate.getComponentType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeDescription getDeclaringType() {
|
||||
return delegate.getDeclaringType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeList getDeclaredTypes() {
|
||||
return delegate.getDeclaredTypes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodDescription.InDefinedShape getEnclosingMethod() {
|
||||
return delegate.getEnclosingMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeDescription getEnclosingType() {
|
||||
return delegate.getEnclosingType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSimpleName() {
|
||||
return delegate.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCanonicalName() {
|
||||
return delegate.getCanonicalName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnonymousType() {
|
||||
return delegate.isAnonymousType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocalType() {
|
||||
return delegate.isLocalType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageDescription getPackage() {
|
||||
return delegate.getPackage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeDescription getNestHost() {
|
||||
return delegate.getNestHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeList getNestMembers() {
|
||||
return delegate.getNestMembers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeList getPermittedSubtypes() {
|
||||
return delegate.getPermittedSubtypes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescriptor() {
|
||||
return delegate.getDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return delegate.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeList.Generic getTypeVariables() {
|
||||
return delegate.getTypeVariables();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationList getDeclaredAnnotations() {
|
||||
return delegate.getDeclaredAnnotations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Generic getSuperClass() {
|
||||
return delegate.getSuperClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StackSize getStackSize() {
|
||||
return delegate.getStackSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isArray() {
|
||||
return delegate.isArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRecord() {
|
||||
return delegate.isRecord();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrimitive() {
|
||||
return delegate.isPrimitive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModifiers() {
|
||||
return delegate.getModifiers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@ import java.util.concurrent.TimeUnit;
|
|||
*/
|
||||
public class SWClassFileLocator implements ClassFileLocator {
|
||||
private static final ILog LOGGER = LogManager.getLogger(SWClassFileLocator.class);
|
||||
private static final String[] TYPE_NAME_TRAITS = {"auxiliary$", "ByteBuddy$", "sw$"};
|
||||
private static final String[] TYPE_NAME_TRAITS = {"auxiliary$", "ByteBuddy$", "$sw$"};
|
||||
private static final int DEFAULT_TIMEOUT_SECONDS = 2;
|
||||
|
||||
private final ForInstrumentation.ClassLoadingDelegate classLoadingDelegate;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
package org.apache.skywalking.apm.agent.bytebuddy.biz;
|
||||
|
||||
public class BizFoo {
|
||||
public class BizFoo implements BizInterface {
|
||||
|
||||
private String name;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 interface BizInterface {
|
||||
|
||||
}
|
||||
|
|
@ -22,7 +22,9 @@ import net.bytebuddy.ByteBuddy;
|
|||
import net.bytebuddy.agent.ByteBuddyAgent;
|
||||
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.implementation.FieldAccessor;
|
||||
import net.bytebuddy.implementation.MethodDelegation;
|
||||
import net.bytebuddy.implementation.SWImplementationContextFactory;
|
||||
import net.bytebuddy.implementation.SuperMethodCall;
|
||||
|
|
@ -36,6 +38,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.core.plugin.interceptor.enhance.EnhancedInstance;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
|
|
@ -52,6 +55,10 @@ import java.security.ProtectionDomain;
|
|||
import java.util.Arrays;
|
||||
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;
|
||||
|
||||
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";
|
||||
|
|
@ -60,7 +67,7 @@ public class AbstractInterceptTest {
|
|||
public static final int BASE_INT_VALUE = 100;
|
||||
public static final String CONSTRUCTOR_INTERCEPTOR_CLASS = "constructorInterceptorClass";
|
||||
public static final String METHOD_INTERCEPTOR_CLASS = "methodInterceptorClass";
|
||||
protected List<String> nameTraits = Arrays.asList("sw2023", "sw2024");
|
||||
protected List<String> nameTraits = Arrays.asList("$sw$", "$sw$2");
|
||||
protected boolean deleteDuplicatedFields = false;
|
||||
|
||||
@BeforeClass
|
||||
|
|
@ -106,6 +113,11 @@ public class AbstractInterceptTest {
|
|||
Log.info("Found interceptor: " + interceptorName);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
protected static void checkErrors() {
|
||||
Assert.assertEquals("Error occurred in transform", 0, EnhanceHelper.getErrors().size());
|
||||
}
|
||||
|
|
@ -142,7 +154,7 @@ public class AbstractInterceptTest {
|
|||
protected void installConstructorInterceptorWithMethodDelegation(String className, int round) {
|
||||
String interceptorClassName = CONSTRUCTOR_INTERCEPTOR_CLASS + "$" + className + "$" + round;
|
||||
String nameTrait = getNameTrait(round);
|
||||
String fieldName = nameTrait + "_delegate$constructor" + round;
|
||||
String fieldName = nameTrait + "delegate$" + "constructor" + round;
|
||||
|
||||
AgentBuilder agentBuilder = newAgentBuilder(nameTrait);
|
||||
agentBuilder.type(ElementMatchers.named(className))
|
||||
|
|
@ -162,6 +174,36 @@ public class AbstractInterceptTest {
|
|||
.installOn(ByteBuddyAgent.install());
|
||||
}
|
||||
|
||||
protected void installInterface(String className) {
|
||||
String nameTrait = getNameTrait(1);
|
||||
newAgentBuilder(nameTrait).type(ElementMatchers.named(className))
|
||||
.transform((builder, typeDescription, classLoader, module, protectionDomain) -> {
|
||||
if (deleteDuplicatedFields) {
|
||||
builder = builder.visit(new SWAsmVisitorWrapper());
|
||||
}
|
||||
/**
|
||||
* Manipulate class source code.<br/>
|
||||
*
|
||||
* new class need:<br/>
|
||||
* 1.Add field, name {@link #CONTEXT_ATTR_NAME}.
|
||||
* 2.Add a field accessor for this field.
|
||||
*
|
||||
* And make sure the source codes manipulation only occurs once.
|
||||
*
|
||||
*/
|
||||
if (!typeDescription.isAssignableTo(EnhancedInstance.class)) {
|
||||
builder = builder.defineField(
|
||||
CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE)
|
||||
.implement(EnhancedInstance.class)
|
||||
.intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME));
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
)
|
||||
.with(getListener("EnhancedInstance"))
|
||||
.installOn(ByteBuddyAgent.install());
|
||||
}
|
||||
|
||||
protected String getNameTrait(int round) {
|
||||
return nameTraits.get(round - 1);
|
||||
}
|
||||
|
|
@ -173,7 +215,7 @@ public class AbstractInterceptTest {
|
|||
|
||||
return new SWAgentBuilderDefault(byteBuddy, new SWNativeMethodStrategy(nameTrait))
|
||||
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
|
||||
.with(AgentBuilder.DescriptionStrategy.Default.POOL_FIRST)
|
||||
.with(new SWDescriptionStrategy(nameTrait))
|
||||
.with(new SWClassFileLocator(ByteBuddyAgent.install(), getClassLoader()));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,19 @@ public class ReTransform1Test extends AbstractReTransformTest {
|
|||
checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1);
|
||||
checkErrors();
|
||||
|
||||
// installTraceClassTransformer("Trace class: ");
|
||||
|
||||
// do retransform
|
||||
reTransform(instrumentation, BizFoo.class);
|
||||
|
||||
// test again
|
||||
callBizFoo(1);
|
||||
|
||||
// check interceptors
|
||||
checkMethodInterceptor(SAY_HELLO_METHOD, 1);
|
||||
checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1);
|
||||
|
||||
// test ProjectService
|
||||
ProjectService projectService = new ProjectService();
|
||||
ProjectDO originProjectDO = ProjectDO.builder()
|
||||
.name("test")
|
||||
|
|
@ -59,19 +72,8 @@ public class ReTransform1Test extends AbstractReTransformTest {
|
|||
ProjectDO projectDO = projectService.getById(1);
|
||||
projectService.list();
|
||||
|
||||
// installTraceClassTransformer("Trace class: ");
|
||||
|
||||
// do retransform
|
||||
reTransform(instrumentation, BizFoo.class);
|
||||
reTransform(instrumentation, ProjectService.class);
|
||||
|
||||
// test again
|
||||
callBizFoo(1);
|
||||
|
||||
// check interceptors
|
||||
checkMethodInterceptor(SAY_HELLO_METHOD, 1);
|
||||
checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1);
|
||||
|
||||
projectDO = projectService.getById(1);
|
||||
Assert.assertEquals(originProjectDO, projectDO);
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,12 @@ public class ReTransform2Test extends AbstractReTransformTest {
|
|||
// do retransform
|
||||
reTransform(instrumentation, BizFoo.class);
|
||||
|
||||
// int retransformCount = 30;
|
||||
// for (int i = 0; i < retransformCount; i++) {
|
||||
// reTransform(instrumentation, BizFoo.class);
|
||||
// // reTransform(instrumentation, ProjectService.class);
|
||||
// }
|
||||
|
||||
// test again
|
||||
callBizFoo(2);
|
||||
// check interceptors
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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.BizFoo;
|
||||
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.instrument.Instrumentation;
|
||||
|
||||
public class ReTransform3Test extends AbstractReTransformTest {
|
||||
|
||||
@Test
|
||||
public void testInterceptConstructor() throws Exception {
|
||||
Instrumentation instrumentation = ByteBuddyAgent.install();
|
||||
|
||||
// install transformer
|
||||
installMethodInterceptor(BIZ_FOO_CLASS_NAME, SAY_HELLO_METHOD, 1);
|
||||
installConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1);
|
||||
// implement EnhancedInstance
|
||||
installInterface(BIZ_FOO_CLASS_NAME);
|
||||
|
||||
// call target class
|
||||
callBizFoo(1);
|
||||
|
||||
// check interceptors
|
||||
checkInterface(BizFoo.class, EnhancedInstance.class);
|
||||
checkMethodInterceptor(SAY_HELLO_METHOD, 1);
|
||||
checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1);
|
||||
checkErrors();
|
||||
|
||||
// installTraceClassTransformer("Trace class: ");
|
||||
|
||||
// do retransform
|
||||
reTransform(instrumentation, BizFoo.class);
|
||||
|
||||
// test again
|
||||
callBizFoo(1);
|
||||
|
||||
// check interceptors
|
||||
checkInterface(BizFoo.class, EnhancedInstance.class);
|
||||
checkMethodInterceptor(SAY_HELLO_METHOD, 1);
|
||||
checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1);
|
||||
checkErrors();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue