better invoke location support, support invoke method name/desc/owner

This commit is contained in:
hengyunabc 2020-05-03 03:35:37 +08:00
parent 4a936476cc
commit 3d3640e80e
8 changed files with 336 additions and 78 deletions

View File

@ -185,18 +185,51 @@ public abstract class Binding {
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = InvokeReturnBindingParser.class)
public static @interface InvokeReturn {
boolean optional() default false;
}
public static class InvokeReturnBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new InvokeReturnBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = InvokeMethodNameBindingParser.class)
public static @interface InvokeMethodName {
boolean optional() default false;
}
public static class InvokeMethodNameBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new InvokeMethodNameBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = InvokeMethodOwnerBindingParser.class)
public static @interface InvokeMethodOwner {
boolean optional() default false;
}
public static class InvokeMethodOwnerBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new InvokeMethodOwnerBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)

View File

@ -1,12 +1,11 @@
package com.taobao.arthas.bytekit.asm.binding;
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.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.taobao.arthas.bytekit.asm.location.MethodInsnNodeWare;
import com.taobao.arthas.bytekit.asm.location.Location;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
import com.taobao.arthas.bytekit.utils.AsmUtils;
/**
*
@ -17,15 +16,15 @@ public class InvokeMethodDeclarationBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
AbstractInsnNode insnNode = bindingContext.getLocation().getInsnNode();
if (insnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
String methodDeclaration = AsmUtils.methodDeclaration(methodInsnNode);
AsmOpUtils.push(instructions, methodDeclaration);
Location location = bindingContext.getLocation();
if (location instanceof MethodInsnNodeWare) {
MethodInsnNodeWare methodInsnNodeWare = (MethodInsnNodeWare) location;
MethodInsnNode methodInsnNode = methodInsnNodeWare.methodInsnNode();
AsmOpUtils.push(instructions, methodInsnNode.desc);
} else {
throw new IllegalArgumentException(
"InvokeMethodDeclarationBinding location is not MethodInsnNode, insnNode: " + insnNode);
"InvokeMethodDeclarationBinding location is not Invocation location, location: " + location);
}
}

View File

@ -0,0 +1,37 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.taobao.arthas.bytekit.asm.location.MethodInsnNodeWare;
import com.taobao.arthas.bytekit.asm.location.Location;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
/**
*
* @author hengyunabc
*
*/
public class InvokeMethodNameBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
Location location = bindingContext.getLocation();
if (location instanceof MethodInsnNodeWare) {
MethodInsnNodeWare methodInsnNodeWare = (MethodInsnNodeWare) location;
MethodInsnNode methodInsnNode = methodInsnNodeWare.methodInsnNode();
AsmOpUtils.push(instructions, methodInsnNode.name);
} else {
throw new IllegalArgumentException(
"InvokeMethodNameBinding location is not Invocation location, location: " + location);
}
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(String.class);
}
}

View File

@ -0,0 +1,37 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.taobao.arthas.bytekit.asm.location.MethodInsnNodeWare;
import com.taobao.arthas.bytekit.asm.location.Location;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
/**
*
* @author hengyunabc 2020-05-02
*
*/
public class InvokeMethodOwnerBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
Location location = bindingContext.getLocation();
if (location instanceof MethodInsnNodeWare) {
MethodInsnNodeWare methodInsnNodeWare = (MethodInsnNodeWare) location;
MethodInsnNode methodInsnNode = methodInsnNodeWare.methodInsnNode();
AsmOpUtils.push(instructions, methodInsnNode.owner);
} else {
throw new IllegalArgumentException(
"InvokeMethodOwnerBinding location is not Invocation location, location: " + location);
}
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(String.class);
}
}

View File

