add bytekit module

This commit is contained in:
hengyunabc
2020-04-12 03:42:51 +08:00
parent 43c8c88afa
commit e330bdc2f6
107 changed files with 9530 additions and 0 deletions
@@ -0,0 +1,18 @@
package com.taobao.arthas.bytekit.asm.inst;
public class InstDemo {
public int returnInt(int i) {
System.out.println(new Object[] { i });
return 9998;
}
public static void onEnter(Object[] args) {
System.out.println(args);
}
public static int returnIntStatic(int i) {
return 9998;
}
}
@@ -0,0 +1,102 @@
package com.taobao.arthas.bytekit.asm.inst;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AnnotationNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.FieldNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
import com.taobao.arthas.bytekit.utils.AsmUtils;
import com.taobao.arthas.bytekit.utils.Decompiler;
import com.taobao.arthas.bytekit.utils.VerifyUtils;
public class InstDemoTest {
@Test
public void test() throws Exception {
ClassNode apmClassNode = AsmUtils.loadClass(InstDemo_APM.class);
ClassNode originClassNode = AsmUtils.loadClass(InstDemo.class);
ClassNode targetClassNode = AsmUtils.copy(originClassNode);
byte[] renameClass = AsmUtils.renameClass(AsmUtils.toBytes(apmClassNode), Type.getObjectType(originClassNode.name).getClassName());
apmClassNode = AsmUtils.toClassNode(renameClass);
for(FieldNode fieldNode : apmClassNode.fields) {
if( fieldNode.visibleAnnotations != null) {
for( AnnotationNode annotationNode : fieldNode.visibleAnnotations) {
System.err.println(annotationNode.desc);
System.err.println(annotationNode.values);
if(Type.getType(NewField.class).equals(Type.getType(annotationNode.desc))) {
AsmUtils.addField(targetClassNode, fieldNode);
}
}
}
}
for (MethodNode methodNode : apmClassNode.methods) {
methodNode = AsmUtils.removeLineNumbers(methodNode);
if (methodNode.name.startsWith("__origin_")) {
continue;
} else {
MethodNode findMethod = AsmUtils.findMethod(originClassNode.methods, methodNode);
if (findMethod != null) {
// 先要替换 invokeOrigin ,要判断
// 从 apm 里查找 __origin_ 开头的函数,忽略
// 查找 非 __origin_ 开头的函数,在原来的类里查找,如果有同样签名的函数
// 则从函数里查找 是否有 __origin_ 的函数调用。如果有的话,则从原有的类里查找到 method,再inline掉。
List<MethodInsnNode> originMethodInsnNodes = AsmUtils.findMethodInsnNodeWithPrefix(methodNode,
"__origin_");
for (MethodInsnNode methodInsnNode : originMethodInsnNodes) {
String toInlineMethodName = methodInsnNode.name.substring("__origin_".length());
MethodNode originMethodNode = AsmUtils.findMethod(originClassNode.methods, toInlineMethodName,
findMethod.desc);
MethodNode tmpMethodNode = AsmUtils.copy(originMethodNode);
tmpMethodNode.name = methodInsnNode.name;
MethodProcessor methodProcessor = new MethodProcessor(apmClassNode.name, methodNode);
methodProcessor.inline(originClassNode.name, tmpMethodNode);
AsmUtils.replaceMethod(targetClassNode, methodProcessor.getMethodNode());
}
} else {
// 没找到的函数,则加进去
AsmUtils.addMethod(targetClassNode, methodNode);
}
}
}
byte[] resutlBytes = AsmUtils.toBytes(targetClassNode);
System.err.println(Decompiler.decompile(resutlBytes));
System.err.println(AsmUtils.toASMCode(resutlBytes));
FileUtils.writeByteArrayToFile(new File("/tmp/ttt/InstDemo.class"), resutlBytes);
VerifyUtils.asmVerify(resutlBytes);
VerifyUtils.instanceVerity(resutlBytes);
}
}
@@ -0,0 +1,29 @@
package com.taobao.arthas.bytekit.asm.inst;
@Instrument
public class InstDemo_APM {
@NewField
private String newField;
public int newMethod(String s) {
return s.length() + 998;
}
// 这种方式来写怎么样?有点丑,但是不需要写那些转换的代码。 在插件的编绎出结果后,可以检查下 名字,static,参数等是否匹配的。
// 这种处理有点丑,但inline应该没问题
public int __origin_returnInt(int i) {
return 0;
}
public int returnInt(int i) {
int re = __origin_returnInt(i);
return 9998 + re;
}
public static int returnIntStatic(int i) {
return 9998;
}
}
@@ -0,0 +1,77 @@
package com.taobao.arthas.bytekit.asm.inst;
import java.util.Date;
/**
* @author hengyunabc 2019-03-13
*
*/
public class InvokeOriginDemo {
public void returnVoid() {
}
public Void returnVoidObject() {
int i = 0;
try {
int parseInt = Integer.parseInt("1000");
i += parseInt;
} catch (Exception e) {
System.err.println(i + " " + e);
}
return null;
}
public int returnInt(int i) {
return 9998;
}
public int returnIntToObject(int i) {
return 9998;
}
public int returnIntToInteger(int i) {
return 9998;
}
public static int returnIntStatic(int i) {
return 9998;
}
public long returnLong() {
return 9998L;
}
public long returnLongToObject() {
return 9998L;
}
public String[] returnStrArray() {
String[] result = new String[] {"abc", "xyz" , "ufo"};
return result;
}
public String[] returnStrArrayWithArgs(int i, String s, long l) {
String[] result = new String[] {"abc" + i, "xyz" + s , "ufo" + l};
return result;
}
public String returnStr() {
return new Date().toString();
}
public Object returnObject() {
return InvokeOriginDemo.class;
}
public int recursive(int i) {
if (i == 1) {
return 1;
}
return i + recursive(i - 1);
}
}
@@ -0,0 +1,85 @@
package com.taobao.arthas.bytekit.asm.inst;
/**
*
* @author hengyunabc 2019-03-18
*
*/
public class InvokeOriginDemo_APM {
public void returnVoid() {
Object o = InstrumentApi.invokeOrigin();
System.out.println(o);
}
public Void returnVoidObject() {
Void v = InstrumentApi.invokeOrigin();
System.out.println(v);
return v;
}
public int returnInt(int i) {
System.out.println("before");
int value = InstrumentApi.invokeOrigin();
System.out.println("after");
return value + 123;
}
public int returnIntToObject(int i) {
Object value = InstrumentApi.invokeOrigin();
return 9998 + (Integer) value;
}
public int returnIntToInteger(int i) {
Integer ixx = InstrumentApi.invokeOrigin();
return ixx + 9998;
}
public static int returnIntStatic(int i) {
int result = InstrumentApi.invokeOrigin();
return 9998 + result;
}
public long returnLong() {
long result = InstrumentApi.invokeOrigin();
return 9998L + result;
}
public long returnLongToObject() {
Long lll = InstrumentApi.invokeOrigin();
return 9998L + lll;
}
public String[] returnStrArray() {
String[] result = InstrumentApi.invokeOrigin();
System.err.println(result);
return result;
}
public String[] returnStrArrayWithArgs(int i, String s, long l) {
System.out.println(i);
String[] result = InstrumentApi.invokeOrigin();
result[0] = "fff";
return result;
}
public String returnStr() {
System.err.println("ssss");
Object result = InstrumentApi.invokeOrigin();
return "hello" + result;
}
public Object returnObject() {
InstrumentApi.invokeOrigin();
return InvokeOriginDemo.class;
}
public int recursive(int i) {
int result = InstrumentApi.invokeOrigin();
System.err.println(result);
return result;
}
}
@@ -0,0 +1,178 @@
package com.taobao.arthas.bytekit.asm.inst;
import java.io.IOException;
import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
import com.taobao.arthas.bytekit.asm.inst.impl.InstrumentImpl;
import com.taobao.arthas.bytekit.utils.AsmUtils;
import com.taobao.arthas.bytekit.utils.Decompiler;
import com.taobao.arthas.bytekit.utils.VerifyUtils;
/**
*
* @author hengyunabc 2019-03-18
*
*/
public class InvokeOriginTest {
ClassNode apmClassNode;
ClassNode originClassNode;
ClassNode targetClassNode;
@Rule
public TestName testName = new TestName();
@BeforeClass
public static void beforeClass() throws IOException {
}
@Before
public void before() throws IOException {
apmClassNode = AsmUtils.loadClass(InvokeOriginDemo_APM.class);
originClassNode = AsmUtils.loadClass(InvokeOriginDemo.class);
byte[] renameClass = AsmUtils.renameClass(AsmUtils.toBytes(apmClassNode),
Type.getObjectType(originClassNode.name).getClassName());
apmClassNode = AsmUtils.toClassNode(renameClass);
targetClassNode = AsmUtils.copy(originClassNode);
}
private Object replace(String methodName) throws Exception {
System.err.println(methodName);
for (MethodNode methodNode : apmClassNode.methods) {
if (methodNode.name.equals(methodName)) {
methodNode = AsmUtils.removeLineNumbers(methodNode);
// 从原来的类里查找对应的函数
MethodNode findMethod = AsmUtils.findMethod(originClassNode.methods, methodNode);
if (findMethod != null) {
MethodNode methodNode2 = InstrumentImpl.replaceInvokeOrigin(originClassNode.name, findMethod,
methodNode);
System.err.println(Decompiler.toString(methodNode2));
AsmUtils.replaceMethod(targetClassNode, methodNode2);
} else {
}
}
}
byte[] resutlBytes = AsmUtils.toBytes(targetClassNode);
System.err.println("=================");
System.err.println(Decompiler.decompile(resutlBytes));
// System.err.println(AsmUtils.toASMCode(resutlBytes));
VerifyUtils.asmVerify(resutlBytes);
return VerifyUtils.instanceVerity(resutlBytes);
}
@Test
public void test_returnVoid() throws Exception {
String methodName = testName.getMethodName().substring("test_".length());
Object object = replace(methodName);
Assertions.assertThat(VerifyUtils.invoke(object, methodName)).isEqualTo(null);
}
@Test
public void test_returnVoidObject() throws Exception {
String methodName = testName.getMethodName().substring("test_".length());
Object object = replace(methodName);
Assertions.assertThat(VerifyUtils.invoke(object, methodName)).isEqualTo(null);
}
@Test
public void test_returnInt() throws Exception {
String methodName = testName.getMethodName().substring("test_".length());
Object object = replace(methodName);
Assertions.assertThat(VerifyUtils.invoke(object, methodName, 123)).isEqualTo(9998 + 123);
}
@Test
public void test_returnIntToObject() throws Exception {
String methodName = testName.getMethodName().substring("test_".length());
Object object = replace(methodName);
Assertions.assertThat(VerifyUtils.invoke(object, methodName, 123)).isEqualTo(9998 + 9998);
}
@Test
public void test_returnIntToInteger() throws Exception {
String methodName = testName.getMethodName().substring("test_".length());
Object object = replace(methodName);
Assertions.assertThat(VerifyUtils.invoke(object, methodName, 123)).isEqualTo(9998 + 9998);
}
@Test
public void test_returnIntStatic() throws Exception {
String methodName = testName.getMethodName().substring("test_".length());
Object object = replace(methodName);
Assertions.assertThat(VerifyUtils.invoke(object, methodName, 123)).isEqualTo(9998 + 9998);
}
@Test
public void test_returnLong() throws Exception {
String methodName = testName.getMethodName().substring("test_".length());
Object object = replace(methodName);
Assertions.assertThat(VerifyUtils.invoke(object, methodName)).isEqualTo(9998L + 9998);
}
@Test
public void test_returnLongToObject() throws Exception {
String methodName = testName.getMethodName().substring("test_".length());
Object object = replace(methodName);
Assertions.assertThat(VerifyUtils.invoke(object, methodName)).isEqualTo(9998L + 9998);
}
@Test
public void test_returnStrArray() throws Exception {
String methodName = testName.getMethodName().substring("test_".length());
Object object = replace(methodName);
Assertions.assertThat(VerifyUtils.invoke(object, methodName)).isEqualTo(new String[] { "abc", "xyz", "ufo" });
}
@Test
public void test_returnStrArrayWithArgs() throws Exception {
String methodName = testName.getMethodName().substring("test_".length());
Object object = replace(methodName);
Assertions.assertThat(VerifyUtils.invoke(object, methodName, 123, "sss", 777L))
.isEqualTo(new Object[] { "fff", "xyz" + "sss", "ufo" + 777 });
}
@Test
public void test_returnStr() throws Exception {
String methodName = testName.getMethodName().substring("test_".length());
Object object = replace(methodName);
Assertions.assertThat(VerifyUtils.invoke(object, methodName)).asString().startsWith("hello");
}
@Test
public void test_returnObject() throws Exception {
String methodName = testName.getMethodName().substring("test_".length());
Object object = replace(methodName);
Assertions.assertThat(VerifyUtils.invoke(object, methodName)).isEqualTo(object.getClass());
}
@Test
public void test_recursive() throws Exception {
String methodName = testName.getMethodName().substring("test_".length());
Object object = replace(methodName);
Assertions.assertThat(VerifyUtils.invoke(object, methodName, 100)).isEqualTo((100 + 1) * 100 / 2);
}
}
@@ -0,0 +1,89 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtEnter;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtEnterTest {
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@Rule
public OutputCapture capture = new OutputCapture();
public static class Sample {
long longField;
String strField;
static int intField;
public int hello(String str, boolean exception) {
if (exception) {
throw new RuntimeException("test exception");
}
return str.length();
}
public long toBeInvoke(int i , long l, String s, long ll) {
return l + ll;
}
public void testInvokeArgs() {
toBeInvoke(1, 123L, "abc", 100L);
}
}
public static class TestPrintSuppressHandler {
@ExceptionHandler(inline = true)
public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
System.err.println("exception handler: " + clazz);
e.printStackTrace();
}
}
public static class EnterInterceptor {
@AtEnter(inline = true
, suppress = RuntimeException.class, suppressHandler = TestPrintSuppressHandler.class
)
public static long onEnter(
@Binding.This Object object, @Binding.Class Object clazz,
@Binding.Field(name = "longField") long longField,
@Binding.Field(name = "longField") Object longFieldObject,
@Binding.Field(name = "intField") int intField,
@Binding.Field(name = "strField") String strField,
@Binding.Field(name = "intField") Object intFielObject
) {
System.err.println("onEnter, object:" + object);
return 123L;
}
}
@Test
public void testEnter() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(EnterInterceptor.class).methodMatcher("hello")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
new Sample().hello("abc", false);
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("onEnter, object:");
}
}
@@ -0,0 +1,84 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExceptionExit;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtExceptionExitTest {
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@Rule
public OutputCapture capture = new OutputCapture();
public static class Sample {
long longField;
String strField;
static int intField;
public int hello(String str, boolean exception) {
if (exception) {
throw new RuntimeException("test exception");
}
return str.length();
}
public long toBeInvoke(int i , long l, String s, long ll) {
return l + ll;
}
public void testInvokeArgs() {
toBeInvoke(1, 123L, "abc", 100L);
}
}
public static class TestPrintSuppressHandler {
@ExceptionHandler(inline = true)
public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
System.err.println("exception handler: " + clazz);
System.err.println(e.getMessage());
assertThat(e).hasMessage("exception for ExceptionHandler");
}
}
public static class ExceptionExitInterceptor {
@AtExceptionExit(inline = false, onException = RuntimeException.class ,suppress = Throwable.class, suppressHandler = TestPrintSuppressHandler.class)
public static void onExceptionExit(@Binding.Throwable RuntimeException ex, @Binding.This Object object,
@Binding.Class Object clazz) {
System.err.println("AtExceptionExit, ex:" + ex);
throw new RuntimeException("exception for ExceptionHandler");
}
}
@Test
public void testExecptionExitException() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(ExceptionExitInterceptor.class).methodMatcher("hello")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
System.err.println(Decompiler.decompile(bytes));
try {
new Sample().hello("abc", true);
} catch (Exception e) {
assertThat(e).isInstanceOf(RuntimeException.class).hasMessageContaining("test exception");
}
assertThat(capture.toString()).contains("AtExceptionExit, ex:");
}
}
@@ -0,0 +1,93 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExit;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtExitTest {
@Rule
public OutputCapture capture = new OutputCapture();
static class Sample {
long longField;
int intField;
String strField;
public void voidExit() {
}
public long longExit() {
return 100L;
}
public static long staticExit() {
return 999L;
}
}
public static class TestPrintSuppressHandler {
@ExceptionHandler(inline = false)
public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
System.err.println("exception handler: " + clazz);
e.printStackTrace();
}
}
public static class TestAccessInterceptor {
@AtExit(inline = false)
public static void atExit(@Binding.This Object object,
@Binding.Class Object clazz
,
@Binding.Return Object re
) {
System.err.println("AtFieldAccess: this" + object);
}
}
public static class ChangeReturnInterceptor {
@AtExit(inline = false, suppress = RuntimeException.class, suppressHandler = TestPrintSuppressHandler.class)
public static Object onExit(@Binding.This Object object, @Binding.Class Object clazz) {
System.err.println("onExit, object:" + object);
return 123L;
}
}
@Test
public void testExit() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(TestAccessInterceptor.class).methodMatcher("voidExit")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
new Sample().voidExit();
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("AtFieldAccess: this");
}
@Test
public void testExitAndChangeReturn() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(ChangeReturnInterceptor.class).methodMatcher("longExit")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
System.err.println(Decompiler.decompile(bytes));
long re = new Sample().longExit();
assertThat(re).isEqualTo(123);
assertThat(capture.toString()).contains("onExit, object:");
}
}
@@ -0,0 +1,60 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtFieldAccess;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtFieldAccessTest {
@Rule
public OutputCapture capture = new OutputCapture();
class Sample {
long longField;
int intField;
String strField;
public int testReadField(int ii) {
longField = 999;
return 123;
}
}
public static class TestPrintSuppressHandler {
@ExceptionHandler(inline = false)
public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
System.err.println("exception handler: " + clazz);
e.printStackTrace();
}
}
public static class FieldAccessInterceptor {
@AtFieldAccess(name = "longField" , inline =false)
public static void onFieldAccess(@Binding.This Object object,
@Binding.Class Object clazz) {
System.err.println("AtFieldAccess: this" + object);
}
}
@Test
public void testEnter() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(FieldAccessInterceptor.class).methodMatcher("testReadField")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
new Sample().testReadField(100);
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("AtFieldAccess: this");
}
}
@@ -0,0 +1,101 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtInvoke;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtInvokeTest {
@Rule
public OutputCapture capture = new OutputCapture();
static class Sample {
long longField;
int intField;
String strField;
public Sample(int i, long l, String s) {
staticToBeCall(i, l, s);
aaa("aaa");
}
public int testCall(int ii) {
toBeCall(ii, 123L, "");
System.err.println("abc");
aaa("abc");
return 123;
}
public void aaa(String aaa) {
return ;
}
public long toBeCall(int i , long l, String s) {
return l + i;
}
public static long staticToBeCall(int i , long l, String s) {
return l + i;
}
}
public static class TestPrintSuppressHandler {
@ExceptionHandler(inline = false)
public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
System.err.println("exception handler: " + clazz);
e.printStackTrace();
}
}
public static class TestAccessInterceptor {
@AtInvoke(name = "", inline = false, whenComplete=false, excludes = {"System."})
public static void onInvoke(
@Binding.This Object object,
@Binding.Class Object clazz
,
@Binding.InvokeArgs Object[] args
) {
System.err.println("onInvoke: this" + object);
}
@AtInvoke(name = "toBeCall", inline = false, whenComplete = true)
public static void onInvokeAfter(
@Binding.This Object object,
@Binding.Class Object clazz
,
@Binding.InvokeReturn Object invokeReturn
,
@Binding.InvokeMethodDeclaration String declaration
) {
System.err.println("onInvokeAfter: this" + object);
System.err.println("declaration: " + declaration);
assertThat(declaration).isEqualTo("long toBeCall(int, long, java.lang.String)");
System.err.println("invokeReturn: " + invokeReturn);
assertThat(invokeReturn).isEqualTo(100 + 123L);
}
}
@Test
public void testInvokeBefore() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(TestAccessInterceptor.class).methodMatcher("testCall")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
new Sample(100, 100L, "").testCall(100);
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("onInvoke: this");
}
}
@@ -0,0 +1,85 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtLine;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtLineTest {
@Rule
public OutputCapture capture = new OutputCapture();
static class Sample {
public int testLine(int i) {
String s = "" + i;
if(i > 0) {
String abc = s + i;
i++;
i = i * 100
+ i
- 100 + Math.max(100, i);
i += s.length() + abc.length();
}else {
if(i == -1) {
try {
System.err.println("i is -1");
throw new RuntimeException();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
return i * 2;
}
}
public static class TestAccessInterceptor {
@AtLine(lines = { -1}, inline = false)
public static void atLine(
@Binding.This Object object,
@Binding.Class Object clazz
,
@Binding.Line int line,
@Binding.Args Object[] args
,
@Binding.ArgNames String[] argNames
,
@Binding.LocalVars Object[] vars,
@Binding.LocalVarNames String[] varNames
) {
System.err.println("atLine: this" + object);
System.err.println("line: " + line);
System.err.println("args: " + Arrays.toString(args));
System.err.println("argNames: " + Arrays.toString(argNames));
System.err.println("vars: " + Arrays.toString(vars));
System.err.println("varNames: " + Arrays.toString(varNames));
}
}
@Test
public void testLine() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(TestAccessInterceptor.class).methodMatcher("*")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
new Sample().testLine(100);
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("atLine: this");
}
}
@@ -0,0 +1,89 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtSyncEnter;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtSyncEnterTest {
@Rule
public OutputCapture capture = new OutputCapture();
static class Sample {
public int testLine(int i) {
String s = "" + i;
synchronized (s) {
if(i > 0) {
String abc = s + i;
i++;
i = i * 100
+ i
- 100 + Math.max(100, i);
i += s.length() + abc.length();
}else {
if(i == -1) {
try {
System.err.println("i is -1");
throw new RuntimeException();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
}
return i * 2;
}
}
public static class TestInterceptor {
@AtSyncEnter(whenComplete=false, inline = false)
public static void atSyncEnter(
@Binding.This Object object,
@Binding.Class Object clazz
,
@Binding.Args Object[] args
,
@Binding.ArgNames String[] argNames
,
@Binding.LocalVars Object[] vars,
@Binding.LocalVarNames String[] varNames
,
@Binding.Monitor Object monitor
) {
System.err.println("atSyncEnter: this" + object);
System.err.println("args: " + Arrays.toString(args));
System.err.println("argNames: " + Arrays.toString(argNames));
System.err.println("vars: " + Arrays.toString(vars));
System.err.println("varNames: " + Arrays.toString(varNames));
}
}
@Test
public void test() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(TestInterceptor.class).methodMatcher("*")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
new Sample().testLine(100);
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("atSyncEnter: this");
}
}
@@ -0,0 +1,89 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtSyncExit;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtSyncExitTest {
@Rule
public OutputCapture capture = new OutputCapture();
static class Sample {
public int testLine(int i) {
String s = "" + i;
synchronized (s) {
if(i > 0) {
String abc = s + i;
i++;
i = i * 100
+ i
- 100 + Math.max(100, i);
i += s.length() + abc.length();
}else {
if(i == -1) {
try {
System.err.println("i is -1");
throw new RuntimeException();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
}
return i * 2;
}
}
public static class TestInterceptor {
@AtSyncExit(whenComplete=false, inline = false)
public static void atSyncExit(
@Binding.This Object object,
@Binding.Class Object clazz
,
@Binding.Args Object[] args
,
@Binding.ArgNames String[] argNames
,
@Binding.LocalVars Object[] vars,
@Binding.LocalVarNames String[] varNames
,
@Binding.Monitor Object monitor
) {
System.err.println("atSyncExit: this" + object);
System.err.println("args: " + Arrays.toString(args));
System.err.println("argNames: " + Arrays.toString(argNames));
System.err.println("vars: " + Arrays.toString(vars));
System.err.println("varNames: " + Arrays.toString(varNames));
}
}
@Test
public void test() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(TestInterceptor.class).methodMatcher("*")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
new Sample().testLine(100);
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("atSyncExit: this");
}
}
@@ -0,0 +1,76 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtThrow;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtThrowTest {
@Rule
public OutputCapture capture = new OutputCapture();
static class Sample {
public static long testThrow(int i , long l, String s) {
try {
if(i < 0) {
throw new RuntimeException("eeeee");
}
} catch (Exception e) {
System.err.println(e.getMessage());
}
return l + i;
}
}
public static class TestPrintSuppressHandler {
@ExceptionHandler(inline = false)
public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
System.err.println("exception handler: " + clazz);
e.printStackTrace();
}
}
public static class TestAccessInterceptor {
@AtThrow(inline = false)
public static void atThrow(
@Binding.This Object object,
@Binding.Class Object clazz
,
@Binding.LocalVars Object[] vars,
@Binding.Throwable Throwable t
) {
System.err.println("atThrow: this" + object);
System.err.println("vars: " + Arrays.toString(vars));
System.err.println("t: " + t);
assertThat(t).hasMessage("eeeee");
}
}
@Test
public void testThrow() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(TestAccessInterceptor.class).methodMatcher("testThrow")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
Sample.testThrow(-1, 0, null);
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("atThrow: this");
}
}
@@ -0,0 +1,78 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.parser.DefaultInterceptorClassParser;
import com.taobao.arthas.bytekit.utils.AgentUtils;
import com.taobao.arthas.bytekit.utils.AsmUtils;
import com.taobao.arthas.bytekit.utils.MatchUtils;
import com.taobao.arthas.bytekit.utils.VerifyUtils;
public class TestHelper {
private Class<?> interceptorClass;
private boolean redefine;
private String methodMatcher = "*";
private boolean asmVerity = true;
public static TestHelper builder() {
return new TestHelper();
}
public TestHelper interceptorClass(Class<?> interceptorClass) {
this.interceptorClass = interceptorClass;
return this;
}
public TestHelper redefine(boolean redefine) {
this.redefine = redefine;
return this;
}
public TestHelper methodMatcher(String methodMatcher) {
this.methodMatcher = methodMatcher;
return this;
}
public byte[] process(Class<?> transform) throws Exception {
DefaultInterceptorClassParser defaultInterceptorClassParser = new DefaultInterceptorClassParser();
List<InterceptorProcessor> interceptorProcessors = defaultInterceptorClassParser.parse(interceptorClass);
ClassNode classNode = AsmUtils.loadClass(transform);
List<MethodNode> matchedMethods = new ArrayList<MethodNode>();
for (MethodNode methodNode : classNode.methods) {
if (MatchUtils.wildcardMatch(methodNode.name, methodMatcher)) {
matchedMethods.add(methodNode);
}
}
for (MethodNode methodNode : matchedMethods) {
MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode);
for (InterceptorProcessor interceptor : interceptorProcessors) {
interceptor.process(methodProcessor);
}
}
byte[] bytes = AsmUtils.toBytes(classNode);
if (asmVerity) {
VerifyUtils.asmVerify(bytes);
}
if (redefine) {
AgentUtils.redefine(transform, bytes);
}
return bytes;
}
}
@@ -0,0 +1,144 @@
package com.taobao.arthas.bytekit.utils;
import java.io.IOException;
import java.util.List;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import com.alibaba.arthas.deps.org.objectweb.asm.ClassWriter;
import com.alibaba.arthas.deps.org.objectweb.asm.MethodVisitor;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
import com.taobao.arthas.bytekit.utils.AsmUtils;
import com.taobao.arthas.bytekit.utils.VerifyUtils;
public class AsmUtilsTest {
abstract static class TestClass {
public static synchronized List<String> sss(int i, long l, List<String> list) throws IOException, ArrayIndexOutOfBoundsException {
return null;
}
protected abstract String hello(String ss);
}
static class TestConstructorClass {
public TestConstructorClass(int i, String s) {
}
}
@Test
public void testMethodDeclaration() throws IOException {
ClassNode classNode = AsmUtils.loadClass(TestClass.class);
MethodNode sss = AsmUtils.findFirstMethod(classNode.methods, "sss");
MethodNode hello = AsmUtils.findFirstMethod(classNode.methods, "hello");
MethodNode constructor = AsmUtils.findFirstMethod(AsmUtils.loadClass(TestConstructorClass.class).methods, "<init>");
String helloDeclaration = AsmUtils.methodDeclaration(Type.getType(TestClass.class), hello);
String sssDeclaration = AsmUtils.methodDeclaration(Type.getType(TestClass.class), sss);
String constructorDeclaration = AsmUtils.methodDeclaration(Type.getType(TestConstructorClass.class), constructor);
System.err.println(helloDeclaration);
System.err.println(sssDeclaration);
System.err.println(constructorDeclaration);
Assertions.assertThat(helloDeclaration).isEqualTo("protected abstract java.lang.String hello(java.lang.String)");
Assertions.assertThat(sssDeclaration).isEqualTo(
"public static synchronized java.util.List sss(int, long, java.util.List) throws java.io.IOException, java.lang.ArrayIndexOutOfBoundsException");
Assertions.assertThat(constructorDeclaration).isEqualTo("public com.taobao.arthas.bytekit.utils.AsmUtilsTest$TestConstructorClass(int, java.lang.String)");
}
public static byte[] emptyMethodBytes() throws Exception {
ClassWriter cw = new ClassWriter(0);
MethodVisitor mv;
cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, "LEmptyClass", null, "java/lang/Object", null);
{
mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "emptyMethod", "()V", null, null);
// mv.visitCode();
mv.visitInsn(Opcodes.RETURN);
// mv.visitMaxs(0, 0);
// mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
@Test
public void emptyMethodTest() throws Exception {
byte[] emptyMethodBytes = emptyMethodBytes();
VerifyUtils.asmVerify(emptyMethodBytes);
VerifyUtils.instanceVerity(emptyMethodBytes);
ClassNode classNode = AsmUtils.toClassNode(emptyMethodBytes);
MethodNode methodNode = AsmUtils.findFirstMethod(classNode.methods, "emptyMethod");
AbstractInsnNode first = methodNode.instructions.getFirst();
AbstractInsnNode last = methodNode.instructions.getLast();
System.err.println(first);
System.err.println(last);
int size = methodNode.instructions.size();
for (int i = 0; i < size; ++i) {
System.err.println(methodNode.instructions.get(i));
}
// String asmCode = AsmUtils.toASMCode(classNode);
// System.err.println(asmCode);
}
private String aaa = "";
public void xxx () {
aaa = "bbb";
}
@Test
public void testFieldAccess() throws IOException {
ClassNode classNode = AsmUtils.loadClass(AsmUtilsTest.class);
MethodNode methodNode = AsmUtils.findFirstMethod(classNode.methods, "xxx");
int size = methodNode.instructions.size();
for (int i = 0; i < size; ++i) {
System.err.println(methodNode.instructions.get(i));
}
}
@Test
public void testRenameClass() throws Exception {
ClassNode classNode = AsmUtils.loadClass(AsmUtilsTest.class);
byte[] classBytes = AsmUtils.toBytes(classNode);
byte[] renameClass = AsmUtils.renameClass(classBytes, "com.test.Test.XXX");
VerifyUtils.asmVerify(renameClass);
Object object = VerifyUtils.instanceVerity(renameClass);
Assertions.assertThat(object.getClass().getName()).isEqualTo("com.test.Test.XXX");
}
}
@@ -0,0 +1,9 @@
package com.taobao.arthas.bytekit.utils;
public class EmptyClass {
public static void emptyMethod() {
}
}