From 240cacb44788cf82fbd96ebdb762680c8c6b3832 Mon Sep 17 00:00:00 2001 From: ascrutae Date: Mon, 20 Mar 2017 10:41:19 +0800 Subject: [PATCH] add plugin test cases --- skywalking-sniffer/skywalking-api/pom.xml | 6 + .../AbstractClassEnhancePluginDefineTest.java | 121 ++++++++++++++++++ ...ckPluginInstanceMethodInstrumentation.java | 69 ++++++++++ .../api/plugin/MockPluginInterceptor.java | 44 +++++++ ...MockPluginStaticMethodInstrumentation.java | 36 ++++++ .../api/plugin/MockTargetObject.java | 22 ++++ .../plugin/utility/ClassFileExtraction.java | 87 +++++++++++++ 7 files changed, 385 insertions(+) create mode 100644 skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/AbstractClassEnhancePluginDefineTest.java create mode 100644 skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockPluginInstanceMethodInstrumentation.java create mode 100644 skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockPluginInterceptor.java create mode 100644 skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockPluginStaticMethodInstrumentation.java create mode 100644 skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockTargetObject.java create mode 100644 skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/utility/ClassFileExtraction.java diff --git a/skywalking-sniffer/skywalking-api/pom.xml b/skywalking-sniffer/skywalking-api/pom.xml index 9b94ddb5c..9e3928b14 100644 --- a/skywalking-sniffer/skywalking-api/pom.xml +++ b/skywalking-sniffer/skywalking-api/pom.xml @@ -40,6 +40,12 @@ byte-buddy 1.5.7 + + net.bytebuddy + byte-buddy-agent + 1.5.7 + test + com.lmax disruptor diff --git a/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/AbstractClassEnhancePluginDefineTest.java b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/AbstractClassEnhancePluginDefineTest.java new file mode 100644 index 000000000..0a878dce9 --- /dev/null +++ b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/AbstractClassEnhancePluginDefineTest.java @@ -0,0 +1,121 @@ +package com.a.eye.skywalking.api.plugin; + +import com.a.eye.skywalking.api.plugin.utility.ClassFileExtraction; + +import net.bytebuddy.agent.ByteBuddyAgent; +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.dynamic.loading.ByteArrayClassLoader; +import net.bytebuddy.dynamic.loading.PackageDefinitionStrategy; +import net.bytebuddy.matcher.ElementMatchers; + +import org.hamcrest.CoreMatchers; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import java.lang.instrument.ClassFileTransformer; + +import static net.bytebuddy.matcher.ElementMatchers.is; +import static net.bytebuddy.matcher.ElementMatchers.none; +import static org.hamcrest.MatcherAssert.assertThat; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({}) +public class AbstractClassEnhancePluginDefineTest { + static final String WEAVE_CLASS = "com.a.eye.skywalking.api.plugin.MockTargetObject"; + static final String INTERCEPTOR_CLASS = "com.a.eye.skywalking.api.plugin.MockPluginInterceptor"; + static final String WEAVE_INSTANCE_METHOD_NAME = "targetInstanceMethod"; + static final String WEAVE_INSTANCE_WITH_EXCEPTION_METHOD_NAME = "targetInstanceMethodWithException"; + static final String WEAVE_STATIC_METHOD_NAME = "targetStaticMethod"; + private ClassLoader classLoader; + + @Before + public void setUp() throws Exception { + classLoader = new ByteArrayClassLoader.ChildFirst(getClass().getClassLoader(), + ClassFileExtraction.of(MockTargetObject.class), + null, + ByteArrayClassLoader.PersistenceHandler.MANIFEST, + PackageDefinitionStrategy.NoOp.INSTANCE); + } + + @Test + public void weaveInstanceMethod() throws Exception { + ByteBuddyAgent.install(); + ClassFileTransformer classFileTransformer = new AgentBuilder.Default() + .with(AgentBuilder.PoolStrategy.Default.FAST) + .ignore(none()) + .type(ElementMatchers.is(MockTargetObject.class), ElementMatchers.is(classLoader)).transform(new MockTargetObjectTransformer()) + .installOnByteBuddyAgent(); + + try { + Class type = classLoader.loadClass(MockTargetObject.class.getName()); + assertThat(type.getDeclaredMethod(WEAVE_INSTANCE_METHOD_NAME).invoke(type.getDeclaredConstructor(String.class).newInstance("a")) + , CoreMatchers.is(WEAVE_INSTANCE_METHOD_NAME + "a")); + } finally { + ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); + } + } + + + @Test(expected = RuntimeException.class) + public void weaveInstanceMethodWITEXCEPTION() throws Exception { + ByteBuddyAgent.install(); + ClassFileTransformer classFileTransformer = new AgentBuilder.Default() + .with(AgentBuilder.PoolStrategy.Default.FAST) + .ignore(none()) + .type(ElementMatchers.is(MockTargetObject.class), ElementMatchers.is(classLoader)).transform(new MockTargetObjectTransformer()) + .installOnByteBuddyAgent(); + + try { + Class type = classLoader.loadClass(MockTargetObject.class.getName()); + type.getDeclaredMethod(WEAVE_INSTANCE_WITH_EXCEPTION_METHOD_NAME).invoke(type.getDeclaredConstructor(String.class).newInstance("a")); + } finally { + ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); + } + } + + @Test + public void weaveStaticMethod() throws Exception { + ByteBuddyAgent.install(); + ClassFileTransformer classFileTransformer = new AgentBuilder.Default() + .with(AgentBuilder.PoolStrategy.Default.FAST) + .ignore(none()) + .type(ElementMatchers.is(MockTargetObject.class), ElementMatchers.is(classLoader)).transform(new MockTargetObjectTransformer()) + .installOnByteBuddyAgent(); + + try { + Class type = classLoader.loadClass(MockTargetObject.class.getName()); + assertThat(type.getDeclaredMethod(WEAVE_STATIC_METHOD_NAME).invoke(type), CoreMatchers.is(WEAVE_STATIC_METHOD_NAME + "_STATIC")); + } finally { + ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); + } + } + + public static class MockTargetObjectTransformer implements AgentBuilder.Transformer { + + @Override + public DynamicType.Builder transform(DynamicType.Builder builder, TypeDescription typeDescription, ClassLoader classLoader) { + try { + DynamicType.Builder newBuilder = transformInstanceMethod(builder); + return transformStaticMethod(newBuilder); + } catch (Exception exception) { + throw new AssertionError(exception); + } + } + + private DynamicType.Builder transformStaticMethod(DynamicType.Builder newBuilder) { + MockPluginStaticMethodInstrumentation staticMethodInstrumentation = new MockPluginStaticMethodInstrumentation(); + return staticMethodInstrumentation.define(WEAVE_CLASS, newBuilder); + } + + private DynamicType.Builder transformInstanceMethod(DynamicType.Builder builder) { + MockPluginInstanceMethodInstrumentation instrumentation = new MockPluginInstanceMethodInstrumentation(); + return instrumentation.define(WEAVE_CLASS, builder); + } + } + +} \ No newline at end of file diff --git a/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockPluginInstanceMethodInstrumentation.java b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockPluginInstanceMethodInstrumentation.java new file mode 100644 index 000000000..07eb4791b --- /dev/null +++ b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockPluginInstanceMethodInstrumentation.java @@ -0,0 +1,69 @@ +package com.a.eye.skywalking.api.plugin; + +import com.a.eye.skywalking.api.plugin.bytebuddy.AllObjectDefaultMethodsMatch; +import com.a.eye.skywalking.api.plugin.interceptor.ConstructorInterceptPoint; +import com.a.eye.skywalking.api.plugin.interceptor.InstanceMethodsInterceptPoint; +import com.a.eye.skywalking.api.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; + +import static com.a.eye.skywalking.api.plugin.AbstractClassEnhancePluginDefineTest.INTERCEPTOR_CLASS; +import static com.a.eye.skywalking.api.plugin.AbstractClassEnhancePluginDefineTest.WEAVE_CLASS; +import static com.a.eye.skywalking.api.plugin.AbstractClassEnhancePluginDefineTest.WEAVE_INSTANCE_METHOD_NAME; +import static com.a.eye.skywalking.api.plugin.AbstractClassEnhancePluginDefineTest.WEAVE_INSTANCE_WITH_EXCEPTION_METHOD_NAME; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +public class MockPluginInstanceMethodInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + @Override + protected String enhanceClassName() { + return WEAVE_CLASS; + } + + @Override + protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArgument(0, String.class); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } + + @Override + protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(WEAVE_INSTANCE_METHOD_NAME).and(not(AllObjectDefaultMethodsMatch.INSTANCE)); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_CLASS; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(WEAVE_INSTANCE_WITH_EXCEPTION_METHOD_NAME); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } +} diff --git a/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockPluginInterceptor.java b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockPluginInterceptor.java new file mode 100644 index 000000000..480ca8222 --- /dev/null +++ b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockPluginInterceptor.java @@ -0,0 +1,44 @@ +package com.a.eye.skywalking.api.plugin; + +import com.a.eye.skywalking.api.plugin.interceptor.EnhancedClassInstanceContext; +import com.a.eye.skywalking.api.plugin.interceptor.enhance.ConstructorInvokeContext; +import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodInvokeContext; +import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import com.a.eye.skywalking.api.plugin.interceptor.enhance.MethodInterceptResult; +import com.a.eye.skywalking.api.plugin.interceptor.enhance.MethodInvokeContext; +import com.a.eye.skywalking.api.plugin.interceptor.enhance.StaticMethodInvokeContext; +import com.a.eye.skywalking.api.plugin.interceptor.enhance.StaticMethodsAroundInterceptor; + +public class MockPluginInterceptor implements InstanceMethodsAroundInterceptor, StaticMethodsAroundInterceptor, InstanceConstructorInterceptor { + @Override + public void beforeMethod(StaticMethodInvokeContext interceptorContext, MethodInterceptResult result) { + } + + @Override + public Object afterMethod(StaticMethodInvokeContext interceptorContext, Object ret) { + return ret + "_STATIC"; + } + + @Override + public void handleMethodException(Throwable t, MethodInvokeContext interceptorContext) { + } + + @Override + public void beforeMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext, MethodInterceptResult result) { + } + + @Override + public Object afterMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext, Object ret) { + return ret + String.valueOf(context.get("VALUE")); + } + + @Override + public void handleMethodException(Throwable t, EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext) { + } + + @Override + public void onConstruct(EnhancedClassInstanceContext context, ConstructorInvokeContext interceptorContext) { + context.set("VALUE" , interceptorContext.allArguments()[0]); + } +} diff --git a/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockPluginStaticMethodInstrumentation.java b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockPluginStaticMethodInstrumentation.java new file mode 100644 index 000000000..751c6f94b --- /dev/null +++ b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockPluginStaticMethodInstrumentation.java @@ -0,0 +1,36 @@ +package com.a.eye.skywalking.api.plugin; + +import com.a.eye.skywalking.api.plugin.interceptor.StaticMethodsInterceptPoint; +import com.a.eye.skywalking.api.plugin.interceptor.enhance.ClassStaticMethodsEnhancePluginDefine; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; + +import static com.a.eye.skywalking.api.plugin.AbstractClassEnhancePluginDefineTest.INTERCEPTOR_CLASS; +import static com.a.eye.skywalking.api.plugin.AbstractClassEnhancePluginDefineTest.WEAVE_CLASS; +import static com.a.eye.skywalking.api.plugin.AbstractClassEnhancePluginDefineTest.WEAVE_STATIC_METHOD_NAME; +import static net.bytebuddy.matcher.ElementMatchers.named; + +public class MockPluginStaticMethodInstrumentation extends ClassStaticMethodsEnhancePluginDefine { + @Override + protected String enhanceClassName() { + return WEAVE_CLASS; + } + + @Override + protected StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() { + return new StaticMethodsInterceptPoint[]{ + new StaticMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(WEAVE_STATIC_METHOD_NAME); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_CLASS; + } + } + }; + } +} diff --git a/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockTargetObject.java b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockTargetObject.java new file mode 100644 index 000000000..6c9b548a3 --- /dev/null +++ b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/MockTargetObject.java @@ -0,0 +1,22 @@ +package com.a.eye.skywalking.api.plugin; + +public class MockTargetObject { + + private String value; + + public MockTargetObject(String value) { + this.value = value; + } + + public String targetInstanceMethod() { + return "targetInstanceMethod"; + } + + public String targetInstanceMethodWithException() { + throw new RuntimeException("test exception"); + } + + public static String targetStaticMethod() { + return "targetStaticMethod"; + } +} diff --git a/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/utility/ClassFileExtraction.java b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/utility/ClassFileExtraction.java new file mode 100644 index 000000000..62a899a4c --- /dev/null +++ b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/utility/ClassFileExtraction.java @@ -0,0 +1,87 @@ +package com.a.eye.skywalking.api.plugin.utility; + +import net.bytebuddy.ClassFileVersion; +import net.bytebuddy.asm.AsmVisitorWrapper; +import net.bytebuddy.description.field.FieldDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.Implementation; +import net.bytebuddy.implementation.auxiliary.AuxiliaryType; +import net.bytebuddy.implementation.bytecode.StackManipulation; +import net.bytebuddy.jar.asm.ClassReader; +import net.bytebuddy.jar.asm.ClassWriter; +import net.bytebuddy.pool.TypePool; + +import org.junit.Test; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class ClassFileExtraction { + + private static final int CA = 0xCA, FE = 0xFE, BA = 0xBA, BE = 0xBE; + + public static Map of(Class... type) throws IOException { + Map result = new HashMap(); + for (Class aType : type) { + result.put(aType.getName(), extract(aType)); + } + return result; + } + + public static byte[] extract(Class type, AsmVisitorWrapper asmVisitorWrapper) throws IOException { + ClassReader classReader = new ClassReader(type.getName()); + ClassWriter classWriter = new ClassWriter(classReader, AsmVisitorWrapper.NO_FLAGS); + classReader.accept(asmVisitorWrapper.wrap(new TypeDescription.ForLoadedType(type), + classWriter, + new IllegalContext(), + TypePool.Empty.INSTANCE, + AsmVisitorWrapper.NO_FLAGS, + AsmVisitorWrapper.NO_FLAGS), AsmVisitorWrapper.NO_FLAGS); + return classWriter.toByteArray(); + } + + public static byte[] extract(Class type) throws IOException { + return extract(type, new AsmVisitorWrapper.Compound()); + } + + @Test + public void testClassFileExtraction() throws Exception { + byte[] binaryFoo = extract(Foo.class); + assertThat(binaryFoo.length > 4, is(true)); + assertThat(binaryFoo[0], is(new Integer(CA).byteValue())); + assertThat(binaryFoo[1], is(new Integer(FE).byteValue())); + assertThat(binaryFoo[2], is(new Integer(BA).byteValue())); + assertThat(binaryFoo[3], is(new Integer(BE).byteValue())); + } + + private static class Foo { + /* empty */ + } + + private static class IllegalContext implements Implementation.Context { + + @Override + public TypeDescription register(AuxiliaryType auxiliaryType) { + throw new AssertionError("Did not expect method call"); + } + + @Override + public FieldDescription.InDefinedShape cache(StackManipulation fieldValue, TypeDescription fieldType) { + throw new AssertionError("Did not expect method call"); + } + + @Override + public TypeDescription getInstrumentedType() { + throw new AssertionError("Did not expect method call"); + } + + @Override + public ClassFileVersion getClassFileVersion() { + throw new AssertionError("Did not expect method call"); + } + } +}