/*
* Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.btrace.runtime;
import com.sun.btrace.runtime.instr.LineNumberInstrumentor;
import com.sun.btrace.runtime.instr.MethodReturnInstrumentor;
import com.sun.btrace.runtime.instr.SynchronizedInstrumentor;
import com.sun.btrace.runtime.instr.MethodCallInstrumentor;
import com.sun.btrace.runtime.instr.CatchInstrumentor;
import com.sun.btrace.runtime.instr.ErrorReturnInstrumentor;
import com.sun.btrace.runtime.instr.ThrowInstrumentor;
import com.sun.btrace.runtime.instr.FieldAccessInstrumentor;
import com.sun.btrace.runtime.instr.MethodInstrumentor;
import com.sun.btrace.runtime.instr.TypeCheckInstrumentor;
import com.sun.btrace.runtime.instr.ObjectAllocInstrumentor;
import com.sun.btrace.runtime.instr.ArrayAccessInstrumentor;
import com.sun.btrace.runtime.instr.ArrayAllocInstrumentor;
import com.sun.btrace.AnyType;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import static com.sun.btrace.org.objectweb.asm.Opcodes.*;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.Sampled;
import com.sun.btrace.annotations.Where;
import com.sun.btrace.org.objectweb.asm.AnnotationVisitor;
import com.sun.btrace.org.objectweb.asm.ClassVisitor;
import com.sun.btrace.org.objectweb.asm.Label;
import com.sun.btrace.org.objectweb.asm.MethodVisitor;
import com.sun.btrace.org.objectweb.asm.Opcodes;
import com.sun.btrace.org.objectweb.asm.Type;
import com.sun.btrace.org.objectweb.asm.tree.MethodNode;
import com.sun.btrace.util.templates.TemplateExpanderVisitor;
import java.util.regex.PatternSyntaxException;
import static com.sun.btrace.runtime.Constants.*;
import com.sun.btrace.util.LocalVariableHelperImpl;
import com.sun.btrace.util.LocalVariableHelper;
import com.sun.btrace.util.MethodID;
import com.sun.btrace.util.templates.impl.MethodTrackingExpander;
import java.util.Collection;
import java.util.LinkedList;
import java.util.TreeSet;
/**
* This instruments a probed class with BTrace probe
* action class.
*
* @author A. Sundararajan
*/
public class Instrumentor extends ClassVisitor {
private final BTraceProbe bcn;
private final Collection<OnMethod> applicableOnMethods;
private final Set<OnMethod> calledOnMethods = new HashSet<>();
private String className, superName;
static Instrumentor create(BTraceClassReader cr, BTraceProbe bcn, ClassVisitor cv) {
if (cr.isInterface()) {
// do not instrument interfaces
return null;
}
Collection<OnMethod> applicables = bcn.getApplicableHandlers(cr);
if (applicables != null && !applicables.isEmpty()) {
return new Instrumentor(bcn, applicables, cv);
}
return null;
}
private Instrumentor(BTraceProbe bcn, Collection<OnMethod> applicables, ClassVisitor cv) {
super(ASM5, cv);
this.bcn = bcn;
this.applicableOnMethods = applicables;
}
final public boolean hasMatch() {
return !calledOnMethods.isEmpty();
}
@Override
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
className = name;
this.superName = superName;
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, final String name,
final String desc, String signature, String[] exceptions) {
List<OnMethod> appliedOnMethods = new LinkedList<>();
if (applicableOnMethods.isEmpty() ||
(access & ACC_ABSTRACT) != 0 ||
name.startsWith(BTRACE_METHOD_PREFIX)) {
return super.visitMethod(access, name, desc, signature, exceptions);
}
for (OnMethod om : applicableOnMethods) {
if (om.getLocation().getValue() == Kind.LINE) {
appliedOnMethods.add(om);
} else {
String methodName = om.getMethod();
boolean regexMatch = om.isMethodRegexMatcher();
if (methodName.isEmpty()) {
methodName = ".*"; // match all the methods
regexMatch = true;
}
if (methodName.equals("#")) {
methodName = om.getTargetName(); // match just the same-named method
}
if (methodName.equals(name) &&
typeMatches(om.getType(), desc)) {
appliedOnMethods.add(om);
} else if (regexMatch) {
try {
if (name.matches(methodName) &&
typeMatches(om.getType(), desc)) {
appliedOnMethods.add(om);
}
} catch (PatternSyntaxException pse) {
reportPatternSyntaxException(name);
}
}
}
}
if (appliedOnMethods.isEmpty()) {
return super.visitMethod(access, name, desc, signature, exceptions);
}
MethodVisitor methodVisitor;
methodVisitor = super.visitMethod(access, name, desc, signature, exceptions);
LocalVariableHelperImpl lvs = new LocalVariableHelperImpl(methodVisitor, access, desc);
TemplateExpanderVisitor tse = new TemplateExpanderVisitor(
lvs, className, name, desc
);
LocalVariableHelper visitor = tse;
for (OnMethod om : appliedOnMethods) {
visitor = instrumentorFor(om, visitor, access, name, desc);
}
final int mAccess = access;
return new MethodVisitor(Opcodes.ASM5, (MethodVisitor)visitor) {
@Override
public AnnotationVisitor visitAnnotation(String annoDesc,
boolean visible) {
LocalVariableHelper visitor = (LocalVariableHelper)mv;
for (OnMethod om : applicableOnMethods) {
String extAnnoName = Type.getType(annoDesc).getClassName();
String annoName = om.getMethod();
if (om.isMethodAnnotationMatcher()) {
if (om.isMethodRegexMatcher()) {
try {
if (extAnnoName.matches(annoName)) {
visitor = instrumentorFor(om, visitor, mAccess, name, desc);
}
} catch (PatternSyntaxException pse) {
reportPatternSyntaxException(extAnnoName);
}
} else if (annoName.equals(extAnnoName)) {
visitor = instrumentorFor(om, visitor, mAccess, name, desc);
}
}
}
mv = (MethodVisitor)visitor;
return mv.visitAnnotation(annoDesc, visible);
}
};
}
private String getMethodOrFieldName(boolean fqn, int opcode, String owner, String name, String desc) {
StringBuilder mName = new StringBuilder();
if (fqn) {
switch (opcode) {
case Opcodes.INVOKEDYNAMIC: {
mName.append("dynamic");
break;
}
case Opcodes.INVOKEINTERFACE: {
mName.append("interface");
break;
}
case Opcodes.INVOKESPECIAL: {
mName.append("special");
break;
}
case Opcodes.INVOKESTATIC: {
mName.append("static");
break;
}
case Opcodes.INVOKEVIRTUAL: {
mName.append("virtual");
break;
}
case Opcodes.PUTSTATIC:
case Opcodes.GETSTATIC: {
mName.append("static field");
break;
}
case Opcodes.PUTFIELD:
case Opcodes.GETFIELD: {
mName.append("field");
break;
}
}
mName.append(' ');
mName.append(TypeUtils.descriptorToSimplified(desc, owner, name));
} else {
mName.append(name);
}
return mName.toString();
}
private LocalVariableHelper instrumentorFor(
final OnMethod om, LocalVariableHelper mv,
final int access, final String name, final String desc) {
final Location loc = om.getLocation();
final Where where = loc.getWhere();
final Type[] actionArgTypes = Type.getArgumentTypes(om.getTargetDescriptor());
final int numActionArgs = actionArgTypes.length;
switch (loc.getValue()) {
case ARRAY_GET:
// <editor-fold defaultstate="collapsed" desc="Array Get Instrumentor">
return new ArrayAccessInstrumentor(mv, className, superName, access, name, desc) {
int[] argsIndex = new int[]{Integer.MIN_VALUE, Integer.MIN_VALUE};
final private int INDEX_PTR = 0;
final private int INSTANCE_PTR = 1;
@Override
protected void onBeforeArrayLoad(int opcode) {
Type arrtype = TypeUtils.getArrayType(opcode);
Type retType = TypeUtils.getElementType(opcode);
if (!locationTypeMatches(loc, arrtype, retType)) return;
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getTargetInstanceParameter(), arrtype);
if (where == Where.AFTER) {
addExtraTypeInfo(om.getReturnParameter(), retType);
}
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[]{Type.INT_TYPE});
if (vr.isValid()) {
if (!vr.isAny()) {
asm.dup2();
argsIndex[INDEX_PTR] = storeNewLocal(Type.INT_TYPE);
argsIndex[INSTANCE_PTR] = storeNewLocal(arrtype);
}
Label l = levelCheckBefore(om, bcn.name);
if (where == Where.BEFORE) {
loadArguments(
localVarArg(vr.getArgIdx(INDEX_PTR), Type.INT_TYPE, argsIndex[INDEX_PTR]),
localVarArg(om.getTargetInstanceParameter(), OBJECT_TYPE, argsIndex[INSTANCE_PTR]),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
selfArg(om.getSelfParameter(), Type.getObjectType(className)));
invokeBTraceAction(asm, om);
}
if (l != null) {
mv.visitLabel(l);
}
}
}
@Override
protected void onAfterArrayLoad(int opcode) {
if (where == Where.AFTER) {
Type arrtype = TypeUtils.getArrayType(opcode);
Type retType = TypeUtils.getElementType(opcode);
if (!locationTypeMatches(loc, arrtype, retType)) return;
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getTargetInstanceParameter(), arrtype);
addExtraTypeInfo(om.getReturnParameter(), retType);
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[]{Type.INT_TYPE});
if (vr.isValid()) {
Label l = levelCheckAfter(om, bcn.name);
int retValIndex = -1;
Type actionArgRetType = om.getReturnParameter() != -1 ?
actionArgTypes[om.getReturnParameter()] :
Type.VOID_TYPE;
if (om.getReturnParameter() != -1) {
asm.dupArrayValue(opcode);
retValIndex = storeNewLocal(retType);
}
loadArguments(
localVarArg(vr.getArgIdx(INDEX_PTR), Type.INT_TYPE, argsIndex[INDEX_PTR]),
localVarArg(om.getTargetInstanceParameter(), OBJECT_TYPE, argsIndex[INSTANCE_PTR]),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
localVarArg(om.getReturnParameter(), retType, retValIndex, TypeUtils.isAnyType(actionArgRetType)),
selfArg(om.getSelfParameter(), Type.getObjectType(className)));
invokeBTraceAction(asm, om);
if (l != null) {
mv.visitLabel(l);
}
}
}
}
};// </editor-fold>
case ARRAY_SET:
// <editor-fold defaultstate="collapsed" desc="Array Set Instrumentor">
return new ArrayAccessInstrumentor(mv, className, superName, access, name, desc) {
int[] argsIndex = new int[]{-1, -1, -1, -1};
final private int INDEX_PTR = 0, VALUE_PTR = 1, INSTANCE_PTR = 2;
@Override
protected void onBeforeArrayStore(int opcode) {
Type elementType = TypeUtils.getElementType(opcode);
Type arrayType = TypeUtils.getArrayType(opcode);
if (!locationTypeMatches(loc, arrayType, elementType)) return;
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getTargetInstanceParameter(), arrayType);
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[]{Type.INT_TYPE, elementType});
if (vr.isValid()) {
Type argElementType = Type.VOID_TYPE;
if (!vr.isAny()) {
int elementIdx = vr.getArgIdx(VALUE_PTR);
argElementType = elementIdx > -1 ?
actionArgTypes[elementIdx] :
Type.VOID_TYPE;
argsIndex[VALUE_PTR] = storeNewLocal(elementType);
asm.dup2();
argsIndex[INDEX_PTR] = storeNewLocal(Type.INT_TYPE);
argsIndex[INSTANCE_PTR] = storeNewLocal(TypeUtils.getArrayType(opcode));
asm.loadLocal(elementType, argsIndex[VALUE_PTR]);
}
Label l = levelCheckBefore(om, bcn.name);
if (where == Where.BEFORE) {
loadArguments(
localVarArg(om.getTargetInstanceParameter(), arrayType, argsIndex[INSTANCE_PTR]),
localVarArg(vr.getArgIdx(INDEX_PTR), Type.INT_TYPE, argsIndex[INDEX_PTR]),
localVarArg(vr.getArgIdx(VALUE_PTR), elementType, argsIndex[VALUE_PTR], TypeUtils.isAnyType(argElementType)),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
selfArg(om.getSelfParameter(), Type.getObjectType(className)));
invokeBTraceAction(asm, om);
}
if (l != null) {
mv.visitLabel(l);
}
}
}
@Override
protected void onAfterArrayStore(int opcode) {
if (where == Where.AFTER) {
Type elementType = TypeUtils.getElementType(opcode);
Type arrayType = TypeUtils.getArrayType(opcode);
if (!locationTypeMatches(loc, arrayType, elementType)) return;
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getTargetInstanceParameter(), arrayType);
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[]{Type.INT_TYPE, elementType});
if (vr.isValid()) {
int elementIdx = vr.getArgIdx(VALUE_PTR);
Type argElementType = elementIdx > -1 ?
actionArgTypes[elementIdx] :
Type.VOID_TYPE;
Label l = levelCheckAfter(om, bcn.name);
loadArguments(
localVarArg(om.getTargetInstanceParameter(), arrayType, argsIndex[INSTANCE_PTR]),
localVarArg(vr.getArgIdx(INDEX_PTR), Type.INT_TYPE, argsIndex[INDEX_PTR]),
localVarArg(vr.getArgIdx(VALUE_PTR), elementType, argsIndex[VALUE_PTR], TypeUtils.isAnyType(argElementType)),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
isStatic() ? constArg(om.getSelfParameter(), null) :
localVarArg(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(asm, om);
if (l != null) {
mv.visitLabel(l);
}
}
}
}
};// </editor-fold>
case CALL:
// <editor-fold defaultstate="collapsed" desc="Method Call Instrumentor">
return new MethodCallInstrumentor(mv, className, superName, access, name, desc) {
private final String localClassName = loc.getClazz();
private final String localMethodName = loc.getMethod();
private int returnVarIndex = -1;
int[] backupArgsIndices;
private boolean generatingCode = false;
private void injectBtrace(ValidationResult vr, final String method, final Type[] callArgTypes, final Type returnType, final boolean staticCall) {
ArgumentProvider[] actionArgs = new ArgumentProvider[actionArgTypes.length + 7];
for(int i=0;i<vr.getArgCnt();i++) {
int index = vr.getArgIdx(i);
Type t = actionArgTypes[index];
if (TypeUtils.isAnyTypeArray(t)) {
if (i < backupArgsIndices.length - 1) {
actionArgs[i] = anytypeArg(index, backupArgsIndices[i+1], callArgTypes);
} else {
actionArgs[i] = new ArgumentProvider(asm, index) {
@Override
protected void doProvide() {
asm.push(0)
.newArray(OBJECT_TYPE);
}
};
}
} else {
actionArgs[i] = localVarArg(index, actionArgTypes[index], backupArgsIndices[i+1]);
}
}
actionArgs[actionArgTypes.length] = localVarArg(om.getReturnParameter(), returnType, returnVarIndex);
actionArgs[actionArgTypes.length + 1] = staticCall ? constArg(om.getTargetInstanceParameter(), null) :
localVarArg(om.getTargetInstanceParameter(), OBJECT_TYPE, backupArgsIndices.length == 0 ? -1 : backupArgsIndices[0]);
actionArgs[actionArgTypes.length + 2] = constArg(om.getTargetMethodOrFieldParameter(), method);
actionArgs[actionArgTypes.length + 3] = constArg(om.getClassNameParameter(), className.replace('/', '.'));
actionArgs[actionArgTypes.length + 4] = constArg(om.getMethodParameter(), getName(om.isMethodFqn()));
actionArgs[actionArgTypes.length + 5] = selfArg(om.getSelfParameter(), Type.getObjectType(className));
actionArgs[actionArgTypes.length + 6] = new ArgumentProvider(asm, om.getDurationParameter()) {
@Override
public void doProvide() {
MethodTrackingExpander.DURATION.insert(mv);
}
};
loadArguments(actionArgs);
invokeBTraceAction(asm, om);
}
@Override
protected void onBeforeCallMethod(int opcode, String cOwner, String cName, String cDesc) {
if (matches(localClassName, cOwner.replace('/', '.'))
&& matches(localMethodName, cName)
&& typeMatches(loc.getType(), cDesc)) {
/*
* Generate a synthetic method id for the method call.
* It will be diferent from the 'native' method id
* in order to prevent double accounting for one hit.
*/
int parentMid = MethodID.getMethodId(className, name, desc);
int mid = MethodID.getMethodId("c$" + parentMid + "$" + cOwner, cName, cDesc);
String method = getMethodOrFieldName(om.isTargetMethodOrFieldFqn(), opcode, cOwner, cName, cDesc);
Type[] calledMethodArgs = Type.getArgumentTypes(cDesc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getTargetInstanceParameter(), Type.getObjectType(cOwner));
if (where == Where.AFTER) {
addExtraTypeInfo(om.getReturnParameter(), Type.getReturnType(cDesc));
}
ValidationResult vr = validateArguments(om, actionArgTypes, calledMethodArgs);
if (vr.isValid()) {
boolean isStaticCall = (opcode == INVOKESTATIC);
if (!isStaticCall) {
if (where == Where.BEFORE && cName.equals(CONSTRUCTOR)) {
return;
}
}
if (!generatingCode) {
try {
generatingCode = true;
if (om.getDurationParameter() != -1) {
MethodTrackingExpander.ENTRY.insert(mv,
MethodTrackingExpander.$MEAN +
"=" +
om.getSamplerMean(),
MethodTrackingExpander.$SAMPLER +
"=" +
om.getSamplerKind(),
MethodTrackingExpander.$TIMED,
MethodTrackingExpander.$METHODID +
"=" +
mid,
MethodTrackingExpander.$LEVEL +
"=" + getLevelStrSafe(om)
);
} else {
MethodTrackingExpander.ENTRY.insert(mv,
MethodTrackingExpander.$MEAN +
"=" +
om.getSamplerMean(),
MethodTrackingExpander.$SAMPLER +
"=" +
om.getSamplerKind(),
MethodTrackingExpander.$METHODID +
"=" +
mid,
MethodTrackingExpander.$LEVEL +
"=" + getLevelStrSafe(om)
);
}
} finally {
generatingCode = false;
}
}
Type[] argTypes = Type.getArgumentTypes(cDesc);
boolean shouldBackup = !vr.isAny() || om.getTargetInstanceParameter() != -1;
// will store the call args into local variables
backupArgsIndices = shouldBackup ? backupStack(argTypes, isStaticCall) : new int[0];
if (where == Where.BEFORE) {
MethodTrackingExpander.TEST_SAMPLE.insert(mv,
MethodTrackingExpander.$METHODID +
"=" +
mid
);
Label l = levelCheckBefore(om, bcn.name);
injectBtrace(vr, method, argTypes, Type.getReturnType(cDesc), isStaticCall);
if (l != null) {
mv.visitLabel(l);
}
MethodTrackingExpander.ELSE_SAMPLE.insert(mv);
}
// put the call args back on stack so the method call can find them
if (shouldBackup) {
restoreStack(backupArgsIndices, argTypes, isStaticCall);
}
}
}
}
@Override
protected void onAfterCallMethod(int opcode,
String cOwner, String cName, String cDesc) {
if (matches(localClassName, cOwner.replace('/', '.'))
&& matches(localMethodName, cName)
&& typeMatches(loc.getType(), cDesc)) {
int parentMid = MethodID.getMethodId(className, name, desc);
int mid = MethodID.getMethodId("c$" + parentMid + "$" + cOwner, cName, cDesc);
Type returnType = Type.getReturnType(cDesc);
Type[] calledMethodArgs = Type.getArgumentTypes(cDesc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getTargetInstanceParameter(), Type.getObjectType(cOwner));
addExtraTypeInfo(om.getReturnParameter(), returnType);
ValidationResult vr = validateArguments(om, actionArgTypes, calledMethodArgs);
if (vr.isValid()) {
if (om.getDurationParameter() == -1) {
MethodTrackingExpander.EXIT.insert(
mv, MethodTrackingExpander.$METHODID
+ "=" + mid
);
}
if (where == Where.AFTER) {
if (om.getDurationParameter() != -1) {
MethodTrackingExpander.TEST_SAMPLE.insert(mv,
MethodTrackingExpander.$TIMED,
MethodTrackingExpander.$METHODID
+ "="
+ mid
);
} else {
MethodTrackingExpander.TEST_SAMPLE.insert(mv,
MethodTrackingExpander.$METHODID
+ "="
+ mid
);
}
Label l = levelCheckAfter(om, bcn.name);
String method = getMethodOrFieldName(om.isTargetMethodOrFieldFqn(), opcode, cOwner, cName, cDesc);;
boolean withReturn = om.getReturnParameter() != -1 && !returnType.equals(Type.VOID_TYPE);
if (withReturn) {
// store the return value to a local variable
int index = storeNewLocal(returnType);
returnVarIndex = index;
}
// will also retrieve the call args and the return value from the backup variables
injectBtrace(vr, method, calledMethodArgs, returnType, opcode == Opcodes.INVOKESTATIC);
if (withReturn) {
if (Type.getReturnType(om.getTargetDescriptor()).getSort() == Type.VOID) {
asm.loadLocal(returnType, returnVarIndex); // restore the return value
}
}
if (l != null) {
mv.visitLabel(l);
}
MethodTrackingExpander.ELSE_SAMPLE.insert(mv);
if (this.parent == null) {
MethodTrackingExpander.RESET.insert(mv);
}
}
}
}
}
};// </editor-fold>
case CATCH:
// <editor-fold defaultstate="collapsed" desc="Catch Instrumentor">
return new CatchInstrumentor(mv, className, superName, access, name, desc) {
@Override
protected void onCatch(String type) {
Type exctype = Type.getObjectType(type);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[]{exctype});
if (vr.isValid()) {
int index = -1;
Label l = levelCheck(om, bcn.name);
if (!vr.isAny()) {
asm.dup();
index = storeNewLocal(exctype);
}
loadArguments(
localVarArg(vr.getArgIdx(0), exctype, index),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
selfArg(om.getSelfParameter(), Type.getObjectType(className)));
invokeBTraceAction(asm, om);
if (l != null) {
mv.visitLabel(l);
}
}
}
};// </editor-fold>
case CHECKCAST:
// <editor-fold defaultstate="collapsed" desc="CheckCast Instrumentor">
return new TypeCheckInstrumentor(mv, className, superName, access, name, desc) {
private void callAction(int opcode, String desc) {
if (opcode == Opcodes.CHECKCAST) {
Type castType = Type.getObjectType(desc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getTargetInstanceParameter(), OBJECT_TYPE);
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[]{STRING_TYPE});
if (vr.isValid()) {
int castTypeIndex = -1;
Label l = levelCheck(om, bcn.name);
if (!vr.isAny()) {
asm.dup();
castTypeIndex = storeNewLocal(castType);
}
loadArguments(
constArg(vr.getArgIdx(0), castType.getClassName()),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
selfArg(om.getSelfParameter(), Type.getObjectType(className)),
localVarArg(om.getTargetInstanceParameter(), OBJECT_TYPE, castTypeIndex));
invokeBTraceAction(asm, om);
if (l != null) {
mv.visitLabel(l);
}
}
}
}
@Override
protected void onBeforeTypeCheck(int opcode, String desc) {
if (where == Where.BEFORE) {
callAction(opcode, desc);
}
}
@Override
protected void onAfterTypeCheck(int opcode, String desc) {
if (where == Where.AFTER) {
callAction(opcode, desc);
}
}
};// </editor-fold>
case ENTRY:
// <editor-fold defaultstate="collapsed" desc="Method Entry Instrumentor">
return new MethodReturnInstrumentor(mv, className, superName, access, name, desc) {
private final ValidationResult vr;
{
Type[] calledMethodArgs = Type.getArgumentTypes(getDescriptor());
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
vr = validateArguments(om, actionArgTypes, calledMethodArgs);
}
private void injectBtrace() {
loadArguments(
vr, actionArgTypes, isStatic(),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
selfArg(om.getSelfParameter(), Type.getObjectType(className))
);
invokeBTraceAction(asm, om);
}
@Override
protected void visitMethodPrologue() {
if (vr.isValid() || vr.isAny()) {
if (om.getSamplerKind() != Sampled.Sampler.None) {
MethodTrackingExpander.ENTRY.insert(mv,
MethodTrackingExpander.$SAMPLER +
"=" + om.getSamplerKind(),
MethodTrackingExpander.$MEAN +
"=" + om.getSamplerMean(),
MethodTrackingExpander.$LEVEL +
"=" + getLevelStrSafe(om)
);
}
}
super.visitMethodPrologue();
}
@Override
protected void onMethodEntry() {
if (vr.isValid() || vr.isAny()) {
if (om.getSamplerKind() != Sampled.Sampler.None) {
MethodTrackingExpander.TEST_SAMPLE.insert(mv, MethodTrackingExpander.$TIMED);
}
Label l = levelCheck(om, bcn.name);
if (numActionArgs == 0) {
invokeBTraceAction(asm, om);
} else {
injectBtrace();
}
if (l != null) {
mv.visitLabel(l);
}
if (om.getSamplerKind() != Sampled.Sampler.None) {
MethodTrackingExpander.ELSE_SAMPLE.insert(mv);
}
}
}
@Override
protected void onMethodReturn(int opcode) {
if (vr.isValid() || vr.isAny()) {
if (om.getSamplerKind() == Sampled.Sampler.Adaptive) {
MethodTrackingExpander.EXIT.insert(mv);
}
}
}
};// </editor-fold>
case ERROR:
// <editor-fold defaultstate="collapsed" desc="Error Instrumentor">
ErrorReturnInstrumentor eri = new ErrorReturnInstrumentor(mv, className, superName, access, name, desc) {
ValidationResult vr;
{
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
vr = validateArguments(om, actionArgTypes, new Type[]{THROWABLE_TYPE});
}
@Override
protected void onErrorReturn() {
if (vr.isValid()) {
int throwableIndex = -1;
MethodTrackingExpander.TEST_SAMPLE.insert(mv, MethodTrackingExpander.$TIMED);
if (!vr.isAny()) {
asm.dup();
throwableIndex = storeNewLocal(THROWABLE_TYPE);
}
ArgumentProvider[] actionArgs = new ArgumentProvider[5];
actionArgs[0] = localVarArg(vr.getArgIdx(0), THROWABLE_TYPE, throwableIndex);
actionArgs[1] = constArg(om.getClassNameParameter(), className.replace('/', '.'));
actionArgs[2] = constArg(om.getMethodParameter(), getName(om.isMethodFqn()));
actionArgs[3] = selfArg(om.getSelfParameter(), Type.getObjectType(className));
actionArgs[4] = new ArgumentProvider(asm, om.getDurationParameter()) {
@Override
public void doProvide() {
MethodTrackingExpander.DURATION.insert(mv);
}
};
Label l = levelCheck(om, bcn.name);
loadArguments(actionArgs);
invokeBTraceAction(asm, om);
if (l != null) {
mv.visitLabel(l);
}
MethodTrackingExpander.ELSE_SAMPLE.insert(mv);
}
}
private boolean generatingCode = false;
@Override
protected void visitMethodPrologue() {
if (vr.isValid()) {
if (!generatingCode) {
try {
generatingCode = true;
if (om.getDurationParameter() != -1) {
MethodTrackingExpander.ENTRY.insert(mv,
MethodTrackingExpander.$MEAN +
"=" +
om.getSamplerMean(),
MethodTrackingExpander.$SAMPLER +
"=" +
om.getSamplerKind(),
MethodTrackingExpander.$TIMED,
MethodTrackingExpander.$LEVEL +
"=" + getLevelStrSafe(om)
);
} else {
MethodTrackingExpander.ENTRY.insert(mv,
MethodTrackingExpander.$MEAN +
"=" +
om.getSamplerMean(),
MethodTrackingExpander.$SAMPLER +
"=" +
om.getSamplerKind(),
MethodTrackingExpander.$LEVEL +
"=" + getLevelStrSafe(om)
);
}
} finally {
generatingCode = false;
}
}
}
super.visitMethodPrologue();
}
};
return eri;
// </editor-fold>
case FIELD_GET:
// <editor-fold defaultstate="collapsed" desc="Field Get Instrumentor">
return new FieldAccessInstrumentor(mv, className, superName, access, name, desc) {
int calledInstanceIndex = Integer.MIN_VALUE;
private final String targetClassName = loc.getClazz();
private final String targetFieldName = loc.getField();
@Override
protected void onBeforeGetField(int opcode, String owner,
String name, String desc) {
if (matches(targetClassName, owner.replace('/', '.'))
&& matches(targetFieldName, name)) {
Type fldType = Type.getType(desc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getTargetInstanceParameter(), Type.getObjectType(owner));
if (where == Where.AFTER) {
addExtraTypeInfo(om.getReturnParameter(), fldType);
}
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[0]);
if (vr.isValid()) {
if (!isStaticAccess && om.getTargetInstanceParameter() != -1) {
asm.dup();
calledInstanceIndex = storeNewLocal(OBJECT_TYPE);
}
Label l = levelCheckBefore(om, bcn.name);
if (where == Where.BEFORE) {
loadArguments(
isStaticAccess ? constArg(om.getTargetInstanceParameter(), null) :
localVarArg(om.getTargetInstanceParameter(), OBJECT_TYPE, calledInstanceIndex),
constArg(om.getTargetMethodOrFieldParameter(), getMethodOrFieldName(
om.isTargetMethodOrFieldFqn(),
opcode,
targetClassName,
targetFieldName,
desc
)),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
selfArg(om.getSelfParameter(), Type.getObjectType(className)));
invokeBTraceAction(asm, om);
}
if (l != null) {
mv.visitLabel(l);
}
}
}
}
@Override
protected void onAfterGetField(int opcode, String owner,
String name, String desc) {
if (where == Where.AFTER
&& matches(targetClassName, owner.replace('/', '.'))
&& matches(targetFieldName, name)) {
Type fldType = Type.getType(desc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getTargetInstanceParameter(), Type.getObjectType(owner));
addExtraTypeInfo(om.getReturnParameter(), fldType);
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[0]);
if (vr.isValid()) {
int returnValIndex = -1;
Label l = levelCheckAfter(om, bcn.name);
if (om.getReturnParameter() != -1) {
asm.dupValue(desc);
returnValIndex = storeNewLocal(fldType);
}
loadArguments(
isStaticAccess ? constArg(om.getTargetInstanceParameter(), null) :
localVarArg(om.getTargetInstanceParameter(), OBJECT_TYPE, calledInstanceIndex),
constArg(om.getTargetMethodOrFieldParameter(), getMethodOrFieldName(
om.isTargetMethodOrFieldFqn(),
opcode,
targetClassName,
targetFieldName,
desc
)),
localVarArg(om.getReturnParameter(), fldType, returnValIndex),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
selfArg(om.getSelfParameter(), Type.getObjectType(className)));
invokeBTraceAction(asm, om);
if (l != null) {
mv.visitLabel(l);
}
}
}
}
};// </editor-fold>
case FIELD_SET:
// <editor-fold defaultstate="collapsed" desc="Field Set Instrumentor">
return new FieldAccessInstrumentor(mv, className, superName, access, name, desc) {
private final String targetClassName = loc.getClazz();
private final String targetFieldName = loc.getField();
private int calledInstanceIndex = Integer.MIN_VALUE;
private int fldValueIndex = -1;
@Override
protected void onBeforePutField(int opcode, String owner,
String name, String desc) {
if (matches(targetClassName, owner.replace('/', '.'))
&& matches(targetFieldName, name)) {
Type fieldType = Type.getType(desc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getTargetInstanceParameter(), Type.getObjectType(owner));
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[]{fieldType});
if (vr.isValid()) {
if (!vr.isAny()) {
// store the field value
fldValueIndex = storeNewLocal(fieldType);
}
if (!isStaticAccess && om.getTargetInstanceParameter() != -1) {
asm.dup();
calledInstanceIndex = storeNewLocal(OBJECT_TYPE);
}
if (!vr.isAny()) {
// need to put the set value back on stack
asm.loadLocal(fieldType, fldValueIndex);
}
Label l = levelCheckBefore(om, bcn.name);
if (where == Where.BEFORE) {
loadArguments(
localVarArg(vr.getArgIdx(0), fieldType, fldValueIndex),
isStaticAccess ? constArg(om.getTargetInstanceParameter(), null) :
localVarArg(om.getTargetInstanceParameter(), OBJECT_TYPE, calledInstanceIndex),
constArg(om.getTargetMethodOrFieldParameter(), getMethodOrFieldName(
om.isTargetMethodOrFieldFqn(),
opcode,
targetClassName,
targetFieldName,
desc
)),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
selfArg(om.getSelfParameter(), Type.getObjectType(className)));
invokeBTraceAction(asm, om);
}
if (l != null) {
mv.visitLabel(l);
}
}
}
}
@Override
protected void onAfterPutField(int opcode,
String owner, String name, String desc) {
if (where == Where.AFTER
&& matches(targetClassName, owner.replace('/', '.'))
&& matches(targetFieldName, name)) {
Type fieldType = Type.getType(desc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getTargetInstanceParameter(), Type.getObjectType(owner));
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[]{fieldType});
if (vr.isValid()) {
Label l = levelCheckAfter(om, bcn.name);
loadArguments(
localVarArg(vr.getArgIdx(0), fieldType, fldValueIndex),
isStaticAccess ? constArg(om.getTargetInstanceParameter(), null) :
localVarArg(om.getTargetInstanceParameter(), OBJECT_TYPE, calledInstanceIndex),
constArg(om.getTargetMethodOrFieldParameter(), getMethodOrFieldName(
om.isTargetMethodOrFieldFqn(),
opcode,
targetClassName,
targetFieldName,
desc
)),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
selfArg(om.getSelfParameter(), Type.getObjectType(className)));
invokeBTraceAction(asm, om);
if (l != null) {
mv.visitLabel(l);
}
}
}
}
};// </editor-fold>
case INSTANCEOF:
// <editor-fold defaultstate="collapsed" desc="InstanceOf Instrumentor">
return new TypeCheckInstrumentor(mv, className, superName, access, name, desc) {
ValidationResult vr;
Type castType = OBJECT_TYPE;
int castTypeIndex = -1;
{
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getTargetInstanceParameter(), OBJECT_TYPE);
}
private void callAction(String cName) {
if (vr.isValid()) {
Label l = levelCheck(om, bcn.name);
loadArguments(
constArg(vr.getArgIdx(0), cName),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
selfArg(om.getSelfParameter(), Type.getObjectType(className)),
localVarArg(om.getTargetInstanceParameter(), OBJECT_TYPE, castTypeIndex));
invokeBTraceAction(asm, om);
if (l != null) {
mv.visitLabel(l);
}
}
}
@Override
protected void onBeforeTypeCheck(int opcode, String desc) {
if (opcode == Opcodes.INSTANCEOF) {
castType = Type.getObjectType(desc);
vr = validateArguments(om, actionArgTypes, new Type[]{STRING_TYPE});
if (vr.isValid()) {
if (!vr.isAny()) {
asm.dup();
castTypeIndex = storeNewLocal(castType);
}
if (where == Where.BEFORE) {
callAction(castType.getClassName());
}
}
}
}
@Override
protected void onAfterTypeCheck(int opcode, String desc) {
if (opcode == Opcodes.INSTANCEOF) {
castType = Type.getObjectType(desc);
vr = validateArguments(om, actionArgTypes, new Type[]{STRING_TYPE});
if (vr.isValid()) {
if (where == Where.AFTER) {
callAction(castType.getClassName());
}
}
}
}
};// </editor-fold>
case LINE:
// <editor-fold defaultstate="collapsed" desc="Line Instrumentor">
return new LineNumberInstrumentor(mv, className, superName, access, name, desc) {
private final int onLine = loc.getLine();
private void callOnLine(int line) {
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[]{Type.INT_TYPE});
if (vr.isValid()) {
Label l = levelCheck(om, bcn.name);
loadArguments(
constArg(vr.getArgIdx(0), line),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
selfArg(om.getSelfParameter(), Type.getObjectType(className)));
invokeBTraceAction(asm, om);
if (l != null) {
mv.visitLabel(l);
}
}
}
@Override
protected void onBeforeLine(int line) {
if ((line == onLine || onLine == -1)
&& where == Where.BEFORE) {
callOnLine(line);
}
}
@Override
protected void onAfterLine(int line) {
if ((line == onLine || onLine == -1)
&& where == Where.AFTER) {
callOnLine(line);
}
}
};// </editor-fold>
case NEW:
// <editor-fold defaultstate="collapsed" desc="New Instance Instrumentor">
return new ObjectAllocInstrumentor(mv, className, superName, access, name, desc, om.getReturnParameter() != -1) {
@Override
protected void beforeObjectNew(String desc) {
if (loc.getWhere() == Where.BEFORE) {
String extName = desc.replace('/', '.');
if (matches(loc.getClazz(), extName)) {
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[]{STRING_TYPE});
if (vr.isValid()) {
Label l = levelCheck(om, bcn.name);
loadArguments(
constArg(vr.getArgIdx(0), extName),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
selfArg(om.getSelfParameter(), Type.getObjectType(className)));
invokeBTraceAction(asm, om);
if (l != null) {
mv.visitLabel(l);
}
}
}
}
}
@Override
protected void afterObjectNew(String desc) {
if (loc.getWhere() == Where.AFTER) {
String extName = desc.replace('/', '.');
if (matches(loc.getClazz(), extName)) {
Type instType = Type.getObjectType(desc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getReturnParameter(), instType);
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[]{STRING_TYPE});
if (vr.isValid()) {
int returnValIndex = -1;
Label l = levelCheck(om, bcn.name);
if (om.getReturnParameter() != -1) {
asm.dupValue(instType);
returnValIndex = storeNewLocal(instType);
}
loadArguments(
constArg(vr.getArgIdx(0), extName),
localVarArg(om.getReturnParameter(), instType, returnValIndex),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
selfArg(om.getSelfParameter(), Type.getObjectType(className)));
invokeBTraceAction(asm, om);
if (l != null) {
mv.visitLabel(l);
}
}
}
}
}
};// </editor-fold>
case NEWARRAY:
// <editor-fold defaultstate="collapsed" desc="New Array Instrumentor">
return new ArrayAllocInstrumentor(mv, className, superName, access, name, desc) {
@Override
protected void onBeforeArrayNew(String desc, int dims) {
if (where == Where.BEFORE) {
String extName = TypeUtils.getJavaType(desc);
String type = loc.getClazz();
if (matches(type, extName)) {
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[]{STRING_TYPE, Type.INT_TYPE});
if (vr.isValid()) {
Label l = levelCheck(om, bcn.name);
loadArguments(
constArg(vr.getArgIdx(0), extName),
constArg(vr.getArgIdx(1), dims),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
selfArg(om.getSelfParameter(), Type.getObjectType(className)));
invokeBTraceAction(asm, om);
if (l != null) {
mv.visitLabel(l);
}
}
}
}
}
@Override
protected void onAfterArrayNew(String desc, int dims) {
if (where == Where.AFTER) {
String extName = TypeUtils.getJavaType(desc);
String type = loc.getClazz();
if (matches(type, extName)) {
StringBuilder arrayType = new StringBuilder();
for (int i = 0; i < dims; i++) {
arrayType.append("[");
}
arrayType.append(desc);
Type instType = Type.getObjectType(arrayType.toString());
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getReturnParameter(), instType);
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[]{STRING_TYPE, Type.INT_TYPE});
if (vr.isValid()) {
int returnValIndex = -1;
Label l = levelCheck(om, bcn.name);
if (om.getReturnParameter() != -1) {
asm.dupValue(instType);
returnValIndex = storeNewLocal(instType);
}
loadArguments(
constArg(vr.getArgIdx(0), extName),
constArg(vr.getArgIdx(1), dims),
localVarArg(om.getReturnParameter(), instType, returnValIndex),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
selfArg(om.getSelfParameter(), Type.getObjectType(className)));
invokeBTraceAction(asm, om);
if (l != null) {
mv.visitLabel(l);
}
}
}
}
}
};// </editor-fold>
case RETURN:
// <editor-fold defaultstate="collapsed" desc="Return Instrumentor">
if (where != Where.BEFORE) {
return mv;
}
MethodReturnInstrumentor mri = new MethodReturnInstrumentor(mv, className, superName, access, name, desc) {
int retValIndex;
ValidationResult vr;
{
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getReturnParameter(), getReturnType());
vr = validateArguments(om, actionArgTypes, Type.getArgumentTypes(getDescriptor()));
}
private void callAction(int retOpCode) {
if (!vr.isValid()) {
return;
}
try {
boolean boxReturnValue = false;
Type probeRetType = getReturnType();
if (om.getReturnParameter() != -1) {
Type retType = Type.getArgumentTypes(om.getTargetDescriptor())[om.getReturnParameter()];
if (probeRetType.equals(Type.VOID_TYPE)) {
if (TypeUtils.isAnyType(retType)) {
// no return value but still tracking
// let's push a synthetic AnyType value on stack
asm.getStatic(Type.getInternalName(AnyType.class), "VOID", ANYTYPE_DESC);
probeRetType = OBJECT_TYPE;
} else if (VOIDREF_TYPE.equals(retType)) {
// intercepting return from method not returning value (void)
// the receiver accepts java.lang.Void only so let's push NULL on stack
asm.loadNull();
probeRetType = VOIDREF_TYPE;
}
} else {
if (Type.getReturnType(om.getTargetDescriptor()).getSort() == Type.VOID) {
asm.dupReturnValue(retOpCode);
}
boxReturnValue = TypeUtils.isAnyType(retType);
}
retValIndex = storeNewLocal(probeRetType);
}
loadArguments(
vr, actionArgTypes, isStatic(),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
constArg(om.getClassNameParameter(), className.replace("/", ".")),
localVarArg(om.getReturnParameter(), probeRetType, retValIndex, boxReturnValue),
selfArg(om.getSelfParameter(), Type.getObjectType(className)),
new ArgumentProvider(asm, om.getDurationParameter()) {
@Override
public void doProvide() {
MethodTrackingExpander.DURATION.insert(mv);
}
}
);
invokeBTraceAction(asm, om);
} finally {
if (getSkipLabel() != null) {
visitLabel(getSkipLabel());
}
}
}
@Override
protected void onMethodReturn(int opcode) {
if (vr.isValid() || vr.isAny()) {
MethodTrackingExpander.TEST_SAMPLE.insert(mv, MethodTrackingExpander.$TIMED);
Label l = levelCheck(om, bcn.name);
if (numActionArgs == 0) {
invokeBTraceAction(asm, om);
} else {
callAction(opcode);
}
MethodTrackingExpander.ELSE_SAMPLE.insert(mv);
if (l != null) {
mv.visitLabel(l);
}
}
}
private boolean generatingCode = false;
@Override
protected void onMethodEntry() {
if (vr.isValid() || vr.isAny()) {
try {
if (!generatingCode) {
generatingCode = true;
if (om.getDurationParameter() != -1) {
MethodTrackingExpander.ENTRY.insert(mv,
MethodTrackingExpander.$MEAN +
"=" +
om.getSamplerMean(),
MethodTrackingExpander.$SAMPLER +
"=" +
om.getSamplerKind(),
MethodTrackingExpander.$LEVEL +
"=" + getLevelStrSafe(om),
MethodTrackingExpander.$TIMED
);
} else {
MethodTrackingExpander.ENTRY.insert(mv,
MethodTrackingExpander.$MEAN +
"=" +
om.getSamplerMean(),
MethodTrackingExpander.$SAMPLER +
"=" +
om.getSamplerKind(),
MethodTrackingExpander.$LEVEL +
"=" + getLevelStrSafe(om)
);
}
}
} finally {
generatingCode = false;
}
}
}
};
return mri;
// </editor-fold>
case SYNC_ENTRY:
// <editor-fold defaultstate="collapsed" desc="SyncEntry Instrumentor">
return new SynchronizedInstrumentor(mv, className, superName, access, name, desc) {
int storedObjIdx = -1;
ValidationResult vr;
{
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getTargetInstanceParameter(), OBJECT_TYPE);
vr = validateArguments(om, actionArgTypes, Type.getArgumentTypes(getDescriptor()));
}
@Override
protected void onBeforeSyncEntry() {
if (vr.isValid()) {
Label l = levelCheckBefore(om, bcn.name);
if (om.getTargetInstanceParameter() != -1) {
if (isSyncMethod) {
if (!isStatic) {
storedObjIdx = 0;
} else {
asm.ldc(Type.getObjectType(className));
storedObjIdx = storeNewLocal(OBJECT_TYPE);
}
} else {
asm.dup();
storedObjIdx = storeNewLocal(OBJECT_TYPE);
}
}
if (where == Where.BEFORE) {
loadArguments(
vr, actionArgTypes, isStatic(),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
constArg(om.getClassNameParameter(), className.replace("/", ".")),
localVarArg(om.getTargetInstanceParameter(), OBJECT_TYPE, storedObjIdx),
selfArg(om.getSelfParameter(), Type.getObjectType(className))
);
invokeBTraceAction(asm, om);
}
if (l != null) {
mv.visitLabel(l);
}
}
}
@Override
protected void onAfterSyncEntry() {
if (where == Where.AFTER) {
if (vr.isValid()) {
Label l = levelCheckAfter(om, bcn.name);
loadArguments(
vr, actionArgTypes, isStatic(),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
constArg(om.getClassNameParameter(), className.replace("/", ".")),
localVarArg(om.getTargetInstanceParameter(), OBJECT_TYPE, storedObjIdx),
selfArg(om.getSelfParameter(), Type.getObjectType(className))
);
invokeBTraceAction(asm, om);
if (l != null) {
mv.visitLabel(l);
}
}
}
}
@Override
protected void onBeforeSyncExit() {
}
@Override
protected void onAfterSyncExit() {
}
};// </editor-fold>
case SYNC_EXIT:
// <editor-fold defaultstate="collapsed" desc="SyncExit Instrumentor">
return new SynchronizedInstrumentor(mv, className, superName, access, name, desc) {
int storedObjIdx = -1;
ValidationResult vr;
{
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getTargetInstanceParameter(), OBJECT_TYPE);
vr = validateArguments(om, actionArgTypes, Type.getArgumentTypes(getDescriptor()));
}
private void loadActionArgs() {
loadArguments(
vr, actionArgTypes, isStatic(),
constArg(om.getMethodParameter(), getName(om.isMethodFqn())),
constArg(om.getClassNameParameter(), className.replace("/", ".")),
localVarArg(om.getTargetInstanceParameter(), OBJECT_TYPE, storedObjIdx),
selfArg(om.getSelfParameter(), Type.getObjectType(className)),
new MethodInstrumentor.ArgumentProvider(asm, om.getDurationParameter()) {
@Override
public void doProvide() {
MethodTrackingExpander.DURATION.insert(mv);
}
}
);
}
@Override
protected void onBeforeSyncExit() {
if (!vr.isValid()) {
return;
}
Label l = levelCheckBefore(om, bcn.name);
if (om.getTargetInstanceParameter() != -1) {
if (isSyncMethod) {
if (!isStatic) {
storedObjIdx = 0;
} else {
asm.ldc(Type.getObjectType(className));
storedObjIdx = storeNewLocal(OBJECT_TYPE);
}
} else {
asm.dup();
storedObjIdx = storeNewLocal(OBJECT_TYPE);
}
}
if (where == Where.BEFORE) {
loadActionArgs();
invokeBTraceAction(asm, om);
}
if (l != null) {
mv.visitLabel(l);
}
}
@Override
protected void onAfterSyncExit() {
if (!vr.isValid()) {
return;
}
if (where == Where.AFTER) {
loadActionArgs();
invokeBTraceAction(asm, om);
}
}
@Override
protected void onAfterSyncEntry() {
if (!vr.isValid()) {
return;
}
if (om.getDurationParameter() != -1) {
MethodTrackingExpander.ENTRY.insert(mv, MethodTrackingExpander.$TIMED);
}
}
@Override
protected void onBeforeSyncEntry() {
}
};// </editor-fold>
case THROW:
// <editor-fold defaultstate="collapsed" desc="Throw Instrumentor">
return new ThrowInstrumentor(mv, className, superName, access, name, desc) {
@Override
protected void onThrow() {
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, actionArgTypes, new Type[]{THROWABLE_TYPE});
if (vr.isValid()) {
int throwableIndex = -1;
Label l = levelCheck(om, bcn.name);
if (!vr.isAny()) {
asm.dup();
throwableIndex = storeNewLocal(THROWABLE_TYPE);
}
loadArguments(
localVarArg(vr.getArgIdx(0), THROWABLE_TYPE, throwableIndex),
constArg(om.getClassNameParameter(), className.replace('/', '.')),
constArg(om.getMethodParameter(),getName(om.isMethodFqn())),
selfArg(om.getSelfParameter(), Type.getObjectType(className)));
invokeBTraceAction(asm, om);
if (l != null) {
mv.visitLabel(l);
}
}
}
};// </editor-fold>
}
return mv;
}
private MethodNode copy(MethodNode n) {
String[] exceptions = n.exceptions != null ? ((List<String>)n.exceptions).toArray(new String[0]) : null;
MethodNode mn = new MethodNode(Opcodes.ASM5, n.access, n.name, n.desc, n.signature, exceptions);
n.accept(mn);
mn.access = ACC_STATIC | ACC_PRIVATE;
mn.desc = mn.desc.replace(ANYTYPE_DESC, OBJECT_DESC);
mn.signature = mn.signature != null ? mn.signature.replace(ANYTYPE_DESC, OBJECT_DESC) : null;
mn.name = getActionMethodName(mn.name);
return mn;
}
private final ClassVisitor copyingVisitor = new ClassVisitor(Opcodes.ASM5, cv) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String sig, String[] exceptions) {
return new MethodVisitor(Opcodes.ASM5, super.visitMethod(access, name, desc, sig, exceptions)) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itfc) {
if (owner.equals(bcn.name)) {
owner = className;
name = getActionMethodName(name);
}
super.visitMethodInsn(opcode, owner, name, desc, itfc);
}
};
}
};
@Override
public void visitEnd() {
Set<MethodNode> copyNodes = new TreeSet<>(BTraceMethodNode.COMPARATOR);
for (OnMethod om : calledOnMethods) {
BTraceMethodNode bmn = om.getMethodNode();
MethodNode mn = copy(bmn);
copyNodes.add(mn);
for(BTraceMethodNode c : bmn.getCallees()) {
copyNodes.add(copy(c));
}
}
for(MethodNode mn : copyNodes) {
mn.accept(copyingVisitor);
}
cv.visitEnd();
}
private String getActionMethodName(String name) {
return InstrumentUtils.getActionPrefix(bcn.name) + name;
}
private void invokeBTraceAction(Assembler asm, OnMethod om) {
asm.invokeStatic(className, getActionMethodName(om.getTargetName()),
om.getTargetDescriptor().replace(ANYTYPE_DESC, OBJECT_DESC));
calledOnMethods.add(om);
}
/**
* Currently used for regex matching in the 'location' attribute
* @param pattern
* @param input
* @return
*/
private boolean matches(String pattern, String input) {
if (pattern.length() == 0) {
return false;
}
if (pattern.charAt(0) == '/' &&
REGEX_SPECIFIER.matcher(pattern).matches()) {
try {
return input.matches(pattern.substring(1, pattern.length() - 1));
} catch (PatternSyntaxException pse) {
reportPatternSyntaxException(pattern.substring(1, pattern.length() - 1));
return false;
}
} else {
return pattern.equals(input);
}
}
private boolean typeMatches(String decl, String desc) {
// empty type declaration matches any method signature
if (decl.isEmpty()) {
return true;
} else {
String d = TypeUtils.declarationToDescriptor(decl);
Type[] args1 = Type.getArgumentTypes(d);
Type[] args2 = Type.getArgumentTypes(desc);
return TypeUtils.isCompatible(args1, args2);
}
}
private static String getLevelStrSafe(OnMethod om) {
return om.getLevel() != null ? om.getLevel().getValue().toString(): "";
}
private static void reportPatternSyntaxException(String pattern) {
System.err.println("btrace ERROR: invalid regex pattern - " + pattern);
}
}