com.taobao.arthas.bytekit.asm.location.InvokeLocationMatcher Maven / Gradle / Ivy
The newest version!
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
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.asm.location.filter.LocationFilter;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
import com.taobao.arthas.bytekit.utils.MatchUtils;
/**
*
* @author hengyunabc
*
*/
public class InvokeLocationMatcher implements LocationMatcher {
/**
* the name of the method being invoked at the point where the trigger point
* should be inserted. maybe null, when null, match all method invoke.
*/
private String methodName;
/**
* the name of the type to which the method belongs or null if any type will do
*/
private String owner;
/**
* the method signature in externalised form, maybe null.
*/
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.
*/
private int count;
/**
* flag which is false if the trigger should be inserted before the method
* invocation is performed and true if it should be inserted after
*/
private boolean whenComplete;
/**
* wildcard matcher to exclude the invoke class, such as java.* to exclude jdk
* invoke.
*/
private List excludes = new ArrayList();
private boolean atInvokeExcpetionExit = false;
public InvokeLocationMatcher(String owner, String methodName, String desc, int count, boolean whenComplete,
List excludes, boolean atInvokeExcpetionExit) {
super();
this.owner = owner;
this.methodName = methodName;
this.desc = desc;
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 excludes) {
this(owner, methodName, desc, count, whenComplete, excludes, false);
}
public InvokeLocationMatcher(String owner, String methodName, String desc, int count, boolean whenComplete) {
this(owner, methodName, desc, count, whenComplete, new ArrayList());
}
@Override
public List match(MethodProcessor methodProcessor) {
if (this.atInvokeExcpetionExit) {
return matchForException(methodProcessor);
}
List locations = new ArrayList();
AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode();
LocationFilter locationFilter = methodProcessor.getLocationFilter();
LocationType locationType = whenComplete ? LocationType.INVOKE_COMPLETED : LocationType.INVOKE;
int matchedCount = 0;
while (insnNode != null) {
if (insnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
if (matchCall(methodInsnNode)) {
if(locationFilter.allow(methodInsnNode, locationType, this.whenComplete)) {
matchedCount++;
if (count <= 0 || count == matchedCount) {
InvokeLocation invokeLocation = new InvokeLocation(methodInsnNode, count, whenComplete);
locations.add(invokeLocation);
}
}
}
}
insnNode = insnNode.getNext();
}
return locations;
}
public List matchForException(MethodProcessor methodProcessor) {
List locations = new ArrayList();
AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode();
MethodNode methodNode = methodProcessor.getMethodNode();
List methodInsnNodes = new ArrayList();
LocationFilter locationFilter = methodProcessor.getLocationFilter();
LocationType locationType = LocationType.INVOKE_EXCEPTION_EXIT;
int matchedCount = 0;
while (insnNode != null) {
if (insnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
if (matchCall(methodInsnNode)) {
if(locationFilter.allow(methodInsnNode, locationType, this.whenComplete)) {
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()) {
if (!this.methodName.equals(methodInsnNode.name)) {
return false;
}
}
if (!excludes.isEmpty()) {
String ownerClassName = Type.getObjectType(methodInsnNode.owner).getClassName();
for (String exclude : excludes) {
if (MatchUtils.wildcardMatch(ownerClassName, exclude)) {
return false;
}
}
}
if (this.owner != null && !this.owner.equals(methodInsnNode.owner)) {
return false;
}
if (this.desc != null && !desc.equals(methodInsnNode.desc)) {
return false;
}
return true;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public boolean isWhenComplete() {
return whenComplete;
}
public void setWhenComplete(boolean whenComplete) {
this.whenComplete = whenComplete;
}
}