bytekit InterceptorProcessor support ClassLoader

This commit is contained in:
hengyunabc
2019-03-01 19:29:57 +08:00
parent eb9cba1958
commit 55ee992d09
21 changed files with 413 additions and 67 deletions
@@ -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;
}
@@ -0,0 +1,32 @@
package com.taobao.arthas.bytekit.asm.inst;
/**
*
* <pre>
* 实现这个 invokeOrigin(),需要多步处理:
*
* 传入要被替换的类,读取到标记了 @Instrument 的类。 类名不一样的话,先替换类名?
*
* 然后查找所有的 field,如果有标记了 @NewField ,则增加到要被替换的类里。
*
* 然后查找所有的函数, 再查找是否在 旧类里有同样签名的,如果有,则执行清除行号, 替换 invokeOrigin() ,再 inline 原来的旧函数
*
* 再替换函数到 旧类里。
*
* 类名有可能要替换
*
* </pre>
*
*
*
*
*
* @author hengyunabc 2019-02-25
*
*/
public class InstrumentApi {
public static final <T> T invokeOrigin() {
return null;
}
}
@@ -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 {
}
@@ -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<Location> locations = locationMatcher.match(methodProcessor);
List<Binding> 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<Binding> 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);
@@ -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);
@@ -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<Binding> bindings = BindingParserUtils.parseBindings(method);
@@ -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);
@@ -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(),
@@ -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);
@@ -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);
@@ -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<Binding> bindings = BindingParserUtils.parseBindings(method);
@@ -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<Binding> bindings = BindingParserUtils.parseBindings(method);
@@ -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<Binding> bindings = BindingParserUtils.parseBindings(method);
@@ -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<MethodNode> methodNodes, MethodNode target) {
return findMethod(methodNodes, target.name, target.desc);
}
public static MethodNode findMethod(Collection<MethodNode> 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<MethodInsnNode> findMethodInsnNodeWithPrefix(MethodNode methodNode, String prefix) {
List<MethodInsnNode> result = new ArrayList<MethodInsnNode>();
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);
@@ -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());
}
}
@@ -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<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,5 @@
package com.taobao.arthas.bytekit.asm.inst;
public class InvokeOriginDemo {
}
@@ -0,0 +1,5 @@
package com.taobao.arthas.bytekit.asm.inst;
public class InvokeOriginDemo_APM {
}
@@ -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
) {
@@ -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");
}
}