mirror of
https://github.com/alibaba/arthas.git
synced 2024-04-21 10:21:39 +00:00
add bytekit module
This commit is contained in:
@@ -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:");
|
||||
}
|
||||
|
||||
}
|
||||
+84
@@ -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:");
|
||||
}
|
||||
}
|
||||
+60
@@ -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() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user