From d37c6c1afffd9b75af09c4a4ef982111da016abc Mon Sep 17 00:00:00 2001 From: Gong Dewei Date: Sat, 24 Jun 2023 20:50:10 +0800 Subject: [PATCH] Improve bytebuddy class enhance for retransform classes (#561) * SWAuxiliaryTypeNamingStrategy Auxiliary type name pattern: $$auxiliary$ * DelegateNamingResolver Interceptor delegate field name pattern: $delegate$$$ * SWMethodNameTransformer Renamed origin method pattern: $original$$ * SWImplementationContextFactory Method cache value field pattern: cachedValue$$$ Accessor method name pattern: $accessor$$ 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; } } ``` --- CHANGES.md | 1 + .../apm/agent/core/conf/Config.java | 14 -- .../apm/agent/core/conf/Constants.java | 6 + .../bytebuddy/AnnotationTypeNameMatch.java | 11 + .../bytebuddy/ArgumentTypeNameMatch.java | 12 + .../CacheableTransformerDecorator.java | 195 ---------------- .../plugin/bytebuddy/ReturnTypeNameMatch.java | 11 + .../ConstructorInterceptPoint.java | 11 + .../InstanceMethodsInterceptPoint.java | 11 + .../StaticMethodsInterceptPoint.java | 11 + .../enhance/ClassEnhancePluginDefine.java | 12 +- .../enhance/DelegateNamingResolver.java | 75 +++++++ .../v2/ClassEnhancePluginDefineV2.java | 13 +- .../v2/ConstructorInterceptV2Point.java | 11 + .../v2/InstanceMethodsInterceptV2Point.java | 11 + .../v2/StaticMethodsInterceptV2Point.java | 11 + .../MethodInheritanceAnnotationMatcher.java | 5 + .../plugin/match/ProtectiveShieldMatcher.java | 5 + .../apm/agent/core/bytebuddy/ClassScan.java | 106 +++++++++ .../ElementMatcherSubclassVerifyTest.java | 90 ++++++++ apm-sniffer/apm-agent/pom.xml | 5 + .../skywalking/apm/agent/SkyWalkingAgent.java | 40 ++-- .../finagle-6.25.x-plugin/pom.xml | 2 + apm-sniffer/bytebuddy-patch/pom.xml | 69 ++++++ .../agent/builder/SWAgentBuilderDefault.java | 84 +++++++ .../agent/builder/SWNativeMethodStrategy.java | 43 ++++ .../SWImplementationContextFactory.java | 61 +++++ .../SWAuxiliaryTypeNamingStrategy.java | 42 ++++ .../bytebuddy/SWMethodNameTransformer.java | 44 ++++ .../apm/agent/bytebuddy/ConstructorInter.java | 54 +++++ .../apm/agent/bytebuddy/EnhanceHelper.java | 76 +++++++ .../apm/agent/bytebuddy/InstMethodsInter.java | 63 ++++++ .../skywalking/apm/agent/bytebuddy/Log.java | 59 +++++ .../agent/bytebuddy/SWAsmVisitorWrapper.java | 120 ++++++++++ .../agent/bytebuddy/SWClassFileLocator.java | 173 ++++++++++++++ .../SWExtractionClassFileTransformer.java | 71 ++++++ .../apm/agent/bytebuddy/biz/BizFoo.java | 44 ++++ .../apm/agent/bytebuddy/biz/ProjectDO.java} | 31 ++- .../agent/bytebuddy/biz/ProjectService.java | 39 ++++ .../cases/AbstractInterceptTest.java | 212 ++++++++++++++++++ .../cases/AbstractReTransformTest.java | 55 +++++ .../agent/bytebuddy/cases/Intercept1Test.java | 42 ++++ .../agent/bytebuddy/cases/Intercept2Test.java | 45 ++++ .../agent/bytebuddy/cases/Intercept3Test.java | 46 ++++ .../agent/bytebuddy/cases/Intercept4Test.java | 44 ++++ .../agent/bytebuddy/cases/Intercept5Test.java | 47 ++++ .../agent/bytebuddy/cases/Intercept6Test.java | 48 ++++ .../cases/MultipleInterceptorTest.java | 101 +++++++++ .../bytebuddy/cases/ReTransform1Test.java | 86 +++++++ .../bytebuddy/cases/ReTransform2Test.java | 72 ++++++ apm-sniffer/config/agent.config | 9 - apm-sniffer/pom.xml | 1 + .../java-agent/configurations.md | 2 - pom.xml | 11 +- .../retransform-class-scenario/bin/startup.sh | 3 - .../configuration.yml | 1 + .../configuration.yml | 4 +- 57 files changed, 2313 insertions(+), 258 deletions(-) delete mode 100644 apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/CacheableTransformerDecorator.java create mode 100644 apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/DelegateNamingResolver.java create mode 100644 apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/bytebuddy/ClassScan.java create mode 100644 apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/bytebuddy/ElementMatcherSubclassVerifyTest.java create mode 100644 apm-sniffer/bytebuddy-patch/pom.xml create mode 100644 apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/agent/builder/SWAgentBuilderDefault.java create mode 100644 apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/agent/builder/SWNativeMethodStrategy.java create mode 100644 apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/implementation/SWImplementationContextFactory.java create mode 100644 apm-sniffer/bytebuddy-patch/src/main/java/org/apache/skywalking/apm/agent/bytebuddy/SWAuxiliaryTypeNamingStrategy.java create mode 100644 apm-sniffer/bytebuddy-patch/src/main/java/org/apache/skywalking/apm/agent/bytebuddy/SWMethodNameTransformer.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/ConstructorInter.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/EnhanceHelper.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/InstMethodsInter.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/Log.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/SWAsmVisitorWrapper.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/SWClassFileLocator.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/SWExtractionClassFileTransformer.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/BizFoo.java rename apm-sniffer/{apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/ClassCacheMode.java => bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ProjectDO.java} (65%) create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ProjectService.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/AbstractInterceptTest.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/AbstractReTransformTest.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept1Test.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept2Test.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept3Test.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept4Test.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept5Test.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept6Test.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/MultipleInterceptorTest.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/ReTransform1Test.java create mode 100644 apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/ReTransform2Test.java diff --git a/CHANGES.md b/CHANGES.md index 52404c2dbd..c323acce3a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ Release Notes. * Fix the conflict between the logging kernel and the JDK threadpool plugin. * Fix the thread safety bug of finishing operation for the span named "SpringCloudGateway/sendRequest" * Fix NPE in guava-eventbus-plugin. +* Support re-transform/hot-swap classes with other java agents, and remove the obsolete cache enhanced class feature. #### Documentation diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java index 2ff91a6de4..be7d54a4e4 100755 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java @@ -25,7 +25,6 @@ import org.apache.skywalking.apm.agent.core.logging.core.LogOutput; import org.apache.skywalking.apm.agent.core.logging.core.ResolverType; import org.apache.skywalking.apm.agent.core.logging.core.WriterFactory; -import org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ClassCacheMode; import org.apache.skywalking.apm.util.Length; /** @@ -110,19 +109,6 @@ public static class Agent { */ public static boolean IS_OPEN_DEBUGGING_CLASS = false; - /** - * If true, SkyWalking agent will cache all instrumented classes to memory or disk files (decided by class cache - * mode), allow other javaagent to enhance those classes that enhanced by SkyWalking agent. - */ - public static boolean IS_CACHE_ENHANCED_CLASS = false; - - /** - * The instrumented classes cache mode: MEMORY or FILE MEMORY: cache class bytes to memory, if instrumented - * classes is too many or too large, it may take up more memory FILE: cache class bytes in `/class-cache` - * folder, automatically clean up cached class files when the application exits - */ - public static ClassCacheMode CLASS_CACHE_MODE = ClassCacheMode.MEMORY; - /** * The identifier of the instance */ diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Constants.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Constants.java index 0fd98f0c16..ae6095a06c 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Constants.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Constants.java @@ -32,4 +32,10 @@ public class Constants { public static String EVENT_LAYER_NAME = "GENERAL"; public static int NULL_VALUE = 0; + + /** + * The name trait for auxiliary type names, field names and method names which generated by ByteBuddy. + */ + public static final String NAME_TRAIT = "sw$"; + } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/AnnotationTypeNameMatch.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/AnnotationTypeNameMatch.java index fe13541e93..8bfbe781aa 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/AnnotationTypeNameMatch.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/AnnotationTypeNameMatch.java @@ -55,6 +55,17 @@ public boolean matches(T target) { return target.getAnnotationType().asErasure().getName().equals(annotationTypeName); } + /** + * To ensure that the hashCode for recreating the XxxInterceptPoint instance is the same as the previous instance, + * each ElementMatcher implementation class needs to implement toString() method. + */ + @Override + public String toString() { + return "AnnotationTypeNameMatch{" + + "annotationTypeName='" + annotationTypeName + '\'' + + '}'; + } + /** * The static method to create {@link AnnotationTypeNameMatch} This is a delegate method to follow byte-buddy {@link * ElementMatcher}'s code style. diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/ArgumentTypeNameMatch.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/ArgumentTypeNameMatch.java index 2dae176409..e2256cf460 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/ArgumentTypeNameMatch.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/ArgumentTypeNameMatch.java @@ -68,6 +68,18 @@ public boolean matches(MethodDescription target) { return false; } + /** + * To ensure that the hashCode for recreating the XxxInterceptPoint instance is the same as the previous instance, + * each ElementMatcher implementation class needs to implement toString() method. + */ + @Override + public String toString() { + return "ArgumentTypeNameMatch{" + + "index=" + index + + ", argumentTypeName='" + argumentTypeName + '\'' + + '}'; + } + /** * The static method to create {@link ArgumentTypeNameMatch} This is a delegate method to follow byte-buddy {@link * ElementMatcher}'s code style. diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/CacheableTransformerDecorator.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/CacheableTransformerDecorator.java deleted file mode 100644 index 2b392512de..0000000000 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/CacheableTransformerDecorator.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * 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.core.plugin.bytebuddy; - -import net.bytebuddy.agent.builder.AgentBuilder; -import net.bytebuddy.agent.builder.ResettableClassFileTransformer; -import net.bytebuddy.utility.RandomString; -import org.apache.skywalking.apm.agent.core.boot.AgentPackageNotFoundException; -import org.apache.skywalking.apm.agent.core.boot.AgentPackagePath; -import org.apache.skywalking.apm.agent.core.logging.api.ILog; -import org.apache.skywalking.apm.agent.core.logging.api.LogManager; -import org.apache.skywalking.apm.agent.core.util.FileUtils; -import org.apache.skywalking.apm.agent.core.util.IOUtils; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.lang.instrument.IllegalClassFormatException; -import java.security.ProtectionDomain; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Wrapper classFileTransformer of ByteBuddy, save the enhanced bytecode to memory cache or file cache, - * and automatically load the previously generated bytecode during the second retransform, - * to solve the problem that ByteBuddy generates auxiliary classes with different random names every time. - * Allow other javaagent to enhance those classes that enhanced by SkyWalking agent. - */ -public class CacheableTransformerDecorator implements AgentBuilder.TransformerDecorator { - - private static final ILog LOGGER = LogManager.getLogger(CacheableTransformerDecorator.class); - - private final ClassCacheMode cacheMode; - private ClassCacheResolver cacheResolver; - - public CacheableTransformerDecorator(ClassCacheMode cacheMode) throws IOException { - this.cacheMode = cacheMode; - initClassCache(); - } - - private void initClassCache() throws IOException { - if (this.cacheMode.equals(ClassCacheMode.FILE)) { - String cacheDirBase = null; - try { - cacheDirBase = AgentPackagePath.getPath() + "/class-cache"; - } catch (AgentPackageNotFoundException e) { - throw new IOException("Can't find the root path for creating /class-cache folder."); - } - File cacheDir = new File(cacheDirBase + "/class-cache-" + RandomString.make()); - if (!cacheDir.exists()) { - cacheDir.mkdirs(); - } - if (!cacheDir.exists()) { - throw new IOException("Create class cache dir failure"); - } - - cacheResolver = new FileCacheResolver(cacheDir); - } else { - cacheResolver = new MemoryCacheResolver(); - } - } - - @Override - public ResettableClassFileTransformer decorate(ResettableClassFileTransformer classFileTransformer) { - return new ResettableClassFileTransformer.WithDelegation(classFileTransformer) { - - @Override - public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { - // load from cache - byte[] classCache = cacheResolver.getClassCache(loader, className); - if (classCache != null) { - return classCache; - } - - //transform class - classfileBuffer = classFileTransformer.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); - - // save to cache - if (classfileBuffer != null) { - cacheResolver.putClassCache(loader, className, classfileBuffer); - } - - return classfileBuffer; - } - }; - } - - private static String getClassLoaderHash(ClassLoader loader) { - String classloader; - if (loader != null) { - classloader = Integer.toHexString(loader.hashCode()); - } else { - //classloader is null for BootstrapClassLoader - classloader = "00000000"; - } - return classloader; - } - - interface ClassCacheResolver { - - byte[] getClassCache(ClassLoader loader, String className); - - void putClassCache(ClassLoader loader, String className, byte[] classfileBuffer); - } - - static class MemoryCacheResolver implements ClassCacheResolver { - // classloaderHashcode@className -> class bytes - private Map classCacheMap = new ConcurrentHashMap(); - - @Override - public byte[] getClassCache(ClassLoader loader, String className) { - String cacheKey = getCacheKey(loader, className); - return classCacheMap.get(cacheKey); - } - - @Override - public void putClassCache(ClassLoader loader, String className, byte[] classfileBuffer) { - String cacheKey = getCacheKey(loader, className); - classCacheMap.put(cacheKey, classfileBuffer); - } - - private String getCacheKey(ClassLoader loader, String className) { - return getClassLoaderHash(loader) + "@" + className; - } - } - - static class FileCacheResolver implements ClassCacheResolver { - - private final File cacheDir; - - FileCacheResolver(File cacheDir) { - this.cacheDir = cacheDir; - - //clean cache dir on exit - FileUtils.deleteDirectoryOnExit(cacheDir); - } - - @Override - public byte[] getClassCache(ClassLoader loader, String className) { - // load from cache - File cacheFile = getCacheFile(loader, className); - if (cacheFile.exists()) { - FileInputStream fileInputStream = null; - try { - fileInputStream = new FileInputStream(cacheFile); - return IOUtils.toByteArray(fileInputStream); - } catch (IOException e) { - LOGGER.error("load class bytes from cache file failure", e); - } finally { - IOUtils.closeQuietly(fileInputStream); - } - } - return null; - } - - @Override - public void putClassCache(ClassLoader loader, String className, byte[] classfileBuffer) { - File cacheFile = getCacheFile(loader, className); - cacheFile.getParentFile().mkdirs(); - FileOutputStream output = null; - try { - output = new FileOutputStream(cacheFile); - IOUtils.copy(new ByteArrayInputStream(classfileBuffer), output); - } catch (IOException e) { - LOGGER.error("save class bytes to cache file failure", e); - } finally { - IOUtils.closeQuietly(output); - } - } - - private File getCacheFile(ClassLoader loader, String className) { - String filename = getClassLoaderHash(loader) + "/" + className.replace('.', '/') + ".class"; - return new File(cacheDir, filename); - } - - } -} diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/ReturnTypeNameMatch.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/ReturnTypeNameMatch.java index 290542b777..d8c65a5f2f 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/ReturnTypeNameMatch.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/ReturnTypeNameMatch.java @@ -53,6 +53,17 @@ public boolean matches(MethodDescription target) { return target.getReturnType().asErasure().getName().equals(returnTypeName); } + /** + * To ensure that the hashCode for recreating the XxxInterceptPoint instance is the same as the previous instance, + * each ElementMatcher implementation class needs to implement toString() method. + */ + @Override + public String toString() { + return "ReturnTypeNameMatch{" + + "returnTypeName='" + returnTypeName + '\'' + + '}'; + } + /** * The static method to create {@link ReturnTypeNameMatch} This is a delegate method to follow byte-buddy {@link * ElementMatcher}'s code style. diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/ConstructorInterceptPoint.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/ConstructorInterceptPoint.java index 9c98419466..200c1890e0 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/ConstructorInterceptPoint.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/ConstructorInterceptPoint.java @@ -21,6 +21,8 @@ import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.matcher.ElementMatcher; +import java.util.Objects; + /** * One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this * "Intercept Point", the definition targets class's constructors, and the interceptor. @@ -41,4 +43,13 @@ public interface ConstructorInterceptPoint { * org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor} */ String getConstructorInterceptor(); + + /** + * To ensure that the hashCode for recreating the XxxInterceptPoint instance is the same as the previous instance, + * each ElementMatcher implementation class needs to implement toString() method. + * @return hashCode of this intercept point + */ + default int computeHashCode() { + return Objects.hash(this.getClass().getName(), this.getConstructorMatcher().toString(), this.getConstructorInterceptor()); + } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/InstanceMethodsInterceptPoint.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/InstanceMethodsInterceptPoint.java index 6ae8057e90..aeb8bac5fe 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/InstanceMethodsInterceptPoint.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/InstanceMethodsInterceptPoint.java @@ -21,6 +21,8 @@ import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.matcher.ElementMatcher; +import java.util.Objects; + /** * One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this * "Intercept Point", the definition targets class's instance methods, and the interceptor. @@ -42,4 +44,13 @@ public interface InstanceMethodsInterceptPoint { String getMethodsInterceptor(); boolean isOverrideArgs(); + + /** + * To ensure that the hashCode for recreating the XxxInterceptPoint instance is the same as the previous instance, + * each ElementMatcher implementation class needs to implement toString() method. + * @return hashCode of this intercept point + */ + default int computeHashCode() { + return Objects.hash(this.getClass().getName(), this.getMethodsMatcher().toString(), this.getMethodsInterceptor(), this.isOverrideArgs()); + } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/StaticMethodsInterceptPoint.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/StaticMethodsInterceptPoint.java index 2af763bc29..9e0eb2f4f2 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/StaticMethodsInterceptPoint.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/StaticMethodsInterceptPoint.java @@ -21,6 +21,8 @@ import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.matcher.ElementMatcher; +import java.util.Objects; + /** * One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this * "Intercept Point", the definition targets class's static methods, and the interceptor. @@ -42,4 +44,13 @@ public interface StaticMethodsInterceptPoint { String getMethodsInterceptor(); boolean isOverrideArgs(); + + /** + * To ensure that the hashCode for recreating the XxxInterceptPoint instance is the same as the previous instance, + * each ElementMatcher implementation class needs to implement toString() method. + * @return hashCode of this intercept point + */ + default int computeHashCode() { + return Objects.hash(this.getClass().getName(), this.getMethodsMatcher().toString(), this.getMethodsInterceptor(), this.isOverrideArgs()); + } } 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 3c545d80f0..e1a1fa4261 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 @@ -78,6 +78,7 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription if (instanceMethodsInterceptPoints != null && instanceMethodsInterceptPoints.length > 0) { existedMethodsInterceptPoints = true; } + DelegateNamingResolver delegateNamingResolver = new DelegateNamingResolver(typeDescription.getTypeName(), this); /** * nothing need to be enhanced in class instance, maybe need enhance static methods. @@ -121,7 +122,7 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher()) .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration() .to(new ConstructorInter(constructorInterceptPoint - .getConstructorInterceptor(), classLoader)))); + .getConstructorInterceptor(), classLoader), delegateNamingResolver.resolve(constructorInterceptPoint)))); } } } @@ -149,7 +150,7 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription newClassBuilder = newClassBuilder.method(junction) .intercept(MethodDelegation.withDefaultConfiguration() .withBinders(Morph.Binder.install(OverrideCallable.class)) - .to(new InstMethodsInterWithOverrideArgs(interceptor, classLoader))); + .to(new InstMethodsInterWithOverrideArgs(interceptor, classLoader), delegateNamingResolver.resolve(instanceMethodsInterceptPoint))); } } else { if (isBootstrapInstrumentation()) { @@ -159,7 +160,7 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription } else { newClassBuilder = newClassBuilder.method(junction) .intercept(MethodDelegation.withDefaultConfiguration() - .to(new InstMethodsInter(interceptor, classLoader))); + .to(new InstMethodsInter(interceptor, classLoader), delegateNamingResolver.resolve(instanceMethodsInterceptPoint))); } } } @@ -183,6 +184,7 @@ protected DynamicType.Builder enhanceClass(TypeDescription typeDescription, D if (staticMethodsInterceptPoints == null || staticMethodsInterceptPoints.length == 0) { return newClassBuilder; } + DelegateNamingResolver delegateNamingResolver = new DelegateNamingResolver(typeDescription.getTypeName(), this); for (StaticMethodsInterceptPoint staticMethodsInterceptPoint : staticMethodsInterceptPoints) { String interceptor = staticMethodsInterceptPoint.getMethodsInterceptor(); @@ -200,7 +202,7 @@ protected DynamicType.Builder enhanceClass(TypeDescription typeDescription, D newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher())) .intercept(MethodDelegation.withDefaultConfiguration() .withBinders(Morph.Binder.install(OverrideCallable.class)) - .to(new StaticMethodsInterWithOverrideArgs(interceptor))); + .to(new StaticMethodsInterWithOverrideArgs(interceptor), delegateNamingResolver.resolve(staticMethodsInterceptPoint))); } } else { if (isBootstrapInstrumentation()) { @@ -210,7 +212,7 @@ protected DynamicType.Builder enhanceClass(TypeDescription typeDescription, D } else { newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher())) .intercept(MethodDelegation.withDefaultConfiguration() - .to(new StaticMethodsInter(interceptor))); + .to(new StaticMethodsInter(interceptor), delegateNamingResolver.resolve(staticMethodsInterceptPoint))); } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/DelegateNamingResolver.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/DelegateNamingResolver.java new file mode 100644 index 0000000000..8b4fe607ce --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/enhance/DelegateNamingResolver.java @@ -0,0 +1,75 @@ +/* + * 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.core.plugin.interceptor.enhance; + +import net.bytebuddy.utility.RandomString; +import org.apache.skywalking.apm.agent.core.conf.Constants; +import org.apache.skywalking.apm.agent.core.plugin.AbstractClassEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.StaticMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.v2.ConstructorInterceptV2Point; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.v2.InstanceMethodsInterceptV2Point; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.v2.StaticMethodsInterceptV2Point; + +import java.util.Objects; + +/** + * Generate fixed delegate field name for MethodDelegation + */ +public class DelegateNamingResolver { + private static final String PREFIX = "delegate$"; + private final String fieldNamePrefix; + + public DelegateNamingResolver(String className, AbstractClassEnhancePluginDefine pluginDefine) { + // Interceptor delegate field name pattern: $delegate$$$ + // something like: InstMethodsInter sw$delegate$td03673$sib0lj0$5n874b1; + this.fieldNamePrefix = Constants.NAME_TRAIT + PREFIX + RandomString.hashOf(className.hashCode()) + "$" + RandomString.hashOf(pluginDefine.hashCode()) + "$"; + } + + public String resolve(ConstructorInterceptPoint interceptPoint) { + Objects.requireNonNull(interceptPoint, "interceptPoint cannot be null"); + return fieldNamePrefix + RandomString.hashOf(interceptPoint.computeHashCode()); + } + + public String resolve(ConstructorInterceptV2Point interceptPoint) { + Objects.requireNonNull(interceptPoint, "interceptPoint cannot be null"); + return fieldNamePrefix + RandomString.hashOf(interceptPoint.computeHashCode()); + } + + public String resolve(InstanceMethodsInterceptPoint interceptPoint) { + Objects.requireNonNull(interceptPoint, "interceptPoint cannot be null"); + return fieldNamePrefix + RandomString.hashOf(interceptPoint.computeHashCode()); + } + + public String resolve(InstanceMethodsInterceptV2Point interceptPoint) { + Objects.requireNonNull(interceptPoint, "interceptPoint cannot be null"); + return fieldNamePrefix + RandomString.hashOf(interceptPoint.computeHashCode()); + } + + public String resolve(StaticMethodsInterceptPoint interceptPoint) { + Objects.requireNonNull(interceptPoint, "interceptPoint cannot be null"); + return fieldNamePrefix + RandomString.hashOf(interceptPoint.computeHashCode()); + } + + public String resolve(StaticMethodsInterceptV2Point interceptPoint) { + Objects.requireNonNull(interceptPoint, "interceptPoint cannot be null"); + return fieldNamePrefix + RandomString.hashOf(interceptPoint.computeHashCode()); + } +} 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 733d32f184..2436c2994b 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 @@ -35,6 +35,7 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.StaticMethodsInterceptPoint; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ConstructorInter; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.DelegateNamingResolver; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.OverrideCallable; import org.apache.skywalking.apm.agent.core.plugin.interceptor.v2.ConstructorInterceptV2Point; @@ -65,6 +66,7 @@ protected DynamicType.Builder enhanceClass(TypeDescription typeDescription, if (staticMethodsInterceptV2Points == null || staticMethodsInterceptV2Points.length == 0) { return newClassBuilder; } + DelegateNamingResolver delegateNamingResolver = new DelegateNamingResolver(typeDescription.getTypeName(), this); for (StaticMethodsInterceptV2Point staticMethodsInterceptV2Point : staticMethodsInterceptV2Points) { String interceptor = staticMethodsInterceptV2Point.getMethodsInterceptorV2(); @@ -85,7 +87,7 @@ protected DynamicType.Builder enhanceClass(TypeDescription typeDescription, isStatic().and(staticMethodsInterceptV2Point.getMethodsMatcher())) .intercept(MethodDelegation.withDefaultConfiguration() .withBinders(Morph.Binder.install(OverrideCallable.class)) - .to(new StaticMethodsInterV2WithOverrideArgs(interceptor))); + .to(new StaticMethodsInterV2WithOverrideArgs(interceptor), delegateNamingResolver.resolve(staticMethodsInterceptV2Point))); } } else { if (isBootstrapInstrumentation()) { @@ -97,7 +99,7 @@ protected DynamicType.Builder enhanceClass(TypeDescription typeDescription, newClassBuilder = newClassBuilder.method( isStatic().and(staticMethodsInterceptV2Point.getMethodsMatcher())) .intercept(MethodDelegation.withDefaultConfiguration() - .to(new StaticMethodsInterV2(interceptor))); + .to(new StaticMethodsInterV2(interceptor), delegateNamingResolver.resolve(staticMethodsInterceptV2Point))); } } @@ -113,6 +115,7 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription ConstructorInterceptPoint[] constructorInterceptPoints = getConstructorsInterceptPoints(); InstanceMethodsInterceptV2Point[] instanceMethodsInterceptV2Points = getInstanceMethodsInterceptV2Points(); String enhanceOriginClassName = typeDescription.getTypeName(); + DelegateNamingResolver fieldNamingResolver = new DelegateNamingResolver(typeDescription.getTypeName(), this); boolean existedConstructorInterceptPoint = false; if (constructorInterceptPoints != null && constructorInterceptPoints.length > 0) { @@ -149,7 +152,7 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher()) .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration() .to(new ConstructorInter(constructorInterceptPoint - .getConstructorInterceptor(), classLoader)))); + .getConstructorInterceptor(), classLoader), fieldNamingResolver.resolve(constructorInterceptPoint)))); } } } @@ -176,7 +179,7 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription newClassBuilder = newClassBuilder.method(junction) .intercept(MethodDelegation.withDefaultConfiguration() .withBinders(Morph.Binder.install(OverrideCallable.class)) - .to(new InstMethodsInterV2WithOverrideArgs(interceptor, classLoader))); + .to(new InstMethodsInterV2WithOverrideArgs(interceptor, classLoader), fieldNamingResolver.resolve(instanceMethodsInterceptV2Point))); } } else { if (isBootstrapInstrumentation()) { @@ -186,7 +189,7 @@ protected DynamicType.Builder enhanceInstance(TypeDescription typeDescription } else { newClassBuilder = newClassBuilder.method(junction) .intercept(MethodDelegation.withDefaultConfiguration() - .to(new InstMethodsInterV2(interceptor, classLoader))); + .to(new InstMethodsInterV2(interceptor, classLoader), fieldNamingResolver.resolve(instanceMethodsInterceptV2Point))); } } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/v2/ConstructorInterceptV2Point.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/v2/ConstructorInterceptV2Point.java index dcd0fa30b4..f494627890 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/v2/ConstructorInterceptV2Point.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/v2/ConstructorInterceptV2Point.java @@ -20,6 +20,8 @@ import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.matcher.ElementMatcher; +import java.util.Objects; + public interface ConstructorInterceptV2Point { /** @@ -35,4 +37,13 @@ public interface ConstructorInterceptV2Point { */ String getConstructorInterceptorV2(); + /** + * To ensure that the hashCode for recreating the XxxInterceptPoint instance is the same as the previous instance, + * each ElementMatcher implementation class needs to implement toString() method. + * @return hashCode of this intercept point + */ + default int computeHashCode() { + return Objects.hash(this.getClass().getName(), this.getConstructorMatcher().toString(), this.getConstructorInterceptorV2()); + } + } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/v2/InstanceMethodsInterceptV2Point.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/v2/InstanceMethodsInterceptV2Point.java index e8932aa35b..18ed4b0beb 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/v2/InstanceMethodsInterceptV2Point.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/v2/InstanceMethodsInterceptV2Point.java @@ -22,6 +22,8 @@ import net.bytebuddy.matcher.ElementMatcher; import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import java.util.Objects; + /** * One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this * "Intercept Point", the definition targets class's instance methods, and the interceptor. @@ -43,4 +45,13 @@ public interface InstanceMethodsInterceptV2Point { String getMethodsInterceptorV2(); boolean isOverrideArgs(); + + /** + * To ensure that the hashCode for recreating the XxxInterceptPoint instance is the same as the previous instance, + * each ElementMatcher implementation class needs to implement toString() method. + * @return hashCode of this intercept point + */ + default int computeHashCode() { + return Objects.hash(this.getClass().getName(), this.getMethodsMatcher().toString(), this.getMethodsInterceptorV2(), this.isOverrideArgs()); + } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/v2/StaticMethodsInterceptV2Point.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/v2/StaticMethodsInterceptV2Point.java index 78052a806e..a9fb4f9287 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/v2/StaticMethodsInterceptV2Point.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/interceptor/v2/StaticMethodsInterceptV2Point.java @@ -22,6 +22,8 @@ import net.bytebuddy.matcher.ElementMatcher; import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import java.util.Objects; + /** * One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this * "Intercept Point", the definition targets class's static methods, and the interceptor. @@ -43,4 +45,13 @@ public interface StaticMethodsInterceptV2Point { String getMethodsInterceptorV2(); boolean isOverrideArgs(); + + /** + * To ensure that the hashCode for recreating the XxxInterceptPoint instance is the same as the previous instance, + * each ElementMatcher implementation class needs to implement toString() method. + * @return hashCode of this intercept point + */ + default int computeHashCode() { + return Objects.hash(this.getClass().getName(), this.getMethodsMatcher().toString(), this.getMethodsInterceptorV2(), this.isOverrideArgs()); + } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/MethodInheritanceAnnotationMatcher.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/MethodInheritanceAnnotationMatcher.java index 21511c1b23..a61740e98e 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/MethodInheritanceAnnotationMatcher.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/MethodInheritanceAnnotationMatcher.java @@ -97,4 +97,9 @@ public static ElementMatcher.Junction byMethodIn ElementMatcher matcher) { return new MethodInheritanceAnnotationMatcher(new CollectionItemMatcher<>(annotationType(matcher))); } + + @Override + public String toString() { + return "MethodInheritanceAnnotationMatcher(" + matcher + ')'; + } } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/ProtectiveShieldMatcher.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/ProtectiveShieldMatcher.java index 96c0d7bbec..75e6dc86f8 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/ProtectiveShieldMatcher.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/ProtectiveShieldMatcher.java @@ -52,4 +52,9 @@ public boolean matches(T target) { return false; } } + + @Override + public String toString() { + return "ProtectiveShieldMatcher(" + matcher + ')'; + } } diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/bytebuddy/ClassScan.java b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/bytebuddy/ClassScan.java new file mode 100644 index 0000000000..fa927fc624 --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/bytebuddy/ClassScan.java @@ -0,0 +1,106 @@ +/* + * 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.core.bytebuddy; + +import com.google.common.collect.ImmutableSet; +import com.google.common.reflect.ClassPath; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; + +/** + * Scan the classes, and notify the listener(s) + */ +public class ClassScan { + + private final List listeners; + + public ClassScan() { + this.listeners = new LinkedList<>(); + } + + /** + * Register the callback listener + * + * @param listener to be called after class found + */ + public void registerListener(ClassScanListener listener) { + listeners.add(new ListenerCache(listener)); + } + + /** + * Begin to scan classes. + */ + public void scan() throws Exception { + ClassPath classpath = ClassPath.from(this.getClass().getClassLoader()); + ImmutableSet classes = classpath.getAllClasses(); + String packagePrefix = "org.apache.skywalking."; + for (ClassPath.ClassInfo classInfo : classes) { + if (!classInfo.getName().startsWith(packagePrefix)) { + continue; + } + Class aClass = classInfo.load(); + + for (ListenerCache listener : listeners) { + if (listener.classMatch().matches(TypeDescription.ForLoadedType.of(aClass))) { + listener.addMatch(aClass); + } + } + } + + for (ListenerCache listener : listeners) { + listener.complete(); + } + } + + public interface ClassScanListener { + + ElementMatcher classMatch(); + + void notify(Class aClass) throws Exception; + } + + private class ListenerCache { + private ClassScanListener listener; + private List> matchedClass; + + private ListenerCache(ClassScanListener listener) { + this.listener = listener; + matchedClass = new LinkedList<>(); + } + + private ElementMatcher classMatch() { + return this.listener.classMatch(); + } + + private void addMatch(Class aClass) { + matchedClass.add(aClass); + } + + private void complete() throws Exception { + matchedClass.sort(Comparator.comparing(Class::getName)); + for (Class aClass : matchedClass) { + listener.notify(aClass); + } + } + } +} diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/bytebuddy/ElementMatcherSubclassVerifyTest.java b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/bytebuddy/ElementMatcherSubclassVerifyTest.java new file mode 100644 index 0000000000..85e1c1dff8 --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/bytebuddy/ElementMatcherSubclassVerifyTest.java @@ -0,0 +1,90 @@ +/* + * 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.core.bytebuddy; + +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.DelegateNamingResolver; +import org.junit.Assert; +import org.junit.Test; + +import java.io.PrintStream; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.isAbstract; +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; + +/** + * Verify subclass of ElementMatcher make sure to impl 'toString()' method. + * see {@link InstanceMethodsInterceptPoint#computeHashCode()}, {@link DelegateNamingResolver} + */ +public class ElementMatcherSubclassVerifyTest { + + // ignore nest subclass inside PluginFinder + private final ElementMatcher ignoredClass = nameStartsWith("org.apache.skywalking.apm.agent.core.plugin.PluginFinder$"); + + @Test + public void test() throws Exception { + List> matchedCLasses = new ArrayList<>(); + ClassScan classScan = new ClassScan(); + classScan.registerListener(new ClassScan.ClassScanListener() { + @Override + public ElementMatcher classMatch() { + return hasSuperType(named(ElementMatcher.class.getName())).and(not(isAbstract())); + } + + @Override + public void notify(Class aClass) throws Exception { + if (ignoredClass.matches(TypeDescription.ForLoadedType.of(aClass))) { + return; + } + try { + // make sure subclass of ElementMatcher has implement "toString" method + Method toStringMethod = aClass.getDeclaredMethod("toString"); + } catch (NoSuchMethodException e) { + matchedCLasses.add(aClass); + } + } + }); + classScan.scan(); + + // print matched classes + PrintStream err = System.err; + for (Class aClass : matchedCLasses) { + String className = aClass.getName(); + if (!className.equals(BadMatcher.class.getName())) { + err.println("Requiring toString() method for subclass of ElementMatcher: " + className); + } + } + Assert.assertEquals(1, matchedCLasses.size()); + } + + public static class BadMatcher implements ElementMatcher { + @Override + public boolean matches(T target) { + return false; + } + } +} diff --git a/apm-sniffer/apm-agent/pom.xml b/apm-sniffer/apm-agent/pom.xml index e70018f03f..15b1e32024 100644 --- a/apm-sniffer/apm-agent/pom.xml +++ b/apm-sniffer/apm-agent/pom.xml @@ -47,6 +47,11 @@ apm-agent-core ${project.version} + + org.apache.skywalking + bytebuddy-patch + ${project.version} + diff --git a/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java b/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java index dd0a072164..8e0c2495b5 100644 --- a/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java +++ b/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java @@ -25,13 +25,18 @@ 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.SWNativeMethodStrategy; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.scaffold.TypeValidation; +import org.apache.skywalking.apm.agent.bytebuddy.SWAuxiliaryTypeNamingStrategy; +import net.bytebuddy.implementation.SWImplementationContextFactory; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatchers; import net.bytebuddy.utility.JavaModule; +import org.apache.skywalking.apm.agent.bytebuddy.SWMethodNameTransformer; import org.apache.skywalking.apm.agent.core.boot.AgentPackageNotFoundException; import org.apache.skywalking.apm.agent.core.boot.ServiceManager; import org.apache.skywalking.apm.agent.core.conf.Config; @@ -46,12 +51,13 @@ import org.apache.skywalking.apm.agent.core.plugin.PluginException; import org.apache.skywalking.apm.agent.core.plugin.PluginFinder; import org.apache.skywalking.apm.agent.core.plugin.bootstrap.BootstrapInstrumentBoost; -import org.apache.skywalking.apm.agent.core.plugin.bytebuddy.CacheableTransformerDecorator; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.DelegateNamingResolver; import org.apache.skywalking.apm.agent.core.plugin.jdk9module.JDK9ModuleExporter; import static net.bytebuddy.matcher.ElementMatchers.nameContains; import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; import static net.bytebuddy.matcher.ElementMatchers.not; +import static org.apache.skywalking.apm.agent.core.conf.Constants.NAME_TRAIT; /** * The main entrance of sky-walking agent, based on javaagent mechanism. @@ -69,7 +75,7 @@ public static void premain(String agentArgs, Instrumentation instrumentation) th } catch (Exception e) { // try to resolve a new logger, and use the new logger to write the error log here LogManager.getLogger(SkyWalkingAgent.class) - .error(e, "SkyWalking agent initialized failure. Shutting down."); + .error(e, "SkyWalking agent initialized failure. Shutting down."); return; } finally { // refresh logger again after initialization finishes @@ -91,9 +97,9 @@ public static void premain(String agentArgs, Instrumentation instrumentation) th return; } - final ByteBuddy byteBuddy = new ByteBuddy().with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS)); + LOGGER.info("Skywalking agent begin to install transformer ..."); - AgentBuilder agentBuilder = new AgentBuilder.Default(byteBuddy).ignore( + AgentBuilder agentBuilder = newAgentBuilder().ignore( nameStartsWith("net.bytebuddy.") .or(nameStartsWith("org.slf4j.")) .or(nameStartsWith("org.groovy.")) @@ -119,15 +125,6 @@ public static void premain(String agentArgs, Instrumentation instrumentation) th return; } - if (Config.Agent.IS_CACHE_ENHANCED_CLASS) { - try { - agentBuilder = agentBuilder.with(new CacheableTransformerDecorator(Config.Agent.CLASS_CACHE_MODE)); - LOGGER.info("SkyWalking agent class cache [{}] activated.", Config.Agent.CLASS_CACHE_MODE); - } catch (Exception e) { - LOGGER.error(e, "SkyWalking agent can't active class cache."); - } - } - agentBuilder.type(pluginFinder.buildMatch()) .transform(new Transformer(pluginFinder)) .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) @@ -137,6 +134,8 @@ public static void premain(String agentArgs, Instrumentation instrumentation) th PluginFinder.pluginInitCompleted(); + LOGGER.info("Skywalking agent transformer has installed."); + try { ServiceManager.INSTANCE.boot(); } catch (Exception e) { @@ -147,6 +146,21 @@ public static void premain(String agentArgs, Instrumentation instrumentation) th .addShutdownHook(new Thread(ServiceManager.INSTANCE::shutdown, "skywalking service shutdown thread")); } + /** + * Create a new agent builder through customized {@link ByteBuddy} powered by + * {@link SWAuxiliaryTypeNamingStrategy} {@link DelegateNamingResolver} {@link SWMethodNameTransformer} and {@link SWImplementationContextFactory} + */ + private static AgentBuilder newAgentBuilder() { + final ByteBuddy byteBuddy = new ByteBuddy() + .with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS)) + .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); + } + private static class Transformer implements AgentBuilder.Transformer { private PluginFinder pluginFinder; diff --git a/apm-sniffer/apm-sdk-plugin/finagle-6.25.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/finagle-6.25.x-plugin/pom.xml index da691fa2e7..6837d9a75e 100644 --- a/apm-sniffer/apm-sdk-plugin/finagle-6.25.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/finagle-6.25.x-plugin/pom.xml @@ -35,6 +35,8 @@ UTF-8 6.34.0 2.11.12 + 1.8 + 1.8 diff --git a/apm-sniffer/bytebuddy-patch/pom.xml b/apm-sniffer/bytebuddy-patch/pom.xml new file mode 100644 index 0000000000..9d3ef6dac8 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/pom.xml @@ -0,0 +1,69 @@ + + + + + + org.apache.skywalking + java-agent-sniffer + 8.17.0-SNAPSHOT + + 4.0.0 + + bytebuddy-patch + + + + org.apache.skywalking + apm-agent-core + ${project.version} + + + net.bytebuddy + byte-buddy + + + + org.projectlombok + lombok + 1.18.20 + provided + + + + junit + junit + test + + + org.ow2.asm + asm + 5.0.3 + test + + + org.ow2.asm + asm-util + 5.0.3 + test + + + + \ No newline at end of file diff --git a/apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/agent/builder/SWAgentBuilderDefault.java b/apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/agent/builder/SWAgentBuilderDefault.java new file mode 100644 index 0000000000..958cf7c51e --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/agent/builder/SWAgentBuilderDefault.java @@ -0,0 +1,84 @@ +/* + * 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.ByteBuddy; +import net.bytebuddy.NamingStrategy; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.ClassFileLocator; +import net.bytebuddy.matcher.ElementMatchers; + +import java.util.Collections; +import java.util.List; + +import static net.bytebuddy.matcher.ElementMatchers.any; +import static net.bytebuddy.matcher.ElementMatchers.isBootstrapClassLoader; +import static net.bytebuddy.matcher.ElementMatchers.isExtensionClassLoader; +import static net.bytebuddy.matcher.ElementMatchers.isSynthetic; +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; +import static net.bytebuddy.matcher.ElementMatchers.not; + +/** + * A custom AgentBuilder.Default for changing NativeMethodStrategy + */ +public class SWAgentBuilderDefault extends AgentBuilder.Default { + + /** + * The default circularity lock that assures that no agent created by any agent builder within this + * class loader causes a class loading circularity. + */ + private static final CircularityLock DEFAULT_LOCK = new CircularityLock.Default(); + + public SWAgentBuilderDefault(ByteBuddy byteBuddy, NativeMethodStrategy nativeMethodStrategy) { + this(byteBuddy, + Listener.NoOp.INSTANCE, + DEFAULT_LOCK, + PoolStrategy.Default.FAST, + TypeStrategy.Default.REBASE, + LocationStrategy.ForClassLoader.STRONG, + ClassFileLocator.NoOp.INSTANCE, + nativeMethodStrategy, + WarmupStrategy.NoOp.INSTANCE, + TransformerDecorator.NoOp.INSTANCE, + new InitializationStrategy.SelfInjection.Split(), + RedefinitionStrategy.DISABLED, + RedefinitionStrategy.DiscoveryStrategy.SinglePass.INSTANCE, + RedefinitionStrategy.BatchAllocator.ForTotal.INSTANCE, + RedefinitionStrategy.Listener.NoOp.INSTANCE, + RedefinitionStrategy.ResubmissionStrategy.Disabled.INSTANCE, + InjectionStrategy.UsingReflection.INSTANCE, + LambdaInstrumentationStrategy.DISABLED, + DescriptionStrategy.Default.HYBRID, + FallbackStrategy.ByThrowableType.ofOptionalTypes(), + ClassFileBufferStrategy.Default.RETAINING, + InstallationListener.NoOp.INSTANCE, + new RawMatcher.Disjunction( + new RawMatcher.ForElementMatchers(any(), isBootstrapClassLoader().or(isExtensionClassLoader())), + new RawMatcher.ForElementMatchers(nameStartsWith("net.bytebuddy.") + .and(not(ElementMatchers.nameStartsWith(NamingStrategy.BYTE_BUDDY_RENAME_PACKAGE + "."))) + .or(nameStartsWith("sun.reflect.").or(nameStartsWith("jdk.internal.reflect."))) + .or(isSynthetic()))), + Collections.emptyList()); + } + + protected SWAgentBuilderDefault(ByteBuddy byteBuddy, Listener listener, CircularityLock circularityLock, PoolStrategy poolStrategy, TypeStrategy typeStrategy, LocationStrategy locationStrategy, ClassFileLocator classFileLocator, NativeMethodStrategy nativeMethodStrategy, WarmupStrategy warmupStrategy, TransformerDecorator transformerDecorator, InitializationStrategy initializationStrategy, RedefinitionStrategy redefinitionStrategy, RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy, RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator, RedefinitionStrategy.Listener redefinitionListener, RedefinitionStrategy.ResubmissionStrategy redefinitionResubmissionStrategy, InjectionStrategy injectionStrategy, LambdaInstrumentationStrategy lambdaInstrumentationStrategy, DescriptionStrategy descriptionStrategy, FallbackStrategy fallbackStrategy, ClassFileBufferStrategy classFileBufferStrategy, InstallationListener installationListener, RawMatcher ignoreMatcher, List transformations) { + super(byteBuddy, listener, circularityLock, poolStrategy, typeStrategy, locationStrategy, classFileLocator, nativeMethodStrategy, warmupStrategy, transformerDecorator, initializationStrategy, redefinitionStrategy, redefinitionDiscoveryStrategy, redefinitionBatchAllocator, redefinitionListener, redefinitionResubmissionStrategy, injectionStrategy, lambdaInstrumentationStrategy, descriptionStrategy, fallbackStrategy, classFileBufferStrategy, installationListener, ignoreMatcher, transformations); + } + +} diff --git a/apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/agent/builder/SWNativeMethodStrategy.java b/apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/agent/builder/SWNativeMethodStrategy.java new file mode 100644 index 0000000000..219ae11f0a --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/agent/builder/SWNativeMethodStrategy.java @@ -0,0 +1,43 @@ +/* + * 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.dynamic.scaffold.inline.MethodNameTransformer; +import org.apache.skywalking.apm.agent.bytebuddy.SWMethodNameTransformer; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; + +public class SWNativeMethodStrategy implements AgentBuilder.Default.NativeMethodStrategy { + + private String nameTrait; + + public SWNativeMethodStrategy(String nameTrait) { + this.nameTrait = nameTrait; + } + + @Override + public MethodNameTransformer resolve() { + return new SWMethodNameTransformer(nameTrait); + } + + @Override + public void apply(Instrumentation instrumentation, ClassFileTransformer classFileTransformer) { + } +} \ No newline at end of file diff --git a/apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/implementation/SWImplementationContextFactory.java b/apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/implementation/SWImplementationContextFactory.java new file mode 100644 index 0000000000..3200da417f --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/main/java/net/bytebuddy/implementation/SWImplementationContextFactory.java @@ -0,0 +1,61 @@ +/* + * 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.implementation; + +import net.bytebuddy.ClassFileVersion; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.scaffold.TypeInitializer; +import net.bytebuddy.implementation.auxiliary.AuxiliaryType; +import net.bytebuddy.utility.RandomString; + +/** + * Support custom suffix name trait, using in cache value field name, field getter/setter delegation, accessor method and so on. + */ +public class SWImplementationContextFactory implements Implementation.Context.Factory { + + private String suffixNameTrait; + + public SWImplementationContextFactory(String suffixNameTrait) { + this.suffixNameTrait = suffixNameTrait; + } + + @Override + public Implementation.Context.ExtractableView make(TypeDescription instrumentedType, + AuxiliaryType.NamingStrategy auxiliaryTypeNamingStrategy, + TypeInitializer typeInitializer, + ClassFileVersion classFileVersion, + ClassFileVersion auxiliaryClassFileVersion) { + return this.make(instrumentedType, auxiliaryTypeNamingStrategy, typeInitializer, classFileVersion, + auxiliaryClassFileVersion, Implementation.Context.FrameGeneration.GENERATE); + } + + @Override + public Implementation.Context.ExtractableView make(TypeDescription instrumentedType, + AuxiliaryType.NamingStrategy auxiliaryTypeNamingStrategy, + TypeInitializer typeInitializer, + ClassFileVersion classFileVersion, + ClassFileVersion auxiliaryClassFileVersion, + Implementation.Context.FrameGeneration frameGeneration) { + // Method cache value field pattern: cachedValue$$$ + // Accessor method name pattern: $accessor$$ + return new Implementation.Context.Default(instrumentedType, classFileVersion, auxiliaryTypeNamingStrategy, + typeInitializer, auxiliaryClassFileVersion, frameGeneration, + suffixNameTrait + RandomString.hashOf(instrumentedType.getName().hashCode())); + } +} diff --git a/apm-sniffer/bytebuddy-patch/src/main/java/org/apache/skywalking/apm/agent/bytebuddy/SWAuxiliaryTypeNamingStrategy.java b/apm-sniffer/bytebuddy-patch/src/main/java/org/apache/skywalking/apm/agent/bytebuddy/SWAuxiliaryTypeNamingStrategy.java new file mode 100644 index 0000000000..b2756aef62 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/main/java/org/apache/skywalking/apm/agent/bytebuddy/SWAuxiliaryTypeNamingStrategy.java @@ -0,0 +1,42 @@ +/* + * 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; + +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.auxiliary.AuxiliaryType; +import net.bytebuddy.utility.RandomString; + +/** + * Generate predicated auxiliary type name for delegate method. + */ +public class SWAuxiliaryTypeNamingStrategy implements AuxiliaryType.NamingStrategy { + private static final String DEFAULT_SUFFIX = "auxiliary$"; + private String suffix; + + public SWAuxiliaryTypeNamingStrategy(String nameTrait) { + this.suffix = nameTrait + DEFAULT_SUFFIX; + } + + @Override + public String name(TypeDescription instrumentedType, AuxiliaryType auxiliaryType) { + // Auxiliary type name pattern: $$auxiliary$ + return instrumentedType.getName() + "$" + suffix + RandomString.hashOf(auxiliaryType.hashCode()); + } + +} diff --git a/apm-sniffer/bytebuddy-patch/src/main/java/org/apache/skywalking/apm/agent/bytebuddy/SWMethodNameTransformer.java b/apm-sniffer/bytebuddy-patch/src/main/java/org/apache/skywalking/apm/agent/bytebuddy/SWMethodNameTransformer.java new file mode 100644 index 0000000000..9d329321ed --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/main/java/org/apache/skywalking/apm/agent/bytebuddy/SWMethodNameTransformer.java @@ -0,0 +1,44 @@ +/* + * 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; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.dynamic.scaffold.inline.MethodNameTransformer; +import net.bytebuddy.utility.RandomString; + +/** + * Generate fixed origin method name with method description hash code + */ +public class SWMethodNameTransformer implements MethodNameTransformer { + + private static final String DEFAULT_PREFIX = "original$"; + + private String prefix; + + public SWMethodNameTransformer(String nameTrait) { + this.prefix = nameTrait + DEFAULT_PREFIX; + } + + @Override + public String transform(MethodDescription methodDescription) { + // Origin method rename pattern: $original$$ + return prefix + methodDescription.getInternalName() + "$" + RandomString.hashOf(methodDescription.toString().hashCode()); + } + +} diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/ConstructorInter.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/ConstructorInter.java new file mode 100644 index 0000000000..2c10f74cd7 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/ConstructorInter.java @@ -0,0 +1,54 @@ +/* + * 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; + +import net.bytebuddy.implementation.bind.annotation.AllArguments; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.implementation.bind.annotation.This; + +import java.util.Arrays; + +/** + * The actual byte-buddy's interceptor to intercept constructor methods. In this class, it provides a bridge between + * byte-buddy and sky-walking plugin. + */ +public class ConstructorInter { + private String interceptorClassName; + private ClassLoader classLoader; + + /** + * @param interceptorClassName class full name. + */ + public ConstructorInter(String interceptorClassName, ClassLoader classLoader) { + this.interceptorClassName = interceptorClassName; + this.classLoader = classLoader; + } + + /** + * Intercept the target constructor. + * + * @param obj target class instance. + * @param allArguments all constructor arguments + */ + @RuntimeType + public void intercept(@This Object obj, @AllArguments Object[] allArguments) { + EnhanceHelper.addInterceptor(interceptorClassName); + Log.info(String.format("ConstructorInterceptorClass: %s, target: %s, args: %s", interceptorClassName, obj, Arrays.asList(allArguments))); + } +} \ No newline at end of file diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/EnhanceHelper.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/EnhanceHelper.java new file mode 100644 index 0000000000..8eb808bb80 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/EnhanceHelper.java @@ -0,0 +1,76 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class EnhanceHelper { + + private static final List INTERCEPTORS = new ArrayList<>(); + private static final List> ERRORS = new ArrayList<>(); + + public static void onError(String message, Throwable error) { + ERRORS.add(new MapEntry<>(message, error)); + } + + public static void addInterceptor(String interceptor) { + INTERCEPTORS.add(interceptor); + } + + public static List getInterceptors() { + return INTERCEPTORS; + } + + public static List> getErrors() { + return ERRORS; + } + + public static void clear() { + ERRORS.clear(); + INTERCEPTORS.clear(); + } + + private static class MapEntry implements Map.Entry { + private final T key; + private P value; + + public MapEntry(T key, P value) { + this.key = key; + this.value = value; + } + + @Override + public T getKey() { + return key; + } + + @Override + public P getValue() { + return value; + } + + @Override + public P setValue(P value) { + this.value = value; + return value; + } + } +} diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/InstMethodsInter.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/InstMethodsInter.java new file mode 100644 index 0000000000..96ee856dc7 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/InstMethodsInter.java @@ -0,0 +1,63 @@ +/* + * 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; + +import net.bytebuddy.implementation.bind.annotation.AllArguments; +import net.bytebuddy.implementation.bind.annotation.Origin; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.implementation.bind.annotation.SuperCall; +import net.bytebuddy.implementation.bind.annotation.This; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.concurrent.Callable; + +public class InstMethodsInter { + private String interceptorClassName; + private ClassLoader classLoader; + + public InstMethodsInter(String interceptorClassName, ClassLoader classLoader) { + this.interceptorClassName = interceptorClassName; + this.classLoader = classLoader; + } + + @RuntimeType + public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @SuperCall Callable zuper, + @Origin Method method) throws Throwable { + EnhanceHelper.addInterceptor(interceptorClassName); + + Object originResult = zuper.call(); + Object finalResult; + if (originResult instanceof String) { + String result = (String) originResult; + result = result.replaceAll("Joe", "John"); + finalResult = result; + } else if (originResult instanceof Integer) { + Integer result = (Integer) originResult; + finalResult = result + 1; + } else { + finalResult = originResult; + } + + Log.info(String.format("InstMethodInterceptorClass: %s, target: %s, args: %s, SuperCall: %s, method: %s, originResult: %s, finalResult: %s", + interceptorClassName, obj, Arrays.asList(allArguments), zuper, method, originResult, finalResult)); + return finalResult; + } + +} diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/Log.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/Log.java new file mode 100644 index 0000000000..f1ee04f3dd --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/Log.java @@ -0,0 +1,59 @@ +/* + * 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; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +public class Log { + private static ByteArrayOutputStream OUTPUT; + private static ByteArrayOutputStream ERR_OUTPUT; + private static PrintStream PRINT; + private static PrintStream ERROR_PRINT; + + public static void info(String msg, Object... args) { + msg = formatLog(msg, args); + PRINT.println(msg); + } + + public static void error(String msg, Object... args) { + msg = formatLog(msg, args); + ERROR_PRINT.println(msg); + } + + private static String formatLog(String msg, Object[] args) { + msg = msg.replaceAll("\\{}", "%s"); + msg = String.format(msg, args); + return msg; + } + + public static void clear() { + OUTPUT = new ByteArrayOutputStream(128); + ERR_OUTPUT = new ByteArrayOutputStream(128); + PRINT = new PrintStream(OUTPUT, true); + ERROR_PRINT = new PrintStream(ERR_OUTPUT, true); + } + + public static void printToConsole() { + PrintStream out = System.out; + PrintStream err = System.err; + out.println(OUTPUT.toString()); + err.println(ERR_OUTPUT.toString()); + } +} diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/SWAsmVisitorWrapper.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/SWAsmVisitorWrapper.java new file mode 100644 index 0000000000..4aeddd8cdb --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/SWAsmVisitorWrapper.java @@ -0,0 +1,120 @@ +/* + * 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; + +import net.bytebuddy.asm.AsmVisitorWrapper; +import net.bytebuddy.description.field.FieldDescription; +import net.bytebuddy.description.field.FieldList; +import net.bytebuddy.description.method.MethodList; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.Implementation; +import net.bytebuddy.jar.asm.ClassVisitor; +import net.bytebuddy.jar.asm.FieldVisitor; +import net.bytebuddy.jar.asm.MethodVisitor; +import net.bytebuddy.jar.asm.Opcodes; +import net.bytebuddy.pool.TypePool; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Remove duplicated fields of instrumentedType + */ +public class SWAsmVisitorWrapper implements AsmVisitorWrapper { + + private String nameTrait = "$"; + + public SWAsmVisitorWrapper() { + } + + @Override + public int mergeWriter(int flags) { + return flags; + } + + @Override + public int mergeReader(int flags) { + return flags; + } + + @Override + public ClassVisitor wrap(TypeDescription instrumentedType, ClassVisitor classVisitor, Implementation.Context implementationContext, + TypePool typePool, FieldList fields, MethodList methods, + int writerFlags, int readerFlags) { + if (classVisitor instanceof RemoveDuplicatedElementsVisitor) { + return classVisitor; + } + return new RemoveDuplicatedElementsVisitor(Opcodes.ASM8, classVisitor); + } + + class RemoveDuplicatedElementsVisitor extends ClassVisitor { + + private Map> fieldCache = new ConcurrentHashMap<>(); + private Map>> methodCache = new ConcurrentHashMap<>(); + private String className; + + public RemoveDuplicatedElementsVisitor(int api, ClassVisitor classVisitor) { + super(api, classVisitor); + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + super.visit(version, access, name, signature, superName, interfaces); + this.className = name; + } + + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { + if (name.contains(nameTrait)) { + Map fieldData = getFieldData(className); + String prevDescriptor = fieldData.get(name); + if (prevDescriptor != null && prevDescriptor.equals(descriptor)) { + // ignore duplicated field of class + return null; + } + fieldData.put(name, descriptor); + } + return super.visitField(access, name, descriptor, signature, value); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + if (name.equals("") || name.contains(nameTrait)) { + Map> methodData = getMethodData(className); + List descriptorList = methodData.computeIfAbsent(name, k -> new ArrayList<>()); + if (descriptorList.contains(descriptor)) { + // ignore duplicated method of class + return null; + } + descriptorList.add(descriptor); + } + return super.visitMethod(access, name, descriptor, signature, exceptions); + } + + private Map getFieldData(String className) { + return fieldCache.computeIfAbsent(className, k -> new ConcurrentHashMap<>()); + } + + private Map> getMethodData(String className) { + return methodCache.computeIfAbsent(className, k -> new ConcurrentHashMap<>()); + } + } +} diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/SWClassFileLocator.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/SWClassFileLocator.java new file mode 100644 index 0000000000..564e4471aa --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/SWClassFileLocator.java @@ -0,0 +1,173 @@ +/* + * 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; + +import net.bytebuddy.dynamic.ClassFileLocator; +import org.apache.skywalking.apm.agent.core.logging.api.ILog; +import org.apache.skywalking.apm.agent.core.logging.api.LogManager; + +import java.io.IOException; +import java.lang.instrument.Instrumentation; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; + +/** + * Resolve auxiliary type from Instrumentation.getAllLoadedClasses(). + * Get class bytecode from separate thread to bypass jdk limitation or bug: https://github.com/raphw/byte-buddy/issues/1434 + */ +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 int DEFAULT_TIMEOUT_SECONDS = 2; + + private final ForInstrumentation.ClassLoadingDelegate classLoadingDelegate; + private final Instrumentation instrumentation; + private final ClassLoader classLoader; + private final BlockingQueue queue = new LinkedBlockingDeque<>(); + private final Thread thread; + private final int timeoutSeconds; + private volatile boolean closed; + + public SWClassFileLocator(Instrumentation instrumentation, ClassLoader classLoader) { + this(instrumentation, classLoader, DEFAULT_TIMEOUT_SECONDS); + } + + public SWClassFileLocator(Instrumentation instrumentation, ClassLoader classLoader, int resolveTimeoutSeconds) { + this.instrumentation = instrumentation; + this.classLoader = classLoader; + this.timeoutSeconds = resolveTimeoutSeconds; + classLoadingDelegate = ForInstrumentation.ClassLoadingDelegate.ForDelegatingClassLoader.of(classLoader); + + // Use thread instead of ExecutorService here, avoiding conflicts with apm-jdk-threadpool-plugin + thread = new Thread(() -> { + while (!closed) { + try { + ResolutionFutureTask task = queue.poll(5, TimeUnit.SECONDS); + if (task != null) { + try { + Resolution resolution = getResolution(task.getClassName()); + task.getFuture().complete(resolution); + } catch (Throwable e) { + task.getFuture().completeExceptionally(e); + } + } + } catch (InterruptedException e) { + // ignore interrupted error + } catch (Throwable e) { + LOGGER.error(e, "Resolve bytecode of class failure"); + } + } + }, "SWClassFileLocator"); + thread.setDaemon(true); + thread.start(); + } + + @Override + public Resolution locate(String name) throws IOException { + if (!match(name)) { + return new Resolution.Illegal(name); + } + if (closed) { + throw new IOException("resolve class failure: closed"); + } + // get class binary representation in a clean thread, avoiding nest calling transformer! + ResolutionFutureTask futureTask = new ResolutionFutureTask(name); + queue.offer(futureTask); + try { + return futureTask.getFuture().get(timeoutSeconds, TimeUnit.SECONDS); + } catch (Exception e) { + throw new IOException("resolve class failure: " + name, e); + } + } + + private boolean match(String name) { + boolean matched = false; + for (String typeNameTrait : TYPE_NAME_TRAITS) { + if (name.contains(typeNameTrait)) { + matched = true; + break; + } + } + return matched; + } + + private Resolution getResolution(String name) throws Exception { + SWExtractionClassFileTransformer classFileTransformer = new SWExtractionClassFileTransformer(name); + try { + instrumentation.addTransformer(classFileTransformer, true); + Class aClass = locateClass(name); + if (aClass == null) { + return new Resolution.Illegal(name); + } + // trigger re-transforming the target class, and receive bytecode in SWExtractionClassFileTransformer + instrumentation.retransformClasses(new Class[]{aClass}); + } finally { + instrumentation.removeTransformer(classFileTransformer); + } + + return classFileTransformer.getBinaryRepresentation() != null ? + new Resolution.Explicit(classFileTransformer.getBinaryRepresentation()) : + new Resolution.Illegal(name); + } + + private Class locateClass(String className) { + // find class in classloader + try { + return classLoadingDelegate.locate(className); + } catch (ClassNotFoundException e) { + } + + // find class in instrumentation + Class[] allLoadedClasses = instrumentation.getAllLoadedClasses(); + for (int i = 0; i < allLoadedClasses.length; i++) { + Class aClass = allLoadedClasses[i]; + if (className.equals(aClass.getName())) { + return aClass; + } + } + return null; + } + + @Override + public void close() { + closed = true; + queue.clear(); + thread.interrupt(); + } + + private class ResolutionFutureTask { + private CompletableFuture future; + private String className; + + public ResolutionFutureTask(String className) { + this.className = className; + future = new CompletableFuture<>(); + } + + public CompletableFuture getFuture() { + return future; + } + + public String getClassName() { + return className; + } + } +} diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/SWExtractionClassFileTransformer.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/SWExtractionClassFileTransformer.java new file mode 100644 index 0000000000..89c1108ce1 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/SWExtractionClassFileTransformer.java @@ -0,0 +1,71 @@ +/* + * 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; + +import java.lang.instrument.ClassFileTransformer; +import java.security.ProtectionDomain; + +public class SWExtractionClassFileTransformer implements ClassFileTransformer { + + /** + * An indicator that an attempted class file transformation did not alter the handed class file. + */ + private static final byte[] DO_NOT_TRANSFORM = null; + + /** + * The name of the type to look up. + */ + private final String typeName; + + /** + * The binary representation of the looked-up class. + */ + private volatile byte[] binaryRepresentation; + + /** + * Creates a class file transformer for the purpose of extraction. + * + * @param typeName The name of the type to look up. + */ + public SWExtractionClassFileTransformer(String typeName) { + this.typeName = typeName; + } + + @Override + public byte[] transform(ClassLoader classLoader, + String internalName, + Class redefinedType, + ProtectionDomain protectionDomain, + byte[] binaryRepresentation) { + if (internalName != null && typeName.equals(internalName.replace('/', '.'))) { + this.binaryRepresentation = binaryRepresentation.clone(); + } + return DO_NOT_TRANSFORM; + } + + /** + * Returns the binary representation of the class file that was looked up. The returned array must never be modified. + * + * @return The binary representation of the class file or {@code null} if no such class file could + * be located. + */ + public byte[] getBinaryRepresentation() { + return binaryRepresentation; + } +} \ No newline at end of file diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/BizFoo.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/BizFoo.java new file mode 100644 index 0000000000..95aaaca1ee --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/BizFoo.java @@ -0,0 +1,44 @@ +/* + * 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 BizFoo { + + private String name; + + public BizFoo() { + this("Tom"); + } + + public BizFoo(String name) { + this.name = name; + } + + public String sayHello(String someone) { + return "Hello to " + someone + " from " + name; + } + + public int sayHello(int uid) { + return uid; + } + + public String greeting(String man) { + return "Greet " + man; + } +} diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/ClassCacheMode.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ProjectDO.java similarity index 65% rename from apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/ClassCacheMode.java rename to apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ProjectDO.java index c805c71826..86e79fed4d 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/ClassCacheMode.java +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ProjectDO.java @@ -16,11 +16,30 @@ * */ -package org.apache.skywalking.apm.agent.core.plugin.bytebuddy; +package org.apache.skywalking.apm.agent.bytebuddy.biz; -/** - * ByteBuddy class cache mode - */ -public enum ClassCacheMode { - FILE, MEMORY +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class ProjectDO { + private int id; + private String name; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ProjectService.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ProjectService.java new file mode 100644 index 0000000000..eeece4afcb --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/biz/ProjectService.java @@ -0,0 +1,39 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.List; + +public class ProjectService { + + private List cache = new ArrayList<>(); + + public void add(ProjectDO projectDO) { + this.cache.add(projectDO); + } + + public ProjectDO getById(int id) { + return cache.stream().filter(projectDO -> projectDO.getId() == id).findAny().orElse(null); + } + + public List list() { + return cache; + } +} 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 new file mode 100644 index 0000000000..c89985ce18 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/AbstractInterceptTest.java @@ -0,0 +1,212 @@ +/* + * 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.ByteBuddy; +import net.bytebuddy.agent.ByteBuddyAgent; +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.agent.builder.SWAgentBuilderDefault; +import net.bytebuddy.agent.builder.SWNativeMethodStrategy; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.SWImplementationContextFactory; +import net.bytebuddy.implementation.SuperMethodCall; +import net.bytebuddy.matcher.ElementMatchers; +import net.bytebuddy.utility.JavaModule; +import org.apache.skywalking.apm.agent.bytebuddy.ConstructorInter; +import org.apache.skywalking.apm.agent.bytebuddy.EnhanceHelper; +import org.apache.skywalking.apm.agent.bytebuddy.InstMethodsInter; +import org.apache.skywalking.apm.agent.bytebuddy.Log; +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.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.util.TraceClassVisitor; + +import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.List; + +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 SAY_HELLO_METHOD = "sayHello"; + 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 nameTraits = Arrays.asList("sw2023", "sw2024"); + protected boolean deleteDuplicatedFields = false; + + @BeforeClass + public static void setUp() { + EnhanceHelper.clear(); + Log.clear(); + } + + @Rule + public TestWatcher watcher = new TestWatcher() { + @Override + protected void failed(Throwable e, Description description) { + Log.error("Test failure: {}.{}(), error: {}", description.getTestClass(), description.getMethodName(), e); + Log.printToConsole(); + } + }; + + protected static void callBizFoo(int round) { + Log.info("-------------"); + Log.info("callBizFoo: " + round); + // load target class + int intResult = new BizFoo().sayHello(BASE_INT_VALUE); + Log.info("result: " + intResult); + + String result = new BizFoo("Smith").sayHello("Joe"); + Log.info(result); + + Assert.assertEquals("Int value is unexpected", BASE_INT_VALUE + round, intResult); + Assert.assertEquals("String value is unexpected", "Hello to John from Smith", result); + } + + protected static void checkMethodInterceptor(String method, int round) { + List interceptors = EnhanceHelper.getInterceptors(); + String interceptorName = METHOD_INTERCEPTOR_CLASS + "$" + method + "$" + round; + Assert.assertTrue("Not found interceptor: " + interceptorName, interceptors.contains(interceptorName)); + Log.info("Found interceptor: " + interceptorName); + } + + protected static void checkConstructorInterceptor(String className, int round) { + List interceptors = EnhanceHelper.getInterceptors(); + String interceptorName = CONSTRUCTOR_INTERCEPTOR_CLASS + "$" + className + "$" + round; + Assert.assertTrue("Not found interceptor: " + interceptorName, interceptors.contains(interceptorName)); + Log.info("Found interceptor: " + interceptorName); + } + + protected static void checkErrors() { + Assert.assertEquals("Error occurred in transform", 0, EnhanceHelper.getErrors().size()); + } + + protected void installMethodInterceptor(String className, String methodName, int round) { + this.installMethodInterceptorWithMethodDelegation(className, methodName, round); + } + + protected void installMethodInterceptorWithMethodDelegation(String className, String methodName, int round) { + String interceptorClassName = METHOD_INTERCEPTOR_CLASS + "$" + methodName + "$" + round; + String nameTrait = getNameTrait(round); + String fieldName = nameTrait + "_delegate$" + methodName + round; + + newAgentBuilder(nameTrait).type(ElementMatchers.named(className)) + .transform((builder, typeDescription, classLoader, module, protectionDomain) -> { + if (deleteDuplicatedFields) { + builder = builder.visit(new SWAsmVisitorWrapper()); + } + return builder + .method(ElementMatchers.nameContainsIgnoreCase(methodName)) + .intercept(MethodDelegation.withDefaultConfiguration() + .to(new InstMethodsInter(interceptorClassName, classLoader), fieldName)) + ; + } + ) + .with(getListener(interceptorClassName)) + .installOn(ByteBuddyAgent.install()); + } + + protected void installConstructorInterceptor(String className, int round) { + installConstructorInterceptorWithMethodDelegation(className, round); + } + + protected void installConstructorInterceptorWithMethodDelegation(String className, int round) { + String interceptorClassName = CONSTRUCTOR_INTERCEPTOR_CLASS + "$" + className + "$" + round; + String nameTrait = getNameTrait(round); + String fieldName = nameTrait + "_delegate$constructor" + round; + + AgentBuilder agentBuilder = newAgentBuilder(nameTrait); + agentBuilder.type(ElementMatchers.named(className)) + .transform((builder, typeDescription, classLoader, module, protectionDomain) -> { + if (deleteDuplicatedFields) { + builder = builder.visit(new SWAsmVisitorWrapper()); + } + return builder + .constructor(ElementMatchers.any()) + .intercept(SuperMethodCall.INSTANCE.andThen( + MethodDelegation.withDefaultConfiguration().to( + new ConstructorInter(interceptorClassName, classLoader), fieldName) + )); + } + ) + .with(getListener(interceptorClassName)) + .installOn(ByteBuddyAgent.install()); + } + + protected String getNameTrait(int round) { + return nameTraits.get(round - 1); + } + + protected AgentBuilder newAgentBuilder(String nameTrait) { + ByteBuddy byteBuddy = new ByteBuddy() + .with(new SWAuxiliaryTypeNamingStrategy(nameTrait)) + .with(new SWImplementationContextFactory(nameTrait)); + + return new SWAgentBuilderDefault(byteBuddy, new SWNativeMethodStrategy(nameTrait)) + .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) + .with(AgentBuilder.DescriptionStrategy.Default.POOL_FIRST) + .with(new SWClassFileLocator(ByteBuddyAgent.install(), getClassLoader())); + } + + protected void installTraceClassTransformer(String msg) { + 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")) { + Log.error(msg + className); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ClassReader cr = new ClassReader(classfileBuffer); + cr.accept(new TraceClassVisitor(new PrintWriter(outputStream)), ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + Log.error(outputStream.toString()); + } + return null; + } + }; + ByteBuddyAgent.install().addTransformer(classFileTransformer, true); + } + + private static ClassLoader getClassLoader() { + return AbstractInterceptTest.class.getClassLoader(); + } + + private static AgentBuilder.Listener.Adapter getListener(String interceptorClassName) { + return new AgentBuilder.Listener.Adapter() { + @Override + public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) { + String msg = String.format("Transform Error: interceptorClassName: %s, typeName: %s, classLoader: %s, module: %s, loaded: %s", interceptorClassName, typeName, classLoader, module, loaded); + EnhanceHelper.onError(msg, throwable); + System.err.println(msg); + throwable.printStackTrace(); + } + }; + } +} diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/AbstractReTransformTest.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/AbstractReTransformTest.java new file mode 100644 index 0000000000..156326e62f --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/AbstractReTransformTest.java @@ -0,0 +1,55 @@ +/* + * 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 org.apache.skywalking.apm.agent.bytebuddy.Log; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.lang.instrument.Instrumentation; +import java.security.ProtectionDomain; + +public class AbstractReTransformTest extends AbstractInterceptTest { + + protected static void reTransform(Instrumentation instrumentation, Class clazz) throws Exception { + Log.info("-------------"); + Log.info("Begin to re-transform class: " + clazz.getName() + " .."); + ClassFileTransformer transformer = new ClassFileTransformer() { + public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, + ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { + Log.info(String.format("transform: className=%s, classBeingRedefined=%s, classloader=%s, protectionDomain=%s, classfileBuffer=%d", + className, classBeingRedefined, loader, protectionDomain.getCodeSource(), classfileBuffer.length)); + return null; + } + }; + try { + instrumentation.addTransformer(transformer, true); + instrumentation.retransformClasses(clazz); + Log.info("ReTransform class " + clazz.getName() + " successful."); + Log.info("-------------"); + } catch (Throwable e) { + Log.info("ReTransform class " + clazz.getName() + " failure: " + e); + Log.info("-------------"); + throw e; + } finally { + instrumentation.removeTransformer(transformer); + } + } + +} diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept1Test.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept1Test.java new file mode 100644 index 0000000000..47d2881916 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept1Test.java @@ -0,0 +1,42 @@ +/* + * 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.junit.Test; + +public class Intercept1Test extends AbstractInterceptTest { + + @Test + public void test1() { + ByteBuddyAgent.install(); + + // install transformer + installMethodInterceptor(BIZ_FOO_CLASS_NAME, SAY_HELLO_METHOD, 1); + installConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + + callBizFoo(1); + + // check interceptors + checkMethodInterceptor(SAY_HELLO_METHOD, 1); + checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + checkErrors(); + } +} + diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept2Test.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept2Test.java new file mode 100644 index 0000000000..4d21ff2891 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept2Test.java @@ -0,0 +1,45 @@ +/* + * 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.junit.Test; + +public class Intercept2Test extends AbstractInterceptTest { + + @Test + public void test2() { + ByteBuddyAgent.install(); + + // install transformer + installMethodInterceptor(BIZ_FOO_CLASS_NAME, SAY_HELLO_METHOD, 1); + installMethodInterceptor(BIZ_FOO_CLASS_NAME, SAY_HELLO_METHOD, 2); + installConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + + // load target class + callBizFoo(2); + + // check interceptors + checkMethodInterceptor(SAY_HELLO_METHOD, 1); + checkMethodInterceptor(SAY_HELLO_METHOD, 2); + checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + checkErrors(); + } +} + diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept3Test.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept3Test.java new file mode 100644 index 0000000000..f9ac67330e --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept3Test.java @@ -0,0 +1,46 @@ +/* + * 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.junit.Test; + +public class Intercept3Test extends AbstractInterceptTest { + + @Test + public void test3() { + ByteBuddyAgent.install(); + + // install transformer + installMethodInterceptor(BIZ_FOO_CLASS_NAME, SAY_HELLO_METHOD, 1); + installConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + installMethodInterceptor(BIZ_FOO_CLASS_NAME, SAY_HELLO_METHOD, 2); + + // load target class + callBizFoo(2); + + // check interceptors + checkMethodInterceptor(SAY_HELLO_METHOD, 1); + checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + checkMethodInterceptor(SAY_HELLO_METHOD, 2); + checkErrors(); + } + +} + diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept4Test.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept4Test.java new file mode 100644 index 0000000000..75db479a8c --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept4Test.java @@ -0,0 +1,44 @@ +/* + * 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.junit.Test; + +public class Intercept4Test extends AbstractInterceptTest { + + @Test + public void test4() { + ByteBuddyAgent.install(); + + // install transformer + installConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + installMethodInterceptor(BIZ_FOO_CLASS_NAME, SAY_HELLO_METHOD, 1); + + // load target class + callBizFoo(1); + + // check interceptors + checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + checkMethodInterceptor(SAY_HELLO_METHOD, 1); + checkErrors(); + } + +} + diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept5Test.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept5Test.java new file mode 100644 index 0000000000..5976f7b327 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept5Test.java @@ -0,0 +1,47 @@ +/* + * 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.junit.Test; + +public class Intercept5Test extends AbstractInterceptTest { + + @Test + public void test5() { + ByteBuddyAgent.install(); + + // install transformer + installConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + installMethodInterceptor(BIZ_FOO_CLASS_NAME, SAY_HELLO_METHOD, 1); + installConstructorInterceptor(BIZ_FOO_CLASS_NAME, 2); + installMethodInterceptor(BIZ_FOO_CLASS_NAME, SAY_HELLO_METHOD, 2); + + // load target class + callBizFoo(2); + + // check interceptors + checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + checkMethodInterceptor(SAY_HELLO_METHOD, 1); + checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 2); + checkMethodInterceptor(SAY_HELLO_METHOD, 2); + checkErrors(); + } +} + diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept6Test.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept6Test.java new file mode 100644 index 0000000000..53817179de --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/Intercept6Test.java @@ -0,0 +1,48 @@ +/* + * 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.junit.Test; + +public class Intercept6Test extends AbstractInterceptTest { + + @Test + public void test6() { + ByteBuddyAgent.install(); + + // install transformer + installMethodInterceptor(BIZ_FOO_CLASS_NAME, SAY_HELLO_METHOD, 1); + installConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + installMethodInterceptor(BIZ_FOO_CLASS_NAME, SAY_HELLO_METHOD, 2); + installConstructorInterceptor(BIZ_FOO_CLASS_NAME, 2); + + // load target class + callBizFoo(2); + + // check interceptors + checkMethodInterceptor(SAY_HELLO_METHOD, 1); + checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + checkMethodInterceptor(SAY_HELLO_METHOD, 2); + checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 2); + checkErrors(); + } + +} + diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/MultipleInterceptorTest.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/MultipleInterceptorTest.java new file mode 100644 index 0000000000..9376ec3794 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/MultipleInterceptorTest.java @@ -0,0 +1,101 @@ +/* + * 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 net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.matcher.ElementMatchers; +import net.bytebuddy.utility.JavaModule; +import org.apache.skywalking.apm.agent.bytebuddy.InstMethodsInter; +import org.apache.skywalking.apm.agent.bytebuddy.SWAsmVisitorWrapper; +import org.apache.skywalking.apm.agent.core.util.FileUtils; +import org.junit.Test; + +import java.io.File; + +public class MultipleInterceptorTest extends AbstractInterceptTest { + + String dumpFolder = "target/class-dump"; + + @Test + public void test1() { + String className = BIZ_FOO_CLASS_NAME; + String methodName = SAY_HELLO_METHOD; + String nameTrait = getNameTrait(1); + + enableClassDump(); + + //newAgentBuilder(nameTrait) + new AgentBuilder.Default() + .type(ElementMatchers.named(className)) + .transform((builder, typeDescription, classLoader, module, protectionDomain) -> { + int round = 1; + String interceptorClassName = METHOD_INTERCEPTOR_CLASS + "$" + methodName + "$" + round; + String fieldName = nameTrait + "_delegate$" + methodName + round; + + if (deleteDuplicatedFields) { + builder = builder.visit(new SWAsmVisitorWrapper()); + } + return builder + .method(ElementMatchers.nameContainsIgnoreCase(methodName)) + .intercept(MethodDelegation.withDefaultConfiguration() + .to(new InstMethodsInter(interceptorClassName, classLoader), fieldName)) + ; + } + ) + .type(ElementMatchers.nameContains(className)) + .transform((builder, typeDescription, classLoader, module, protectionDomain) -> { + int round = 2; + String interceptorClassName = METHOD_INTERCEPTOR_CLASS + "$" + methodName + "$" + round; + String fieldName = nameTrait + "_delegate$" + methodName + round; + + if (deleteDuplicatedFields) { + builder = builder.visit(new SWAsmVisitorWrapper()); + } + return builder + .method(ElementMatchers.nameContainsIgnoreCase(methodName)) + .intercept(MethodDelegation.withDefaultConfiguration() + .to(new InstMethodsInter(interceptorClassName, classLoader), fieldName)) + ; + } + ) + .with(new AgentBuilder.Listener.Adapter() { + @Override + public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) { + System.err.println(String.format("Transform Error: typeName: %s, classLoader: %s, module: %s, loaded: %s", typeName, classLoader, module, loaded)); + throwable.printStackTrace(); + } + }) + .installOn(ByteBuddyAgent.install()); + + callBizFoo(1); + // check interceptors + // checkMethodInterceptor(SAY_HELLO_METHOD, 1); + checkMethodInterceptor(SAY_HELLO_METHOD, 2); + checkErrors(); + } + + private void enableClassDump() { + System.setProperty("net.bytebuddy.dump", dumpFolder); + File dumpDir = new File(dumpFolder); + FileUtils.deleteDirectory(dumpDir); + dumpDir.mkdirs(); + } +} diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/ReTransform1Test.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/ReTransform1Test.java new file mode 100644 index 0000000000..a33d1bec45 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/ReTransform1Test.java @@ -0,0 +1,86 @@ +/* + * 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.bytebuddy.biz.ProjectDO; +import org.apache.skywalking.apm.agent.bytebuddy.biz.ProjectService; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.instrument.Instrumentation; + +public class ReTransform1Test 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); + // project service + installMethodInterceptor(PROJECT_SERVICE_CLASS_NAME, "add", 1); + installMethodInterceptor(PROJECT_SERVICE_CLASS_NAME, "get", 1); + installMethodInterceptor(PROJECT_SERVICE_CLASS_NAME, "list", 1); + installConstructorInterceptor(PROJECT_SERVICE_CLASS_NAME, 1); + + // call target class + callBizFoo(1); + + // check interceptors + checkMethodInterceptor(SAY_HELLO_METHOD, 1); + checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + checkErrors(); + + ProjectService projectService = new ProjectService(); + ProjectDO originProjectDO = ProjectDO.builder() + .name("test") + .id(1) + .build(); + projectService.add(originProjectDO); + 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); + + checkConstructorInterceptor(PROJECT_SERVICE_CLASS_NAME, 1); + checkMethodInterceptor("add", 1); + checkMethodInterceptor("get", 1); + checkMethodInterceptor("list", 1); + checkErrors(); + } + +} + diff --git a/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/ReTransform2Test.java b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/ReTransform2Test.java new file mode 100644 index 0000000000..0dd8749da4 --- /dev/null +++ b/apm-sniffer/bytebuddy-patch/src/test/java/org/apache/skywalking/apm/agent/bytebuddy/cases/ReTransform2Test.java @@ -0,0 +1,72 @@ +/* + * 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.Log; +import org.apache.skywalking.apm.agent.bytebuddy.biz.BizFoo; +import org.junit.Test; + +import java.lang.instrument.Instrumentation; + +public class ReTransform2Test extends AbstractReTransformTest { + + @Test + public void testInterceptConstructor() throws Exception { + Instrumentation instrumentation = ByteBuddyAgent.install(); + + //this.deleteDuplicatedFields = true; + + // install transformer 1 + installMethodInterceptor(BIZ_FOO_CLASS_NAME, SAY_HELLO_METHOD, 1); + installConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + + // install transformer 2 + installConstructorInterceptor(BIZ_FOO_CLASS_NAME, 2); + installMethodInterceptor(BIZ_FOO_CLASS_NAME, SAY_HELLO_METHOD, 2); + installMethodInterceptor(BIZ_FOO_CLASS_NAME, "greeting", 2); + + // call target class + callBizFoo(2); + + // check interceptors + Log.info("-------------"); + Log.info("Check interceptors .."); + checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 2); + checkMethodInterceptor(SAY_HELLO_METHOD, 1); + checkMethodInterceptor(SAY_HELLO_METHOD, 2); + + // do retransform + reTransform(instrumentation, BizFoo.class); + + // test again + callBizFoo(2); + // check interceptors + Log.info("-------------"); + Log.info("Check interceptors .."); + checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 1); + checkConstructorInterceptor(BIZ_FOO_CLASS_NAME, 2); + checkMethodInterceptor(SAY_HELLO_METHOD, 1); + checkMethodInterceptor(SAY_HELLO_METHOD, 2); + checkErrors(); + } + +} + diff --git a/apm-sniffer/config/agent.config b/apm-sniffer/config/agent.config index 14bf708b83..dea03afe8e 100755 --- a/apm-sniffer/config/agent.config +++ b/apm-sniffer/config/agent.config @@ -46,15 +46,6 @@ agent.ignore_suffix=${SW_AGENT_IGNORE_SUFFIX:.jpg,.jpeg,.js,.css,.png,.bmp,.gif, # SkyWalking team may ask for these files in order to resolve compatible problem. agent.is_open_debugging_class=${SW_AGENT_OPEN_DEBUG:false} -# If true, SkyWalking agent will cache all instrumented classes files to memory or disk files (decided by class cache mode), -# allow other javaagent to enhance those classes that enhanced by SkyWalking agent. -agent.is_cache_enhanced_class=${SW_AGENT_CACHE_CLASS:false} - -# The instrumented classes cache mode: MEMORY or FILE -# MEMORY: cache class bytes to memory, if instrumented classes is too many or too large, it may take up more memory -# FILE: cache class bytes in `/class-cache` folder, automatically clean up cached class files when the application exits -agent.class_cache_mode=${SW_AGENT_CLASS_CACHE_MODE:MEMORY} - # Instance name is the identity of an instance, should be unique in the service. If empty, SkyWalking agent will # generate an 32-bit uuid. BY Default, SkyWalking uses UUID@hostname as the instance name. Max length is 50(UTF-8 char) agent.instance_name=${SW_AGENT_INSTANCE_NAME:} diff --git a/apm-sniffer/pom.xml b/apm-sniffer/pom.xml index eb0c88aab7..f1057cfc17 100644 --- a/apm-sniffer/pom.xml +++ b/apm-sniffer/pom.xml @@ -37,6 +37,7 @@ bootstrap-plugins optional-plugins optional-reporter-plugins + bytebuddy-patch diff --git a/docs/en/setup/service-agent/java-agent/configurations.md b/docs/en/setup/service-agent/java-agent/configurations.md index b55dc4450f..3b3be04338 100644 --- a/docs/en/setup/service-agent/java-agent/configurations.md +++ b/docs/en/setup/service-agent/java-agent/configurations.md @@ -13,8 +13,6 @@ This is the properties list supported in `agent/config/agent.config`. | `agent.span_limit_per_segment` | The max number of spans in a single segment. Through this config item, SkyWalking keep your application memory cost estimated. | SW_AGENT_SPAN_LIMIT | 300 | | `agent.ignore_suffix` | If the operation name of the first span is included in this set, this segment should be ignored. | SW_AGENT_IGNORE_SUFFIX | Not set | | `agent.is_open_debugging_class` | If true, skywalking agent will save all instrumented classes files in `/debugging` folder. SkyWalking team may ask for these files in order to resolve compatible problem. | SW_AGENT_OPEN_DEBUG | Not set | -| `agent.is_cache_enhanced_class` | If true, SkyWalking agent will cache all instrumented classes files to memory or disk files (decided by class cache mode), allow another java agent to enhance those classes that enhanced by SkyWalking agent. To use some Java diagnostic tools (such as BTrace, Arthas) to diagnose applications or add a custom java agent to enhance classes, you need to enable this feature. | SW_AGENT_CACHE_CLASS | `false` | -| `agent.class_cache_mode` | The instrumented classes cache mode: `MEMORY` or `FILE`. `MEMORY`: cache class bytes to memory, if instrumented classes is too many or too large, it may take up more memory. `FILE`: cache class bytes in `/class-cache` folder, automatically clean up cached class files when the application exits. | SW_AGENT_CLASS_CACHE_MODE | `MEMORY` | | `agent.instance_name` | Instance name is the identity of an instance, should be unique in the service. If empty, SkyWalking agent will generate an 32-bit uuid. Default, use `UUID`@`hostname` as the instance name. Max length is 50(UTF-8 char) | SW_AGENT_INSTANCE_NAME | `""` | | `agent.instance_properties_json={"key":"value"}` | Add service instance custom properties in json format. | SW_INSTANCE_PROPERTIES_JSON | Not set | | `agent.cause_exception_depth` | How depth the agent goes, when log all cause exceptions. | SW_AGENT_CAUSE_EXCEPTION_DEPTH | `5` | diff --git a/pom.xml b/pom.xml index baebc37938..d5badbbbcf 100755 --- a/pom.xml +++ b/pom.xml @@ -86,7 +86,7 @@ 1.18.20 - 1.12.19 + 1.14.4 1.50.0 4.1.86.Final 2.8.9 @@ -238,11 +238,15 @@ test + + net.bytebuddy + byte-buddy + ${bytebuddy.version} + net.bytebuddy byte-buddy-agent ${bytebuddy.version} - test org.objenesis @@ -419,7 +423,8 @@ org/apache/skywalking/oap/server/exporter/grpc/*.java, org/apache/skywalking/oap/server/configuration/service/*.java, **/jmh_generated/*_jmhType*.java, - **/jmh_generated/*_jmhTest.java + **/jmh_generated/*_jmhTest.java, + net/bytebuddy/** import.control=${maven.multiModuleProjectDirectory}/apm-checkstyle/importControl.xml diff --git a/test/plugin/scenarios/retransform-class-scenario/bin/startup.sh b/test/plugin/scenarios/retransform-class-scenario/bin/startup.sh index b94792e604..423bc894d4 100644 --- a/test/plugin/scenarios/retransform-class-scenario/bin/startup.sh +++ b/test/plugin/scenarios/retransform-class-scenario/bin/startup.sh @@ -18,7 +18,4 @@ home="$(cd "$(dirname $0)"; pwd)" -# enable class cache feature -export agent_opts="$agent_opts -Dskywalking.agent.is_cache_enhanced_class=true -Dskywalking.agent.class_cache_mode=MEMORY" - java -jar ${agent_opts} ${home}/../libs/retransform-class-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/retransform-class-scenario/configuration.yml b/test/plugin/scenarios/retransform-class-scenario/configuration.yml index ee921e3eb1..47884e13c2 100644 --- a/test/plugin/scenarios/retransform-class-scenario/configuration.yml +++ b/test/plugin/scenarios/retransform-class-scenario/configuration.yml @@ -20,3 +20,4 @@ healthCheck: http://localhost:8080/case/healthCheck startScript: ./bin/startup.sh environment: dependencies: +withPlugins: apm-spring-*.jar, apm-jdk-*.jar diff --git a/test/plugin/scenarios/retransform-class-tomcat-scenario/configuration.yml b/test/plugin/scenarios/retransform-class-tomcat-scenario/configuration.yml index ff2ed8bcd6..51a59ec13e 100644 --- a/test/plugin/scenarios/retransform-class-tomcat-scenario/configuration.yml +++ b/test/plugin/scenarios/retransform-class-tomcat-scenario/configuration.yml @@ -17,7 +17,5 @@ type: tomcat entryService: http://localhost:8080/retransform-class-tomcat-scenario/case/retransform-class healthCheck: http://localhost:8080/retransform-class-tomcat-scenario/case/healthCheck -environment: - - CATALINA_OPTS="-Dskywalking.agent.is_cache_enhanced_class=true -Dskywalking.agent.class_cache_mode=FILE" dependencies: -withPlugins: apm-spring-annotation-plugin-*.jar +withPlugins: apm-spring-*.jar, apm-jdk-*.jar