diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/Instrument.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/Instrument.java
new file mode 100644
index 000000000..1dc51dba6
--- /dev/null
+++ b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/Instrument.java
@@ -0,0 +1,35 @@
+package com.taobao.arthas.bytekit.asm.inst;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+
+/**
+ * 在这里支持配置一个 error hander ?做为插入的异常处理的
+ *
+ * 按名字匹配,按模糊匹配??有没有这样子的需求?,按interface匹配,按基础类继承的匹配
+ *
+ * 函数的匹配,直接是名字一样,desc 一样的。 匹配有 annotation 的
+ *
+ * 只有 NewField 才是加新的field,原来类里有的field,就直接写上就可以了。
+ * @author hengyunabc
+ *
+ */
+@Target({ java.lang.annotation.ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Instrument {
+// Instrumentation
+// MatchType type() default MatchType.ExactClass;
+
+ String[] Class() default {};
+ String[] BaseClass() default {};
+ String[] Interface() default {};
+
+ String originalName() default "";
+
+ Class extends Throwable> suppress() default Throwable.class;
+
+ Class> suppressHandler() default Void.class;
+}
\ No newline at end of file
diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/InstrumentApi.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/InstrumentApi.java
new file mode 100644
index 000000000..d9e48a5db
--- /dev/null
+++ b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/InstrumentApi.java
@@ -0,0 +1,32 @@
+package com.taobao.arthas.bytekit.asm.inst;
+
+
+/**
+ *
+ *
+ * 实现这个 invokeOrigin(),需要多步处理:
+ *
+ * 传入要被替换的类,读取到标记了 @Instrument 的类。 类名不一样的话,先替换类名?
+ *
+ * 然后查找所有的 field,如果有标记了 @NewField ,则增加到要被替换的类里。
+ *
+ * 然后查找所有的函数, 再查找是否在 旧类里有同样签名的,如果有,则执行清除行号, 替换 invokeOrigin() ,再 inline 原来的旧函数
+ *
+ * 再替换函数到 旧类里。
+ *
+ * 类名有可能要替换
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @author hengyunabc 2019-02-25
+ *
+ */
+public class InstrumentApi {
+ public static final T invokeOrigin() {
+ return null;
+ }
+}
diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/NewField.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/NewField.java
new file mode 100644
index 000000000..3e0707a04
--- /dev/null
+++ b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/NewField.java
@@ -0,0 +1,10 @@
+package com.taobao.arthas.bytekit.asm.inst;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ java.lang.annotation.ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NewField {
+}
\ No newline at end of file
diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/InterceptorProcessor.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/InterceptorProcessor.java
index e51158fb1..d87f6d514 100644
--- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/InterceptorProcessor.java
+++ b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/InterceptorProcessor.java
@@ -24,28 +24,37 @@ import com.taobao.arthas.bytekit.utils.Decompiler;
public class InterceptorProcessor {
private LocationMatcher locationMatcher;
-
+
/**
* 插入的回调函数的配置
*/
- private InterceptorMethodConfig interceptorMethodConfig;
-
+ private InterceptorMethodConfig interceptorMethodConfig;
+
/**
* 插入的代码被 try/catch 包围的配置,注意有一些location插入try/catch会可能失败,因为不能确切知道栈上的情况
*/
- private InterceptorMethodConfig exceptionHandlerConfig;
+ private InterceptorMethodConfig exceptionHandlerConfig;
+
+ /**
+ * 加载inlne类所需要的ClassLoader
+ */
+ private ClassLoader classLoader;
+
+ public InterceptorProcessor(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
public void process(MethodProcessor methodProcessor) throws Exception {
List locations = locationMatcher.match(methodProcessor);
-
+
List interceptorBindings = interceptorMethodConfig.getBindings();
for (Location location : locations) {
-
+
// 有三小段代码,1: 保存当前栈上的值的 , 2: 插入的回调的 , 3:恢复当前栈的
InsnList toInsert = new InsnList();
-
+
InsnList stackSaveInsnList = new InsnList();
InsnList stackLoadInsnList = new InsnList();
@@ -54,33 +63,33 @@ public class InterceptorProcessor {
stackSaver = location.getStackSaver();
}
BindingContext bindingContext = new BindingContext(location, methodProcessor, stackSaver);
-
+
if(stackSaver != null) {
stackSaver.store(stackSaveInsnList, bindingContext);
stackSaver.load(stackLoadInsnList, bindingContext);
}
-
-
+
+
Type methodType = Type.getMethodType(interceptorMethodConfig.getMethodDesc());
Type[] argumentTypes = methodType.getArgumentTypes();
// 检查回调函数的参数和 binding数一致
if(interceptorBindings.size() != argumentTypes.length) {
throw new IllegalArgumentException("interceptorBindings size no equals with interceptorMethod args size.");
}
-
+
// 把当前栈上的数据保存起来
int fromStackBindingCount = 0;
for (Binding binding : interceptorBindings) {
if(binding.fromStack()) {
- fromStackBindingCount++;
+ fromStackBindingCount++;
}
}
// 只允许一个binding从栈上保存数据
if(fromStackBindingCount > 1) {
throw new IllegalArgumentException("interceptorBindings have more than one from stack Binding.");
}
-
-
+
+
// 组装好要调用的 static 函数的参数
for(int i = 0 ; i < argumentTypes.length; ++i) {
Binding binding = interceptorBindings.get(i);
@@ -88,15 +97,15 @@ public class InterceptorProcessor {
// 检查 回调函数的参数类型,看是否要box一下 ,检查是否原始类型就可以了。
// 只有类型不一样时,才需要判断。比如两个都是 long,则不用判断
Type bindingType = binding.getType(bindingContext);
- if(!bindingType.equals(argumentTypes[i])) {
+ if(!bindingType.equals(argumentTypes[i])) {
if(AsmOpUtils.needBox(bindingType)) {
AsmOpUtils.box(toInsert, binding.getType(bindingContext));
}
}
}
-
+
// TODO 要检查 binding 和 回调的函数的参数类型是否一致。回调函数的类型可以是 Object,或者super。但是不允许一些明显的类型问题,比如array转到int
-
+
toInsert.add(new MethodInsnNode(Opcodes.INVOKESTATIC, interceptorMethodConfig.getOwner(), interceptorMethodConfig.getMethodName(),
interceptorMethodConfig.getMethodDesc(), false));
@@ -120,27 +129,27 @@ public class InterceptorProcessor {
}
}
}
-
-
+
+
TryCatchBlock errorHandlerTryCatchBlock = null;
// 生成的代码用try/catch包围起来
if( exceptionHandlerConfig != null) {
LabelNode gotoDest = new LabelNode();
-
+
errorHandlerTryCatchBlock = new TryCatchBlock(methodProcessor.getMethodNode(), exceptionHandlerConfig.getSuppress());
toInsert.insertBefore(toInsert.getFirst(), errorHandlerTryCatchBlock.getStartLabelNode());
toInsert.add(new JumpInsnNode(Opcodes.GOTO, gotoDest));
toInsert.add(errorHandlerTryCatchBlock.getEndLabelNode());
// 这里怎么把栈上的数据保存起来?还是强制回调函数的第一个参数是 exception,后面的binding可以随便搞。
-
+
// MethodInsnNode printStackTrace = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", "printStackTrace", "()V", false);
// toInsert.add(printStackTrace);
-
+
errorHandler(methodProcessor, toInsert);
-
+
toInsert.add(gotoDest);
}
-
+
// System.err.println(Decompiler.toString(toInsert));
@@ -151,36 +160,40 @@ public class InterceptorProcessor {
}else {
methodProcessor.getMethodNode().instructions.insertBefore(location.getInsnNode(), stackSaveInsnList);
}
-
+
if( exceptionHandlerConfig != null) {
errorHandlerTryCatchBlock.sort();
}
-
+
// inline callback
if(interceptorMethodConfig.isInline()) {
- Class> forName = Class.forName(Type.getObjectType(interceptorMethodConfig.getOwner()).getClassName());
+// Class> forName = Class.forName(Type.getObjectType(interceptorMethodConfig.getOwner()).getClassName());
+
+ Class> forName = classLoader.loadClass(Type.getObjectType(interceptorMethodConfig.getOwner()).getClassName());
MethodNode toInlineMethodNode = AsmUtils.findMethod(AsmUtils.loadClass(forName).methods, interceptorMethodConfig.getMethodName(), interceptorMethodConfig.getMethodDesc());
-
+
methodProcessor.inline(interceptorMethodConfig.getOwner(), toInlineMethodNode);
}
if(exceptionHandlerConfig != null && exceptionHandlerConfig.isInline()) {
- Class> forName = Class.forName(Type.getObjectType(exceptionHandlerConfig.getOwner()).getClassName());
+// Class> forName = Class.forName(Type.getObjectType(exceptionHandlerConfig.getOwner()).getClassName());
+
+ Class> forName = classLoader.loadClass(Type.getObjectType(exceptionHandlerConfig.getOwner()).getClassName());
MethodNode toInlineMethodNode = AsmUtils.findMethod(AsmUtils.loadClass(forName).methods, exceptionHandlerConfig.getMethodName(), exceptionHandlerConfig.getMethodDesc());
-
+
methodProcessor.inline(exceptionHandlerConfig.getOwner(), toInlineMethodNode);
}
-
+
// System.err.println(Decompiler.toString(methodProcessor.getMethodNode()));
// System.err.println(AsmUtils.toASMCode(methodProcessor.getMethodNode()));
}
}
-
+
private void errorHandler(MethodProcessor methodProcessor, InsnList insnList) {
// MethodInsnNode printStackTrace = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", "printStackTrace", "()V", false);
// insnList.add(printStackTrace);
- // 第一个参数要求是 throwable ,或者一个exception ?
+ // 第一个参数要求是 throwable ,或者一个exception ?
// 有很多 binding 并不能使用的,因为location不生效
- BindingContext bindingContext = new BindingContext(null, methodProcessor, null);
+ BindingContext bindingContext = new BindingContext(null, methodProcessor, null);
Type methodType = Type.getMethodType(this.exceptionHandlerConfig.getMethodDesc());
Type[] argumentTypes = methodType.getArgumentTypes();
List bindings = this.exceptionHandlerConfig.getBindings();
@@ -201,10 +214,10 @@ public class InterceptorProcessor {
AsmOpUtils.box(insnList, binding.getType(bindingContext));
}
}
-
+
insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, exceptionHandlerConfig.getOwner(), exceptionHandlerConfig.getMethodName(),
exceptionHandlerConfig.getMethodDesc(), false));
-
+
int size = methodType.getReturnType().getSize();
if (size == 1) {
AsmOpUtils.pop(insnList);
diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtEnter.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtEnter.java
index ca5fe5a61..af121ace2 100644
--- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtEnter.java
+++ b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtEnter.java
@@ -33,8 +33,7 @@ public @interface AtEnter {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
-
- InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
+ InterceptorProcessor interceptorProcessor = new InterceptorProcessor(method.getDeclaringClass().getClassLoader());
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExceptionExit.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExceptionExit.java
index 3703da67c..85561d034 100644
--- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExceptionExit.java
+++ b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExceptionExit.java
@@ -36,7 +36,7 @@ public @interface AtExceptionExit {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
- InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
+ InterceptorProcessor interceptorProcessor = new InterceptorProcessor(method.getDeclaringClass().getClassLoader());
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
@@ -47,9 +47,9 @@ public @interface AtExceptionExit {
AtExceptionExit atExceptionExit = (AtExceptionExit) annotationOnMethod;
interceptorMethodConfig.setInline(atExceptionExit.inline());
-
+
LocationMatcher locationMatcher = new ExceptionExitLocationMatcher(Type.getInternalName(atExceptionExit.onException()));;
-
+
interceptorProcessor.setLocationMatcher(locationMatcher);
List bindings = BindingParserUtils.parseBindings(method);
diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExit.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExit.java
index c8f03e449..5bc60d0e0 100644
--- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExit.java
+++ b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExit.java
@@ -26,13 +26,13 @@ public @interface AtExit {
boolean inline() default true;
Class extends Throwable> suppress() default None.class;
Class> suppressHandler() default Void.class;
-
+
class ExitInterceptorProcessorParser implements InterceptorProcessorParser {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
- InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
+ InterceptorProcessor interceptorProcessor = new InterceptorProcessor(method.getDeclaringClass().getClassLoader());
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtFieldAccess.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtFieldAccess.java
index 084763183..d7e602dbe 100644
--- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtFieldAccess.java
+++ b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtFieldAccess.java
@@ -47,7 +47,7 @@ public @interface AtFieldAccess {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
- InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
+ InterceptorProcessor interceptorProcessor = new InterceptorProcessor(method.getDeclaringClass().getClassLoader());
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
@@ -65,7 +65,7 @@ public @interface AtFieldAccess {
if(!atFieldAccess.type().equals(Void.class)) {
fieldDesc = Type.getType(atFieldAccess.type()).getDescriptor();
}
-
+
LocationMatcher locationMatcher = new FieldAccessLocationMatcher(
ownerClass,
fieldDesc, atFieldAccess.name(), atFieldAccess.count(),
diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtInvoke.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtInvoke.java
index 7d2580229..6305eceae 100644
--- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtInvoke.java
+++ b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtInvoke.java
@@ -47,7 +47,7 @@ public @interface AtInvoke {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
- InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
+ InterceptorProcessor interceptorProcessor = new InterceptorProcessor(method.getDeclaringClass().getClassLoader());
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtLine.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtLine.java
index e8847b187..e16d44753 100644
--- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtLine.java
+++ b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtLine.java
@@ -36,7 +36,7 @@ public @interface AtLine {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
- InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
+ InterceptorProcessor interceptorProcessor = new InterceptorProcessor(method.getDeclaringClass().getClassLoader());
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncEnter.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncEnter.java
index 0e6bbb1ac..c541d0c9a 100644
--- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncEnter.java
+++ b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncEnter.java
@@ -29,7 +29,7 @@ public @interface AtSyncEnter {
Class extends Throwable> suppress() default None.class;
Class> suppressHandler() default Void.class;
-
+
int count() default -1;
boolean whenComplete() default false;
@@ -38,7 +38,7 @@ public @interface AtSyncEnter {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
- InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
+ InterceptorProcessor interceptorProcessor = new InterceptorProcessor(method.getDeclaringClass().getClassLoader());
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
@@ -47,10 +47,10 @@ public @interface AtSyncEnter {
interceptorMethodConfig.setMethodDesc(Type.getMethodDescriptor(method));
AtSyncEnter atSyncEnter = (AtSyncEnter) annotationOnMethod;
-
+
LocationMatcher locationMatcher = new SyncLocationMatcher(Opcodes.MONITORENTER, atSyncEnter.count(), atSyncEnter.whenComplete());
interceptorProcessor.setLocationMatcher(locationMatcher);
-
+
interceptorMethodConfig.setInline(atSyncEnter.inline());
List bindings = BindingParserUtils.parseBindings(method);
diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncExit.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncExit.java
index e24522a49..b139bb180 100644
--- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncExit.java
+++ b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncExit.java
@@ -29,7 +29,7 @@ public @interface AtSyncExit {
Class extends Throwable> suppress() default None.class;
Class> suppressHandler() default Void.class;
-
+
int count() default -1;
boolean whenComplete() default false;
@@ -38,7 +38,7 @@ public @interface AtSyncExit {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
- InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
+ InterceptorProcessor interceptorProcessor = new InterceptorProcessor(method.getDeclaringClass().getClassLoader());
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
@@ -47,10 +47,10 @@ public @interface AtSyncExit {
interceptorMethodConfig.setMethodDesc(Type.getMethodDescriptor(method));
AtSyncExit atSyncExit = (AtSyncExit) annotationOnMethod;
-
+
LocationMatcher locationMatcher = new SyncLocationMatcher(Opcodes.MONITOREXIT, atSyncExit.count(), atSyncExit.whenComplete());
interceptorProcessor.setLocationMatcher(locationMatcher);
-
+
interceptorMethodConfig.setInline(atSyncExit.inline());
List bindings = BindingParserUtils.parseBindings(method);
diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtThrow.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtThrow.java
index 04c47ad57..75e7a5e47 100644
--- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtThrow.java
+++ b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtThrow.java
@@ -28,7 +28,7 @@ public @interface AtThrow {
Class extends Throwable> suppress() default None.class;
Class> suppressHandler() default Void.class;
-
+
int count() default -1;
class ThrowInterceptorProcessorParser implements InterceptorProcessorParser {
@@ -36,7 +36,7 @@ public @interface AtThrow {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
- InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
+ InterceptorProcessor interceptorProcessor = new InterceptorProcessor(method.getDeclaringClass().getClassLoader());
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
@@ -47,7 +47,7 @@ public @interface AtThrow {
AtThrow atThrow = (AtThrow) annotationOnMethod;
LocationMatcher locationMatcher = new ThrowLocationMatcher(atThrow.count());
interceptorProcessor.setLocationMatcher(locationMatcher);
-
+
interceptorMethodConfig.setInline(atThrow.inline());
List bindings = BindingParserUtils.parseBindings(method);
diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmUtils.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmUtils.java
index e213c2857..7daed4df1 100644
--- a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmUtils.java
+++ b/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmUtils.java
@@ -12,12 +12,15 @@ import java.util.Comparator;
import java.util.List;
import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.ClassRemapper;
import org.objectweb.asm.commons.JSRInlinerAdapter;
+import org.objectweb.asm.commons.Remapper;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
@@ -52,6 +55,42 @@ public class AsmUtils {
return writer.toByteArray();
}
+ public static byte[] renameClass(byte[] classBytes, final String newClassName) {
+ final String internalName = newClassName.replace('.', '/');
+
+ ClassReader reader = new ClassReader(classBytes);
+ ClassWriter writer = new ClassWriter(0);
+
+ class RenameRemapper extends Remapper {
+ private String className;
+
+ @Override
+ public String map(String typeName) {
+ if (typeName.equals(className)) {
+ return internalName;
+ }
+ return super.map(typeName);
+ }
+
+ public void setClassName(String className) {
+ this.className = className;
+ }
+ }
+
+ final RenameRemapper renameRemapper = new RenameRemapper();
+ ClassRemapper adapter = new ClassRemapper(writer, renameRemapper) {
+ @Override
+ public void visit(final int version, final int access, final String name, final String signature,
+ final String superName, final String[] interfaces) {
+ renameRemapper.setClassName(name);
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+ };
+ reader.accept(adapter, ClassReader.EXPAND_FRAMES);
+ writer.visitEnd();
+ return writer.toByteArray();
+ }
+
public static void replaceMethod(ClassNode classNode, MethodNode methodNode) {
for (int index = 0; index < classNode.methods.size(); ++index) {
MethodNode tmp = classNode.methods.get(index);
@@ -131,6 +170,10 @@ public class AsmUtils {
return result;
}
+ public static MethodNode findMethod(Collection methodNodes, MethodNode target) {
+ return findMethod(methodNodes, target.name, target.desc);
+ }
+
public static MethodNode findMethod(Collection methodNodes, String name, String desc) {
for (MethodNode methodNode : methodNodes) {
if (methodNode.name.equals(name) && methodNode.desc.equals(desc)) {
@@ -163,6 +206,20 @@ public class AsmUtils {
return null;
}
+ public static List findMethodInsnNodeWithPrefix(MethodNode methodNode, String prefix) {
+ List result = new ArrayList();
+ for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode
+ .getNext()) {
+ if (insnNode instanceof MethodInsnNode) {
+ final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
+ if(methodInsnNode.name.startsWith(prefix)) {
+ result.add(methodInsnNode);
+ }
+ }
+ }
+ return result;
+ }
+
public static boolean isStatic(MethodNode methodNode) {
return (methodNode.access & Opcodes.ACC_STATIC) != 0;
}
@@ -238,6 +295,20 @@ public class AsmUtils {
return result;
}
+ public static ClassNode copy(ClassNode source) {
+ ClassNode result = new ClassNode(Opcodes.ASM7);
+ source.accept(new ClassVisitor(Opcodes.ASM7, result) {
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+ String[] exceptions) {
+ MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
+ return new JSRInlinerAdapter(mv, access, name, desc, signature, exceptions);
+ }
+ });
+ return result;
+ }
+
+
public static String methodDeclaration(MethodInsnNode methodInsnNode) {
StringBuilder sb = new StringBuilder(128);
@@ -369,6 +440,15 @@ public class AsmUtils {
return null;
}
+ public static void addField(ClassNode classNode, FieldNode fieldNode) {
+ // TODO 检查是否有重复?
+ classNode.fields.add(fieldNode);
+ }
+
+ public static void addMethod(ClassNode classNode, MethodNode methodNode) {
+ classNode.methods.add(methodNode);
+ }
+
// TODO 是否真的 unique 了?
public static String uniqueNameForMethod(String className, String methodName, String desc) {
StringBuilder result = new StringBuilder(128);
diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemo.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemo.java
new file mode 100644
index 000000000..672ed14fa
--- /dev/null
+++ b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemo.java
@@ -0,0 +1,21 @@
+package com.taobao.arthas.bytekit.asm.inst;
+
+import java.util.Date;
+
+public class InstDemo {
+
+ public int returnInt(int i) {
+ return 9998;
+ }
+
+ public static int returnIntStatic(int i) {
+ return 9998;
+ }
+
+ public static void main(String[] args) {
+ Date date = new Date(1551168643);
+
+ System.err.println(date.toLocaleString());
+ }
+
+}
diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemoTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemoTest.java
new file mode 100644
index 000000000..3be246c2b
--- /dev/null
+++ b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemoTest.java
@@ -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 org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AnnotationNode;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.FieldNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import 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 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);
+ }
+}
diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemo_APM.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemo_APM.java
new file mode 100644
index 000000000..75cfd3aed
--- /dev/null
+++ b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemo_APM.java
@@ -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;
+ }
+}
diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginDemo.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginDemo.java
new file mode 100644
index 000000000..cdfb5f79d
--- /dev/null
+++ b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginDemo.java
@@ -0,0 +1,5 @@
+package com.taobao.arthas.bytekit.asm.inst;
+
+public class InvokeOriginDemo {
+
+}
diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginDemo_APM.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginDemo_APM.java
new file mode 100644
index 000000000..0b15a7713
--- /dev/null
+++ b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginDemo_APM.java
@@ -0,0 +1,5 @@
+package com.taobao.arthas.bytekit.asm.inst;
+
+public class InvokeOriginDemo_APM {
+
+}
diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtEnterTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtEnterTest.java
index f39d50d5d..5dead641f 100644
--- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtEnterTest.java
+++ b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtEnterTest.java
@@ -21,7 +21,7 @@ public class AtEnterTest {
public OutputCapture capture = new OutputCapture();
public static class Sample {
-
+
long longField;
String strField;
static int intField;
@@ -32,20 +32,20 @@ public class AtEnterTest {
}
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 = false)
+ @ExceptionHandler(inline = true)
public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
System.err.println("exception handler: " + clazz);
e.printStackTrace();
@@ -54,14 +54,14 @@ public class AtEnterTest {
public static class EnterInterceptor {
- @AtEnter(inline = false
+ @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 = "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
) {
diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/utils/AsmUtilsTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/utils/AsmUtilsTest.java
index a8cb68f81..f46210956 100644
--- a/bytekit/src/test/java/com/taobao/arthas/bytekit/utils/AsmUtilsTest.java
+++ b/bytekit/src/test/java/com/taobao/arthas/bytekit/utils/AsmUtilsTest.java
@@ -126,4 +126,19 @@ public class AsmUtilsTest {
}
+
+ @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");
+ }
+
}