@ -1,17 +1,28 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
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.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.JumpInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LabelNode;
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.asm.TryCatchBlock;
import com.taobao.arthas.bytekit.asm.location.Location.InvokeExceptionExitLocation;
import com.taobao.arthas.bytekit.asm.location.Location.InvokeLocation;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
import com.taobao.arthas.bytekit.utils.MatchUtils;
/**
*
* @author hengyunabc
*
*/
public class InvokeLocationMatcher implements LocationMatcher {
/**
@ -21,8 +32,7 @@ public class InvokeLocationMatcher implements LocationMatcher {
private String methodName;
/**
* the name of the type to which the method belongs or null if any type will
* do
* the name of the type to which the method belongs or null if any type will do
*/
private String owner;
@ -32,8 +42,8 @@ public class InvokeLocationMatcher implements LocationMatcher {
private String desc;
/**
* count identifying which invocation should be taken as the trigger point.
* if not specified as a parameter this defaults to the first invocation.
* count identifying which invocation should be taken as the trigger point. if
* not specified as a parameter this defaults to the first invocation.
*/
private int count;
@ -44,12 +54,15 @@ public class InvokeLocationMatcher implements LocationMatcher {
private boolean whenComplete;
/**
* wildcard matcher to exclude class, such as java.* to exclude jdk invoke.
* wildcard matcher to exclude the invoke class, such as java.* to exclude jdk
* invoke.
*/
private List<String> excludes = new ArrayList<String>();
private boolean atInvokeExcpetionExit = false;
public InvokeLocationMatcher(String owner, String methodName, String desc, int count, boolean whenComplete,
List<String> excludes) {
List<String> excludes, boolean atInvokeExcpetionExit) {
super();
this.owner = owner;
this.methodName = methodName;
@ -57,6 +70,12 @@ public class InvokeLocationMatcher implements LocationMatcher {
this.count = count;
this.whenComplete = whenComplete;
this.excludes = excludes;
this.atInvokeExcpetionExit = atInvokeExcpetionExit;
}
public InvokeLocationMatcher(String owner, String methodName, String desc, int count, boolean whenComplete,
List<String> excludes) {
this(owner, methodName, desc, count, whenComplete, excludes, false);
}
public InvokeLocationMatcher(String owner, String methodName, String desc, int count, boolean whenComplete) {
@ -65,9 +84,12 @@ public class InvokeLocationMatcher implements LocationMatcher {
@Override
public List<Location> match(MethodProcessor methodProcessor) {
if (this.atInvokeExcpetionExit) {
return matchForException(methodProcessor);
}
List<Location> locations = new ArrayList<Location>();
AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode();
int matchedCount = 0;
while (insnNode != null) {
if (insnNode instanceof MethodInsnNode) {
@ -76,8 +98,7 @@ public class InvokeLocationMatcher implements LocationMatcher {
if (matchCall(methodInsnNode)) {
matchedCount++;
if (count <= 0 || count == matchedCount) {
InvokeLocation invokeLocation = new InvokeLocation(methodInsnNode, count,
whenComplete);
InvokeLocation invokeLocation = new InvokeLocation(methodInsnNode, count, whenComplete);
locations.add(invokeLocation);
}
}
@ -88,16 +109,64 @@ public class InvokeLocationMatcher implements LocationMatcher {
return locations;
}
public List<Location> matchForException(MethodProcessor methodProcessor) {
List<Location> locations = new ArrayList<Location>();
AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode();
MethodNode methodNode = methodProcessor.getMethodNode();
List<MethodInsnNode> methodInsnNodes = new ArrayList<MethodInsnNode>();
int matchedCount = 0;
while (insnNode != null) {
if (insnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
if (matchCall(methodInsnNode)) {
matchedCount++;
if (count <= 0 || count == matchedCount) {
methodInsnNodes.add(methodInsnNode);
}
}
}
insnNode = insnNode.getNext();
}
// insert try/catch
for (MethodInsnNode methodInsnNode : methodInsnNodes) {
TryCatchBlock tryCatchBlock = new TryCatchBlock(methodNode);
InsnList toInsert = new InsnList();
LabelNode gotoDest = new LabelNode();
LabelNode startLabelNode = tryCatchBlock.getStartLabelNode();
LabelNode endLabelNode = tryCatchBlock.getEndLabelNode();
toInsert.add(new JumpInsnNode(Opcodes.GOTO, gotoDest));
toInsert.add(endLabelNode);
AsmOpUtils.throwException(toInsert);
locations.add(new InvokeExceptionExitLocation(methodInsnNode, endLabelNode));
toInsert.add(gotoDest);
methodNode.instructions.insertBefore(methodInsnNode, startLabelNode);
methodNode.instructions.insert(methodInsnNode, toInsert);
tryCatchBlock.sort();
}
return locations;
}
private boolean matchCall(MethodInsnNode methodInsnNode) {
if(methodName == null || methodName.isEmpty()) {
return true;
if (methodName != null && !methodName.isEmpty()) {
if (!this.methodName.equals(methodInsnNode.name)) {
return false;
}
}
if (!this.methodName.equals(methodInsnNode.name)) {
return false;
}
if (!excludes.isEmpty()) {
String ownerClassName = Type.getObjectType(methodInsnNode.owner).getClassName();
for (String exclude : excludes) {

View File

@ -205,7 +205,7 @@ public abstract class Location {
/**
* location identifying a method invocation trigger point
*/
public static class InvokeLocation extends Location {
public static class InvokeLocation extends Location implements MethodInsnNodeWare {
/**
* count identifying which invocation should be taken as the trigger point. if
@ -216,7 +216,7 @@ public abstract class Location {
public InvokeLocation(MethodInsnNode insnNode, int count, boolean whenComplete) {
super(insnNode, whenComplete);
this.count = count;
this.stackNeedSave = true;
this.stackNeedSave = false;
}
@Override
@ -313,34 +313,43 @@ public abstract class Location {
boolean isStatic = AsmUtils.isStatic(methodInsnNode);
Type[] argumentTypes = methodType.getArgumentTypes();
// 如果是非static则存放到数组的index要多 1
AsmOpUtils.push(instructions, argumentTypes.length + (isStatic ? 0 : 1));
AsmOpUtils.newArray(instructions, AsmOpUtils.OBJECT_TYPE);
LocalVariableNode invokeArgsVariableNode = methodProcessor.initInvokeArgsVariableNode();
AsmOpUtils.storeVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
//保存到 var里再从var取出放到数组里
for(int i = argumentTypes.length - 1; i >= 0 ; --i) {
LocalVariableNode methodInvokeArgVar = methodProcessor.addInterceptorLocalVariable("__" + i, argumentTypes[i].getDescriptor());
AsmOpUtils.storeVar(instructions, argumentTypes[i], methodInvokeArgVar.index);
}
// 保存this到 var
LocalVariableNode methodInvokeThis = methodProcessor.addInterceptorLocalVariable("__invokemethodthis", AsmOpUtils.OBJECT_TYPE.getDescriptor());
AsmOpUtils.storeVar(instructions, AsmOpUtils.OBJECT_TYPE, methodInvokeThis.index);
// // 如果是非static则存放到数组的index要多 1
// AsmOpUtils.push(instructions, argumentTypes.length + (isStatic ? 0 : 1));
// AsmOpUtils.newArray(instructions, AsmOpUtils.OBJECT_TYPE);
// LocalVariableNode invokeArgsVariableNode = methodProcessor.initInvokeArgsVariableNode();
// AsmOpUtils.storeVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
// 从invoke的参数的后面一个个存到数组里
for(int i = argumentTypes.length - 1; i >= 0 ; --i) {
AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
AsmOpUtils.swap(instructions, argumentTypes[i], AsmOpUtils.OBJECT_ARRAY_TYPE);
// 如果是非static则存放到数组的index要多 1
AsmOpUtils.push(instructions, i + (isStatic ? 0 : 1));
AsmOpUtils.swap(instructions, argumentTypes[i], Type.INT_TYPE);
AsmOpUtils.box(instructions, argumentTypes[i]);
AsmOpUtils.arrayStore(instructions, AsmOpUtils.OBJECT_TYPE);
}
// for(int i = argumentTypes.length - 1; i >= 0 ; --i) {
// AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
//
// AsmOpUtils.swap(instructions, argumentTypes[i], AsmOpUtils.OBJECT_ARRAY_TYPE);
// // 如果是非static则存放到数组的index要多 1
// AsmOpUtils.push(instructions, i + (isStatic ? 0 : 1));
// AsmOpUtils.swap(instructions, argumentTypes[i], Type.INT_TYPE);
//
// AsmOpUtils.box(instructions, argumentTypes[i]);
// AsmOpUtils.arrayStore(instructions, AsmOpUtils.OBJECT_TYPE);
//
// }
// 处理this
if(!isStatic) {
AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
AsmOpUtils.swap(instructions, AsmOpUtils.OBJECT_TYPE, AsmOpUtils.OBJECT_ARRAY_TYPE);
AsmOpUtils.push(instructions, 0);
AsmOpUtils.swap(instructions, AsmOpUtils.OBJECT_TYPE, Type.INT_TYPE);
AsmOpUtils.arrayStore(instructions, AsmOpUtils.OBJECT_TYPE);
}
// if(!isStatic) {
// AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
//
// AsmOpUtils.swap(instructions, AsmOpUtils.OBJECT_TYPE, AsmOpUtils.OBJECT_ARRAY_TYPE);
// AsmOpUtils.push(instructions, 0);
// AsmOpUtils.swap(instructions, AsmOpUtils.OBJECT_TYPE, Type.INT_TYPE);
// AsmOpUtils.arrayStore(instructions, AsmOpUtils.OBJECT_TYPE);
// }
}else {
throw new IllegalArgumentException("location is not a InvokeLocation, location: " + location);
@ -363,25 +372,36 @@ public abstract class Location {
boolean isStatic = AsmUtils.isStatic(methodInsnNode);
Type[] argumentTypes = methodType.getArgumentTypes();
if(!isStatic) {
// 取出this
AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
AsmOpUtils.push(instructions, 0);
AsmOpUtils.arrayLoad(instructions, AsmOpUtils.OBJECT_TYPE);
AsmOpUtils.checkCast(instructions, Type.getObjectType(methodInsnNode.owner));
// 从var里取回this
LocalVariableNode methodInvokeThis = methodProcessor.findLocalVariableLabel("__invokemethodthis");
AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_TYPE, methodInvokeThis.index);
AsmOpUtils.checkCast(instructions, Type.getObjectType(methodInsnNode.owner));
// var里取出原来的数据放回栈上
for(int i = 0; i < argumentTypes.length; ++i) {
LocalVariableNode methodInvokeArgVar = methodProcessor.findLocalVariableLabel("__" + i);
AsmOpUtils.loadVar(instructions, argumentTypes[i], methodInvokeArgVar.index);
}
for(int i = 0; i < argumentTypes.length; ++i) {
AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
AsmOpUtils.push(instructions, i + (isStatic ? 0 : 1));
AsmOpUtils.arrayLoad(instructions, AsmOpUtils.OBJECT_TYPE);
// TODO 这里直接 unbox 就可以了unbox里带有 check cast
if(AsmOpUtils.needBox(argumentTypes[i])) {
AsmOpUtils.unbox(instructions, argumentTypes[i]);
}else {
AsmOpUtils.checkCast(instructions, argumentTypes[i]);
}
}
// if(!isStatic) {
// // 取出this
// AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
// AsmOpUtils.push(instructions, 0);
// AsmOpUtils.arrayLoad(instructions, AsmOpUtils.OBJECT_TYPE);
// AsmOpUtils.checkCast(instructions, Type.getObjectType(methodInsnNode.owner));
// }
//
// for(int i = 0; i < argumentTypes.length; ++i) {
// AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
// AsmOpUtils.push(instructions, i + (isStatic ? 0 : 1));
// AsmOpUtils.arrayLoad(instructions, AsmOpUtils.OBJECT_TYPE);
// // TODO 这里直接 unbox 就可以了unbox里带有 check cast
// if(AsmOpUtils.needBox(argumentTypes[i])) {
// AsmOpUtils.unbox(instructions, argumentTypes[i]);
// }else {
// AsmOpUtils.checkCast(instructions, argumentTypes[i]);
// }
// }
}else {
throw new IllegalArgumentException("location is not a InvokeLocation, location: " + location);
@ -399,6 +419,11 @@ public abstract class Location {
return stackSaver;
}
@Override
public MethodInsnNode methodInsnNode() {
return (MethodInsnNode) insnNode;
}
}
/**
@ -606,8 +631,7 @@ public abstract class Location {
/**
* location identifying a method exceptional exit trigger point
*/
public static class ExceptionExitLocation extends Location {
public static class ExceptionExitLocation extends Location{
public ExceptionExitLocation(AbstractInsnNode insnNode) {
super(insnNode, true);
stackNeedSave = true;
@ -643,4 +667,50 @@ public abstract class Location {
}
}
/**
* location identifying a method exceptional exit trigger point
*/
public static class InvokeExceptionExitLocation extends Location implements MethodInsnNodeWare {
private MethodInsnNode methodInsnNode;
public InvokeExceptionExitLocation(MethodInsnNode methodInsnNode, AbstractInsnNode insnNode) {
super(insnNode, true);
stackNeedSave = true;
this.methodInsnNode = methodInsnNode;
}
public LocationType getLocationType() {
return LocationType.INVOKE_EXCEPTION_EXIT;
}
public StackSaver getStackSaver() {
StackSaver stackSaver = new StackSaver() {
@Override
public void store(InsnList instructions, BindingContext bindingContext) {
LocalVariableNode throwVariableNode = bindingContext.getMethodProcessor().initThrowVariableNode();
AsmOpUtils.storeVar(instructions, Type.getType(Throwable.class), throwVariableNode.index);
}
@Override
public void load(InsnList instructions, BindingContext bindingContext) {
LocalVariableNode throwVariableNode = bindingContext.getMethodProcessor().initThrowVariableNode();
AsmOpUtils.loadVar(instructions, Type.getType(Throwable.class), throwVariableNode.index);
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(Throwable.class);
}
};
return stackSaver;
}
@Override
public MethodInsnNode methodInsnNode() {
return methodInsnNode;
}
}
}

View File

@ -46,6 +46,11 @@ public enum LocationType {
*
*/
INVOKE_COMPLETED,
/**
* method invoke exception
*/
INVOKE_EXCEPTION_EXIT,
/**
* synchronize operation
*

View File

@ -0,0 +1,8 @@
package com.taobao.arthas.bytekit.asm.location;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
public interface MethodInsnNodeWare {
public MethodInsnNode methodInsnNode();
}