Skip to content

Commit 267aba8

Browse files
authored
Improve bytebuddy class enhance for retransform classes (#561)
* SWAuxiliaryTypeNamingStrategy Auxiliary type name pattern: <origin_class_name>$<name_trait>$auxiliary$<auxiliary_type_instance_hash> * DelegateNamingResolver Interceptor delegate field name pattern: <name_trait>$delegate$<class_name_hash>$<plugin_define_hash>$<intercept_point_hash> * SWMethodNameTransformer Renamed origin method pattern: <name_trait>$original$<method_name>$<method_description_hash> * SWImplementationContextFactory Method cache value field pattern: cachedValue$<name_trait>$<origin_class_name_hash>$<field_value_hash> Accessor method name pattern: <renamed_origin_method>$accessor$<name_trait>$<origin_class_name_hash> Here is an example of manipulated enhanced class with new naming policies of auxiliary classes, fields, and methods ```java import sample.mybatis.controller.HotelController$sw$auxiliary$19cja42; import sample.mybatis.controller.HotelController$sw$auxiliary$p257su0; import sample.mybatis.domain.Hotel; import sample.mybatis.service.HotelService; @RequestMapping(value={"/hotel"}) @RestController public class HotelController implements EnhancedInstance { @Autowired @lazy private HotelService hotelService; private volatile Object _$EnhancedClassField_ws; // Interceptor delegate fields public static volatile /* synthetic */ InstMethodsInter sw$delegate$td03673$ain2do0$8im5jm1; public static volatile /* synthetic */ InstMethodsInter sw$delegate$td03673$ain2do0$edkmf61; public static volatile /* synthetic */ ConstructorInter sw$delegate$td03673$ain2do0$qs9unv1; public static volatile /* synthetic */ InstMethodsInter sw$delegate$td03673$fl4lnk1$m3ia3a2; public static volatile /* synthetic */ InstMethodsInter sw$delegate$td03673$fl4lnk1$sufrvp1; public static volatile /* synthetic */ ConstructorInter sw$delegate$td03673$fl4lnk1$cteu7s1; // Origin method cache value field private static final /* synthetic */ Method cachedValue$sw$td03673$g5sobj1; public HotelController() { this(null); sw$delegate$td03673$ain2do0$qs9unv1.intercept(this, new Object[0]); } private /* synthetic */ HotelController(sw.auxiliary.p257su0 p257su02) { } @GetMapping(value={"city/{cityId}"}) public Hotel selectByCityId(@PathVariable(value="cityId") int n) { // call interceptor with auxiliary type and parameters and origin method object return (Hotel)sw$delegate$td03673$ain2do0$8im5jm1.intercept(this, new Object[]{n}, new HotelController$sw$auxiliary$19cja42(this, n), cachedValue$sw$td03673$g5sobj1); } // Renamed origin method private /* synthetic */ Hotel sw$origin$selectByCityId$a8458p3(int cityId) { /*22*/ return this.hotelService.selectByCityId(cityId); } // Accessor of renamed origin method, calling from auxiliary type final /* synthetic */ Hotel sw$origin$selectByCityId$a8458p3$accessor$sw$td03673(int n) { // Calling renamed origin method return this.sw$origin$selectByCityId$a8458p3(n); } @OverRide public Object getSkyWalkingDynamicField() { return this._$EnhancedClassField_ws; } @OverRide public void setSkyWalkingDynamicField(Object object) { this._$EnhancedClassField_ws = object; } static { ClassLoader.getSystemClassLoader().loadClass("org.apache.skywalking.apm.dependencies.net.bytebuddy.dynamic.Nexus").getMethod("initialize", Class.class, Integer.TYPE).invoke(null, HotelController.class, -1072476370); // Method object cachedValue$sw$td03673$g5sobj1 = HotelController.class.getMethod("selectByCityId", Integer.TYPE); } } ``` Auxiliary type of Constructor : ```java class HotelController$sw$auxiliary$p257su0 { } ``` Auxiliary type of `selectByCityId` method: ```java class HotelController$sw$auxiliary$19cja42 implements Runnable, Callable { private HotelController argument0; private int argument1; public Object call() throws Exception { return this.argument0.sw$origin$selectByCityId$a8458p3$accessor$sw$td03673(this.argument1); } @OverRide public void run() { this.argument0.sw$origin$selectByCityId$a8458p3$accessor$sw$td03673(this.argument1); } HotelController$sw$auxiliary$19cja42(HotelController hotelController, int n) { this.argument0 = hotelController; this.argument1 = n; } } ```
1 parent b72e984 commit 267aba8

File tree

57 files changed

+2313
-258
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2313
-258
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Release Notes.
1212
* Fix the conflict between the logging kernel and the JDK threadpool plugin.
1313
* Fix the thread safety bug of finishing operation for the span named "SpringCloudGateway/sendRequest"
1414
* Fix NPE in guava-eventbus-plugin.
15+
* Support re-transform/hot-swap classes with other java agents, and remove the obsolete cache enhanced class feature.
1516

1617
#### Documentation
1718

apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import org.apache.skywalking.apm.agent.core.logging.core.LogOutput;
2626
import org.apache.skywalking.apm.agent.core.logging.core.ResolverType;
2727
import org.apache.skywalking.apm.agent.core.logging.core.WriterFactory;
28-
import org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ClassCacheMode;
2928
import org.apache.skywalking.apm.util.Length;
3029

3130
/**
@@ -110,19 +109,6 @@ public static class Agent {
110109
*/
111110
public static boolean IS_OPEN_DEBUGGING_CLASS = false;
112111

113-
/**
114-
* If true, SkyWalking agent will cache all instrumented classes to memory or disk files (decided by class cache
115-
* mode), allow other javaagent to enhance those classes that enhanced by SkyWalking agent.
116-
*/
117-
public static boolean IS_CACHE_ENHANCED_CLASS = false;
118-
119-
/**
120-
* The instrumented classes cache mode: MEMORY or FILE MEMORY: cache class bytes to memory, if instrumented
121-
* classes is too many or too large, it may take up more memory FILE: cache class bytes in `/class-cache`
122-
* folder, automatically clean up cached class files when the application exits
123-
*/
124-
public static ClassCacheMode CLASS_CACHE_MODE = ClassCacheMode.MEMORY;
125-
126112
/**
127113
* The identifier of the instance
128114
*/

apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Constants.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,10 @@ public class Constants {
3232
public static String EVENT_LAYER_NAME = "GENERAL";
3333

3434
public static int NULL_VALUE = 0;
35+
36+
/**
37+
* The name trait for auxiliary type names, field names and method names which generated by ByteBuddy.
38+
*/
39+
public static final String NAME_TRAIT = "sw$";
40+
3541
}

apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/AnnotationTypeNameMatch.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ public boolean matches(T target) {
5555
return target.getAnnotationType().asErasure().getName().equals(annotationTypeName);
5656
}
5757

58+
/**
59+
* To ensure that the hashCode for recreating the XxxInterceptPoint instance is the same as the previous instance,
60+
* each ElementMatcher implementation class needs to implement toString() method.
61+
*/
62+
@Override
63+
public String toString() {
64+
return "AnnotationTypeNameMatch{" +
65+
"annotationTypeName='" + annotationTypeName + '\'' +
66+
'}';
67+
}
68+
5869
/**
5970
* The static method to create {@link AnnotationTypeNameMatch} This is a delegate method to follow byte-buddy {@link
6071
* ElementMatcher}'s code style.

apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/ArgumentTypeNameMatch.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,18 @@ public boolean matches(MethodDescription target) {
6868
return false;
6969
}
7070

71+
/**
72+
* To ensure that the hashCode for recreating the XxxInterceptPoint instance is the same as the previous instance,
73+
* each ElementMatcher implementation class needs to implement toString() method.
74+
*/
75+
@Override
76+
public String toString() {
77+
return "ArgumentTypeNameMatch{" +
78+
"index=" + index +
79+
", argumentTypeName='" + argumentTypeName + '\'' +
80+
'}';
81+
}
82+
7183
/**
7284
* The static method to create {@link ArgumentTypeNameMatch} This is a delegate method to follow byte-buddy {@link
7385
* ElementMatcher}'s code style.

apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/CacheableTransformerDecorator.java

Lines changed: 0 additions & 195 deletions
This file was deleted.

apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/ReturnTypeNameMatch.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@ public boolean matches(MethodDescription target) {
5353
return target.getReturnType().asErasure().getName().equals(returnTypeName);
5454
}
5555

56+
/**
57+
* To ensure that the hashCode for recreating the XxxInterceptPoint instance is the same as the previous instance,
58+
* each ElementMatcher implementation class needs to implement toString() method.
59+
*/
60+
@Override
61+
public String toString() {
62+
return "ReturnTypeNameMatch{" +
63+
"returnTypeName='" + returnTypeName + '\'' +
64+
'}';
65+
}
66+
5667
/**
5768
* The static method to create {@link ReturnTypeNameMatch} This is a delegate method to follow byte-buddy {@link
5869
* ElementMatcher}'s code style.

apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/ConstructorInterceptPoint.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import net.bytebuddy.description.method.MethodDescription;
2222
import net.bytebuddy.matcher.ElementMatcher;
2323

24+
import java.util.Objects;
25+
2426
/**
2527
* One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this
2628
* "Intercept Point", the definition targets class's constructors, and the interceptor.
@@ -41,4 +43,13 @@ public interface ConstructorInterceptPoint {
4143
* org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor}
4244
*/
4345
String getConstructorInterceptor();
46+
47+
/**
48+
* To ensure that the hashCode for recreating the XxxInterceptPoint instance is the same as the previous instance,
49+
* each ElementMatcher implementation class needs to implement toString() method.
50+
* @return hashCode of this intercept point
51+
*/
52+
default int computeHashCode() {
53+
return Objects.hash(this.getClass().getName(), this.getConstructorMatcher().toString(), this.getConstructorInterceptor());
54+
}
4455
}

apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/InstanceMethodsInterceptPoint.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import net.bytebuddy.description.method.MethodDescription;
2222
import net.bytebuddy.matcher.ElementMatcher;
2323

24+
import java.util.Objects;
25+
2426
/**
2527
* One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this
2628
* "Intercept Point", the definition targets class's instance methods, and the interceptor.
@@ -42,4 +44,13 @@ public interface InstanceMethodsInterceptPoint {
4244
String getMethodsInterceptor();
4345

4446
boolean isOverrideArgs();
47+
48+
/**
49+
* To ensure that the hashCode for recreating the XxxInterceptPoint instance is the same as the previous instance,
50+
* each ElementMatcher implementation class needs to implement toString() method.
51+
* @return hashCode of this intercept point
52+
*/
53+
default int computeHashCode() {
54+
return Objects.hash(this.getClass().getName(), this.getMethodsMatcher().toString(), this.getMethodsInterceptor(), this.isOverrideArgs());
55+
}
4556
}

apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/StaticMethodsInterceptPoint.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import net.bytebuddy.description.method.MethodDescription;
2222
import net.bytebuddy.matcher.ElementMatcher;
2323

24+
import java.util.Objects;
25+
2426
/**
2527
* One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this
2628
* "Intercept Point", the definition targets class's static methods, and the interceptor.
@@ -42,4 +44,13 @@ public interface StaticMethodsInterceptPoint {
4244
String getMethodsInterceptor();
4345

4446
boolean isOverrideArgs();
47+
48+
/**
49+
* To ensure that the hashCode for recreating the XxxInterceptPoint instance is the same as the previous instance,
50+
* each ElementMatcher implementation class needs to implement toString() method.
51+
* @return hashCode of this intercept point
52+
*/
53+
default int computeHashCode() {
54+
return Objects.hash(this.getClass().getName(), this.getMethodsMatcher().toString(), this.getMethodsInterceptor(), this.isOverrideArgs());
55+
}
4556
}

0 commit comments

Comments
 (0)