support InstrumentApi.invokeOrigin(). #573

4.0.x
hengyunabc 6 years ago
parent 77460d071f
commit 1f306c5a50

@ -1,7 +1,14 @@
package com.taobao.arthas.bytekit.asm;
/**
*
* @author hengyunabc 2019-03-18
*
*/
public class MethodInfo {
private String owner;
private int access;
private String name;
private String desc;
@ -30,4 +37,12 @@ public class MethodInfo {
this.desc = desc;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
}

@ -80,7 +80,7 @@ public class MethodProcessor {
private static final Method LONG_VALUE = Method.getMethod("long longValue()");
private static final Method DOUBLE_VALUE = Method.getMethod("double doubleValue()");
public static final String DEFAULT_INNER_VARIABLE_PREFIX = "_$bytekit$_";
private final LabelNode interceptorVariableStartLabelNode = new LabelNode();
@ -90,12 +90,12 @@ public class MethodProcessor {
// TODO 这里应该直接从 InsnList 里来取?因为插入代码之后,这个会改变的。
// TODO 这个没有被使用到,是不是没用的??
private AbstractInsnNode lastInsnNode;
/**
* variable
*/
private boolean keepLocalVariableNames;
private String innerVariablePrefix;
private String returnVariableName;
@ -106,21 +106,21 @@ public class MethodProcessor {
private LocalVariableNode throwVariableNode = null;
private LocalVariableNode invokeArgsVariableNode = null;
private LocalVariableNode monitorVariableNode = null; // for synchronized
private String invokeReturnVariablePrefix;
private Map<String, LocalVariableNode> invokeReturnVariableNodeMap = new HashMap<String, LocalVariableNode>();
private TryCatchBlock tryCatchBlock = null;
public MethodProcessor(final ClassNode classNode, final MethodNode methodNode) {
this(classNode, methodNode, false);
}
public MethodProcessor(final ClassNode classNode, final MethodNode methodNode, boolean keepLocalVariableNames) {
this(classNode.name, methodNode, keepLocalVariableNames);
this.classNode = classNode;
}
public MethodProcessor(final String owner, final MethodNode methodNode, boolean keepLocalVariableNames) {
this.owner = owner;
this.methodNode = methodNode;
@ -135,20 +135,20 @@ public class MethodProcessor {
} else {
this.enterInsnNode = methodNode.instructions.getFirst();
}
// when the method is empty, both enterInsnNode and lastInsnNode are Opcodes.RETURN ;
this.lastInsnNode = methodNode.instructions.getLast();
// setup interceptor variables start/end label.
this.methodNode.instructions.insertBefore(this.enterInsnNode, this.interceptorVariableStartLabelNode);
this.methodNode.instructions.insert(this.lastInsnNode, this.interceptorVariableEndLabelNode);
initInnerVariablePrefix();
}
public MethodProcessor(final String owner, final MethodNode methodNode) {
this(owner, methodNode, false);
}
private void initInnerVariablePrefix() {
String prefix = DEFAULT_INNER_VARIABLE_PREFIX;
int count = 0;
@ -157,15 +157,15 @@ public class MethodProcessor {
count++;
}
this.innerVariablePrefix = prefix;
returnVariableName = innerVariablePrefix + "_return";
throwVariableName = innerVariablePrefix + "_throw";
invokeArgsVariableName = innerVariablePrefix + "_invokeArgs";
monitorVariableName = innerVariablePrefix + "_monitor";
invokeReturnVariablePrefix = innerVariablePrefix + "_invokeReturn_";
}
private boolean existLocalVariableWithPrefix(String prefix) {
for (LocalVariableNode variableNode : this.methodNode.localVariables) {
if (variableNode.name.startsWith(prefix)) {
@ -196,16 +196,16 @@ public class MethodProcessor {
}
return invokeArgsVariableNode;
}
public LocalVariableNode initReturnVariableNode() {
if (returnVariableNode == null) {
returnVariableNode = this.addInterceptorLocalVariable(returnVariableName, returnType.getDescriptor());
}
return returnVariableNode;
}
/**
*
*
* @param name
* @param type
* @return
@ -219,7 +219,7 @@ public class MethodProcessor {
}
return variableNode;
}
public TryCatchBlock initTryCatchBlock() {
return initTryCatchBlock(THROWABLE_TYPE.getInternalName());
}
@ -232,7 +232,7 @@ public class MethodProcessor {
InsnList instructions = new InsnList();
AsmOpUtils.throwException(instructions);
this.methodNode.instructions.insert(tryCatchBlock.getEndLabelNode(), instructions);
tryCatchBlock.sort();
}
return tryCatchBlock;
@ -649,12 +649,12 @@ public class MethodProcessor {
* @param tmpToInlineMethodNode
*/
public void inline(String owner, MethodNode toInlineMethodNode) {
ListIterator<AbstractInsnNode> originMethodIter = this.methodNode.instructions.iterator();
while(originMethodIter.hasNext()) {
AbstractInsnNode originMethodInsnNode = originMethodIter.next();
if (originMethodInsnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) originMethodInsnNode;
if (methodInsnNode.owner.equals(owner) && methodInsnNode.name.equals(toInlineMethodNode.name)
@ -662,16 +662,16 @@ public class MethodProcessor {
// 要copy一份否则inline多次会出问题
MethodNode tmpToInlineMethodNode = AsmUtils.copy(toInlineMethodNode);
tmpToInlineMethodNode = AsmUtils.removeLineNumbers(tmpToInlineMethodNode);
LabelNode end = new LabelNode();
this.methodNode.instructions.insert(methodInsnNode, end);
InsnList instructions = new InsnList();
// 要先记录好当前的 maxLocals ,然后再依次把 栈上的 args保存起来 ,后面调整 VarInsnNode index里要加上当前的 maxLocals
// save args to local vars
int currentMaxLocals = this.nextLocals;
int off = (tmpToInlineMethodNode.access & Opcodes.ACC_STATIC) != 0 ? 0 : 1;
Type[] args = Type.getArgumentTypes(tmpToInlineMethodNode.desc);
int argsOff = off;
@ -682,12 +682,12 @@ public class MethodProcessor {
// 记录新的 maxLocals
this.nextLocals += argsOff;
methodNode.maxLocals = this.nextLocals;
for(int i = args.length - 1; i >= 0; --i) {
argsOff -= args[i].getSize();
// this.visitVarInsn(args[i].getOpcode(Opcodes.ISTORE), argsOff);
AsmOpUtils.storeVar(instructions, args[i], currentMaxLocals + argsOff);
}
@ -696,15 +696,15 @@ public class MethodProcessor {
// this.visitVarInsn(Opcodes.ASTORE, 0);
AsmOpUtils.storeVar(instructions, OBJECT_TYPE, currentMaxLocals);
}
ListIterator<AbstractInsnNode> inlineIterator = tmpToInlineMethodNode.instructions.iterator();
ListIterator<AbstractInsnNode> inlineIterator = tmpToInlineMethodNode.instructions.iterator();
while(inlineIterator.hasNext()) {
AbstractInsnNode abstractInsnNode = inlineIterator.next();
if(abstractInsnNode instanceof FrameNode) {
continue;
}
if(abstractInsnNode instanceof VarInsnNode) {
VarInsnNode varInsnNode = (VarInsnNode) abstractInsnNode;
varInsnNode.var += currentMaxLocals;
@ -716,12 +716,12 @@ public class MethodProcessor {
inlineIterator.remove();
instructions.add(new JumpInsnNode(Opcodes.GOTO, end));
continue;
}
}
inlineIterator.remove();
instructions.add(abstractInsnNode);
}
// 插入inline之后的代码再删除掉原来的 MethodInsnNode
this.methodNode.instructions.insertBefore(methodInsnNode, instructions);
originMethodIter.remove();
@ -735,7 +735,7 @@ public class MethodProcessor {
}
}
public void sortTryCatchBlock() {
if (this.methodNode.tryCatchBlocks == null) {
return;
@ -762,5 +762,5 @@ public class MethodProcessor {
this.methodNode.tryCatchBlocks.get(i).updateIndex(i);
}
}
}

@ -0,0 +1,125 @@
package com.taobao.arthas.bytekit.asm.inst.impl;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
import com.taobao.arthas.bytekit.utils.AsmUtils;
/**
*
* @author hengyunabc 2019-03-15
*
*/
public class InstrumentImpl {
public static MethodNode replaceInvokeOrigin(String originOwner, MethodNode originMethodNode,
MethodNode apmMethodNode) {
// 查找到所有的 InstrumentApi.invokeOrigin() 指令
List<MethodInsnNode> methodInsnNodes = AsmUtils.findMethodInsnNode(apmMethodNode,
"com/taobao/arthas/bytekit/asm/inst/InstrumentApi", "invokeOrigin", "()Ljava/lang/Object;");
Type originReturnType = Type.getMethodType(originMethodNode.desc).getReturnType();
for (MethodInsnNode methodInsnNode : methodInsnNodes) {
InsnList instructions = new InsnList();
AbstractInsnNode secondInsnNode = methodInsnNode.getNext();
// 如果是 非 static ,则要 load this
boolean isStatic = AsmUtils.isStatic(originMethodNode);
int opcode = isStatic ? Opcodes.INVOKESTATIC : Opcodes.INVOKEVIRTUAL;
if (!isStatic) {
AsmOpUtils.loadThis(instructions);
}
AsmOpUtils.loadArgs(instructions, originMethodNode);
MethodInsnNode originMethodInsnNode = new MethodInsnNode(opcode, originOwner, originMethodNode.name,
originMethodNode.desc, false);
// 调用原来的函数
instructions.add(originMethodInsnNode);
int sort = originReturnType.getSort();
if (sort == Type.VOID) {
if (secondInsnNode != null) {
if (secondInsnNode.getOpcode() == Opcodes.POP) {
// TODO 原来的函数没有返回值,这里要把 POP去掉。有没有可能是 POP2 ?
apmMethodNode.instructions.remove(secondInsnNode);
} else {
// TODO 原来函数没有返回值,这里有没有可能要赋值??是否要 push null?
AsmOpUtils.pushNUll(instructions);
}
}
} else if (sort >= Type.BOOLEAN && sort <= Type.DOUBLE) {
if (secondInsnNode.getOpcode() == Opcodes.POP) {
// 原来是 pop掉一个栈如果函数返回的是 long则要pop2
if (originReturnType.getSize() == 2) {
apmMethodNode.instructions.insert(secondInsnNode, new InsnNode(Opcodes.POP2));
apmMethodNode.instructions.remove(secondInsnNode);
}
} else {
/**
* castunbox
*
* <pre>
* CHECKCAST java/lang/Integer
* INVOKEVIRTUAL java/lang/Integer.intValue ()I
* </pre>
*/
boolean removeCheckCast = false;
if (secondInsnNode.getOpcode() == Opcodes.CHECKCAST) {
TypeInsnNode typeInsnNode = (TypeInsnNode) secondInsnNode;
// 从原始函数的返回值获取到它对应的自动box的类
Type boxedType = AsmOpUtils.getBoxedType(originReturnType);
if (Type.getObjectType(typeInsnNode.desc).equals(boxedType)) {
AbstractInsnNode thridInsnNode = secondInsnNode.getNext();
if (thridInsnNode != null && thridInsnNode.getOpcode() == Opcodes.INVOKEVIRTUAL) {
MethodInsnNode valueInsnNode = (MethodInsnNode) thridInsnNode;
Method unBoxMethod = AsmOpUtils.getUnBoxMethod(originReturnType);
if (unBoxMethod.getDescriptor().equals(valueInsnNode.desc)
&& valueInsnNode.owner.equals(boxedType.getInternalName())) {
apmMethodNode.instructions.remove(thridInsnNode);
apmMethodNode.instructions.remove(secondInsnNode);
removeCheckCast = true;
}
}
}
}
if (!removeCheckCast) {
// 没有被转换为原始类型也没有pop则说明赋值给了一个对象用类似Long.valudOf转换为Object
AsmOpUtils.box(instructions, originReturnType);
}
}
} else {// ARRAY/OBJECT
// 移掉可能有的 check cast
if (secondInsnNode.getOpcode() == Opcodes.CHECKCAST) {
TypeInsnNode typeInsnNode = (TypeInsnNode) secondInsnNode;
if (Type.getObjectType(typeInsnNode.desc).equals(originReturnType)) {
apmMethodNode.instructions.remove(secondInsnNode);
}
}
}
apmMethodNode.instructions.insertBefore(methodInsnNode, instructions);
apmMethodNode.instructions.remove(methodInsnNode);
}
MethodProcessor methodProcessor = new MethodProcessor(originOwner, apmMethodNode);
methodProcessor.inline(originOwner, originMethodNode);
return apmMethodNode;
}
}

@ -0,0 +1,52 @@
package com.taobao.arthas.bytekit.asm.inst.impl;
import org.objectweb.asm.Label;
import org.objectweb.asm.tree.MethodNode;
/**
*
* @author hengyunabc 2019-03-18
*
*/
public class MethodReplaceResult {
private boolean success;
private Label start;
private Label end;
private MethodNode methodNode;
public Label getStart() {
return start;
}
public void setStart(Label start) {
this.start = start;
}
public Label getEnd() {
return end;
}
public void setEnd(Label end) {
this.end = end;
}
public MethodNode getMethodNode() {
return methodNode;
}
public void setMethodNode(MethodNode methodNode) {
this.methodNode = methodNode;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
}

@ -37,11 +37,11 @@ public class AsmOpUtils {
private static final Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double");
public static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
public static final Type OBJECT_ARRAY_TYPE = Type.getType(Object[].class);
public static final Type STRING_TYPE = Type.getObjectType("java/lang/String");
public static final Type STRING_ARRAY_TYPE = Type.getType(String[].class);
private static final Type NUMBER_TYPE = Type.getObjectType("java/lang/Number");
@ -50,6 +50,10 @@ public class AsmOpUtils {
private static final Method CHAR_VALUE = Method.getMethod("char charValue()");
private static final Method BYTE_VALUE = Method.getMethod("byte byteValue()");
private static final Method SHORT_VALUE = Method.getMethod("short shortValue()");
private static final Method INT_VALUE = Method.getMethod("int intValue()");
private static final Method FLOAT_VALUE = Method.getMethod("float floatValue()");
@ -58,7 +62,7 @@ public class AsmOpUtils {
private static final Method DOUBLE_VALUE = Method.getMethod("double doubleValue()");
static Type getBoxedType(final Type type) {
public static Type getBoxedType(final Type type) {
switch (type.getSort()) {
case Type.BYTE:
return BYTE_TYPE;
@ -80,6 +84,28 @@ public class AsmOpUtils {
return type;
}
public static Method getUnBoxMethod(final Type type) {
switch (type.getSort()) {
case Type.BYTE:
return BYTE_VALUE;
case Type.BOOLEAN:
return BOOLEAN_VALUE;
case Type.SHORT:
return SHORT_VALUE;
case Type.CHAR:
return CHAR_VALUE;
case Type.INT:
return INT_VALUE;
case Type.FLOAT:
return FLOAT_VALUE;
case Type.LONG:
return LONG_VALUE;
case Type.DOUBLE:
return DOUBLE_VALUE;
}
throw new IllegalArgumentException(type + " is not a primitive type.");
}
public static void newInstance(final InsnList instructions, final Type type) {
instructions.add(new TypeInsnNode(Opcodes.NEW, type.getInternalName()));
}
@ -103,7 +129,11 @@ public class AsmOpUtils {
insnList.add(new LdcInsnNode(value));
}
}
public static void pushNUll(InsnList insnList) {
insnList.add(new InsnNode(Opcodes.ACONST_NULL));
}
/**
* @see org.objectweb.asm.tree.LdcInsnNode#cst
* @param value
@ -145,8 +175,8 @@ public class AsmOpUtils {
public static void dup2X2(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.DUP2_X2));
}
public static void pop(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.POP));
}
@ -157,14 +187,14 @@ public class AsmOpUtils {
public static void pop2(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.POP2));
}
public static void swap(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.SWAP));
}
/**
* Generates the instructions to swap the top two stack values.
*
*
* @param prev
* type of the top - 1 stack value.
* @param type
@ -226,6 +256,12 @@ public class AsmOpUtils {
.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, owner, method.getName(), method.getDescriptor(), false));
}
/**
*
* @param instructions
* @param type
* @see org.objectweb.asm.commons.GeneratorAdapter#unbox(Type)
*/
public static void unbox(final InsnList instructions, Type type) {
Type t = NUMBER_TYPE;
Method sig = null;
@ -262,7 +298,7 @@ public class AsmOpUtils {
sig.getDescriptor(), false));
}
}
public static boolean needBox(Type type) {
switch (type.getSort()) {
case Type.BYTE:
@ -305,6 +341,16 @@ public class AsmOpUtils {
instructions.add(new InsnNode(type.getOpcode(Opcodes.IALOAD)));
}
/**
* Generates the instruction to load 'this' on the stack.
* @see org.objectweb.asm.commons.GeneratorAdapter#loadThis()
* @param instructions
*/
public static void loadThis(final InsnList instructions) {
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
}
/**
* Generates the instructions to load all the method arguments on the stack,
* as a single object array.
@ -354,10 +400,10 @@ public class AsmOpUtils {
public static void storeVar(final InsnList instructions, Type type, final int index) {
instructions.add(new VarInsnNode(type.getOpcode(Opcodes.ISTORE), index));
}
/**
* Generates a type dependent instruction.
*
*
* @param opcode
* the instruction's opcode.
* @param type
@ -366,11 +412,11 @@ public class AsmOpUtils {
private static void typeInsn(final InsnList instructions, final int opcode, final Type type) {
instructions.add(new TypeInsnNode(opcode, type.getInternalName()));
}
/**
* Generates the instruction to check that the top stack value is of the
* given type.
*
*
* @param type
* a class or interface type.
*/
@ -379,15 +425,15 @@ public class AsmOpUtils {
typeInsn(instructions, Opcodes.CHECKCAST, type);
}
}
public static void throwException(final InsnList instructions) {
instructions.add(new InsnNode(Opcodes.ATHROW));
}
public static boolean isReturnCode(final int opcode) {
return opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN;
}
public static List<LocalVariableNode> validVariables(List<LocalVariableNode> localVariables,
AbstractInsnNode currentInsnNode) {
List<LocalVariableNode> results = new ArrayList<LocalVariableNode>();

@ -220,6 +220,22 @@ public class AsmUtils {
return result;
}
public static List<MethodInsnNode> findMethodInsnNode(MethodNode methodNode, String owner, String name,
String desc) {
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.owner.equals(owner) && methodInsnNode.name.equals(name)
&& methodInsnNode.desc.equals(desc)) {
result.add(methodInsnNode);
}
}
}
return result;
}
public static boolean isStatic(MethodNode methodNode) {
return (methodNode.access & Opcodes.ACC_STATIC) != 0;
}

@ -2,6 +2,7 @@ package com.taobao.arthas.bytekit.utils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
@ -11,44 +12,58 @@ import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type;
import org.objectweb.asm.util.CheckClassAdapter;
/**
*
* @author hengyunabc
*
*/
public class VerifyUtils {
public static void asmVerify(byte[] bytes) throws IOException {
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
ClassReader cr = new ClassReader(inputStream);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new CheckClassAdapter(cw);
public static void asmVerify(byte[] bytes) throws IOException {
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
ClassReader cr = new ClassReader(inputStream);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new CheckClassAdapter(cw);
cr.accept(cv, 0);
}
cr.accept(cv, 0);
}
public static Object instanceVerity(byte[] bytes) throws Exception {
String name = Type.getObjectType(AsmUtils.toClassNode(bytes).name).getClassName();
URLClassLoader systemClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
public static Object instanceVerity(byte[] bytes) throws Exception {
String name = Type.getObjectType(AsmUtils.toClassNode(bytes).name).getClassName();
@SuppressWarnings("resource")
ClassbyteClassLoader cl = new ClassbyteClassLoader(systemClassLoader.getURLs(),
ClassLoader.getSystemClassLoader().getParent());
URLClassLoader systemClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
cl.addClass(name, bytes);
@SuppressWarnings("resource")
ClassbyteClassLoader cl = new ClassbyteClassLoader(systemClassLoader.getURLs(),
ClassLoader.getSystemClassLoader().getParent());
Class<?> loadClass = cl.loadClass(name);
return loadClass.newInstance();
cl.addClass(name, bytes);
}
Class<?> loadClass = cl.loadClass(name);
return loadClass.newInstance();
}
public static class ClassbyteClassLoader extends URLClassLoader {
public ClassbyteClassLoader(URL[] urls, ClassLoader cl) {
super(urls, cl);
}
public static Object invoke(Object instance, String name, Object... args) throws Exception {
Method[] methods = instance.getClass().getMethods();
for (Method method : methods) {
if (name.contentEquals(method.getName())) {
return method.invoke(instance, args);
}
}
throw new NoSuchMethodError("name: " + name);
}
public Class<?> addClass(String name, byte[] bytes) throws ClassFormatError {
Class<?> cl = defineClass(name, bytes, 0, bytes.length);
resolveClass(cl);
public static class ClassbyteClassLoader extends URLClassLoader {
public ClassbyteClassLoader(URL[] urls, ClassLoader cl) {
super(urls, cl);
}
return cl;
}
}
public Class<?> addClass(String name, byte[] bytes) throws ClassFormatError {
Class<?> cl = defineClass(name, bytes, 0, bytes.length);
resolveClass(cl);
return cl;
}
}
}

@ -1,21 +1,18 @@
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 int returnInt(int i) {
System.out.println(new Object[] { i });
return 9998;
}
public static void main(String[] args) {
Date date = new Date(1551168643);
public static void onEnter(Object[] args) {
System.out.println(args);
}
System.err.println(date.toLocaleString());
public static int returnIntStatic(int i) {
return 9998;
}
}

@ -1,5 +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);
}
}

@ -1,5 +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 org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import 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);
}
}
Loading…
Cancel
Save