
com.sun.btrace.runtime.Instrumentor Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2008-2010 Sun Microsystems, Inc. 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. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.btrace.runtime;
import com.sun.btrace.AnyType;
import java.util.List;
import java.util.ArrayList;
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.Where;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import com.sun.btrace.org.objectweb.asm.AnnotationVisitor;
import com.sun.btrace.org.objectweb.asm.ClassReader;
import com.sun.btrace.org.objectweb.asm.ClassVisitor;
import com.sun.btrace.org.objectweb.asm.ClassWriter;
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.util.LocalVariablesSorter;
import com.sun.btrace.util.TimeStampGenerator;
import com.sun.btrace.util.TimeStampHelper;
import java.util.regex.PatternSyntaxException;
import static com.sun.btrace.runtime.Constants.*;
/**
* This instruments a probed class with BTrace probe
* action class.
*
* @author A. Sundararajan
*/
public class Instrumentor extends ClassVisitor {
private String btraceClassName;
private ClassReader btraceClass;
private List onMethods;
private List applicableOnMethods;
private Set calledOnMethods;
private String className, superName;
private Class clazz;
private boolean usesTimeStamp = false;
private boolean timeStampExisting = false;
public Instrumentor(Class clazz,
String btraceClassName, ClassReader btraceClass,
List onMethods, ClassVisitor cv) {
super(ASM4, cv);
this.clazz = clazz;
this.btraceClassName = btraceClassName.replace('.', '/');
this.btraceClass = btraceClass;
this.onMethods = onMethods;
this.applicableOnMethods = new ArrayList();
this.calledOnMethods = new HashSet();
}
public Instrumentor(Class clazz,
String btraceClassName, byte[] btraceCode,
List onMethods, ClassVisitor cv) {
this(clazz, btraceClassName, new ClassReader(btraceCode), onMethods, cv);
}
final public boolean hasMatch() {
return !calledOnMethods.isEmpty();
}
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
usesTimeStamp = false;
timeStampExisting = false;
className = name;
this.superName = superName;
// we filter the probe methods applicable for this particular
// class by brute force walking. FIXME: should I optimize?
String externalName = name.replace('/', '.');
for (OnMethod om : onMethods) {
String probeClazz = om.getClazz();
if (probeClazz.length() == 0) {
continue;
}
char firstChar = probeClazz.charAt(0);
if (firstChar == '/' &&
REGEX_SPECIFIER.matcher(probeClazz).matches()) {
probeClazz = probeClazz.substring(1, probeClazz.length() - 1);
try {
if (externalName.matches(probeClazz)) {
applicableOnMethods.add(om);
}
} catch (PatternSyntaxException pse) {
reportPatternSyntaxException(probeClazz);
}
} else if (firstChar == '+') {
// super type being matched.
String superType = probeClazz.substring(1);
// internal name of super type.
String superTypeInternal = superType.replace('.', '/');
/*
* If we are redefining a class, then we have a Class object
* of it and we can walk through it's hierarchy to match for
* specified super type. But, if we are loading it a fresh, then
* we can not walk through super hierarchy. We just check the
* immediate super class and directly implemented interfaces
*/
if (ClassFilter.isSubTypeOf(this.clazz, superType) ||
superName.equals(superTypeInternal) ||
isInArray(interfaces, superTypeInternal)) {
applicableOnMethods.add(om);
}
} else if (probeClazz.equals(externalName)) {
applicableOnMethods.add(om);
}
}
super.visit(version, access, name, signature, superName, interfaces);
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
AnnotationVisitor av = super.visitAnnotation(desc, visible);
String extName = Type.getType(desc).getClassName();
for (OnMethod om : onMethods) {
String probeClazz = om.getClazz();
if (probeClazz.length() > 0 && probeClazz.charAt(0) == '@') {
probeClazz = probeClazz.substring(1);
if (probeClazz.length() == 0) {
continue;
}
if (REGEX_SPECIFIER.matcher(probeClazz).matches()) {
probeClazz = probeClazz.substring(1, probeClazz.length() - 1);
try {
if (extName.matches(probeClazz)) {
applicableOnMethods.add(om);
}
} catch (PatternSyntaxException pse) {
reportPatternSyntaxException(probeClazz);
}
} else if (probeClazz.equals(extName)) {
applicableOnMethods.add(om);
}
}
}
return av;
}
public MethodVisitor visitMethod(final int access, final String name,
final String desc, String signature, String[] exceptions) {
MethodVisitor methodVisitor = super.visitMethod(access, name, desc,
signature, exceptions);
if (applicableOnMethods.isEmpty() ||
(access & ACC_ABSTRACT) != 0 ||
(access & ACC_NATIVE) != 0 ||
name.startsWith(BTRACE_METHOD_PREFIX)) {
return methodVisitor;
}
if (name.equals(TimeStampHelper.TIME_STAMP_NAME)) {
timeStampExisting = true;
return methodVisitor;
}
// used to create new local variables while keeping the class internals consistent
// Call "int index = lvs.newVar()" to create a new local variable.
// Then use the generated index to get hold of the variable
LocalVariablesSorter.Memento externalState = new LocalVariablesSorter.Memento();
final LocalVariablesSorter lvs = new LocalVariablesSorter(access, desc, methodVisitor, externalState);
methodVisitor = lvs;
final int[] tsIndex = new int[]{-1, -1};
for (OnMethod om : applicableOnMethods) {
if (om.getLocation().getValue() == Kind.LINE) {
methodVisitor = instrumentorFor(om, methodVisitor, lvs, tsIndex, access, name, desc);
} else {
String methodName = om.getMethod();
if (methodName.equals("")) {
methodName = om.getTargetName();
}
if (methodName.equals(name) &&
typeMatches(om.getType(), desc)) {
methodVisitor = instrumentorFor(om, methodVisitor, lvs, tsIndex, access, name, desc);
} else if (methodName.charAt(0) == '/' &&
REGEX_SPECIFIER.matcher(methodName).matches()) {
methodName = methodName.substring(1, methodName.length() - 1);
try {
if (name.matches(methodName) &&
typeMatches(om.getType(), desc)) {
methodVisitor = instrumentorFor(om, methodVisitor, lvs, tsIndex, access, name, desc);
}
} catch (PatternSyntaxException pse) {
reportPatternSyntaxException(name);
}
}
// lvs[0] = new LocalVariablesSorter(access, desc, methodVisitor, externalState);
}
}
return new MethodVisitor(ASM4,
methodVisitor) {
public AnnotationVisitor visitAnnotation(String annoDesc,
boolean visible) {
for (OnMethod om : applicableOnMethods) {
String extAnnoName = Type.getType(annoDesc).getClassName();
String annoName = om.getMethod();
if (annoName.length() > 0 && annoName.charAt(0) == '@') {
annoName = annoName.substring(1);
if (annoName.length() == 0) {
continue;
}
if (REGEX_SPECIFIER.matcher(annoName).matches()) {
annoName = annoName.substring(1, annoName.length() - 1);
try {
if (extAnnoName.matches(annoName)) {
mv = instrumentorFor(om, mv, lvs, access, name, desc);
}
} catch (PatternSyntaxException pse) {
reportPatternSyntaxException(extAnnoName);
}
} else if (annoName.equals(extAnnoName)) {
mv = instrumentorFor(om, mv, lvs, access, name, desc);
}
}
}
return mv.visitAnnotation(annoDesc, visible);
}
};
}
private MethodVisitor instrumentorFor(
final OnMethod om, MethodVisitor mv, final LocalVariablesSorter lvs,
int access, String name, String desc) {
return instrumentorFor(om, mv, lvs, null, access, name, desc);
}
private MethodVisitor instrumentorFor(
final OnMethod om, MethodVisitor mv, final LocalVariablesSorter lvs,
final int[] tsIndex, int access, 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:
//
return new ArrayAccessInstrumentor(mv, className, superName, access, name, desc) {
int[] argsIndex = new int[]{-1, -1};
final private int INSTANCE_PTR = 0;
final private int INDEX_PTR = 1;
@Override
protected void onBeforeArrayLoad(int opcode) {
Type arrtype = TypeUtils.getArrayType(opcode);
Type retType = TypeUtils.getElementType(opcode);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
if (where == Where.AFTER) {
addExtraTypeInfo(om.getReturnParameter(), retType);
}
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, new Type[]{arrtype, Type.INT_TYPE});
if (vr.isValid()) {
lvs.freeze();
try {
if (!vr.isAny()) {
dup2();
argsIndex[INDEX_PTR] = lvs.newLocal(Type.INT_TYPE);
argsIndex[INSTANCE_PTR] = lvs.newLocal(arrtype);
}
if (where == Where.BEFORE) {
loadArguments(
new LocalVarArgProvider(vr.getArgIdx(INDEX_PTR), Type.INT_TYPE, argsIndex[INDEX_PTR]),
new LocalVarArgProvider(vr.getArgIdx(INSTANCE_PTR), arrtype, argsIndex[INSTANCE_PTR]),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
}
} finally {
lvs.unfreeze();
}
}
}
@Override
protected void onAfterArrayLoad(int opcode) {
if (where == Where.AFTER) {
Type arrtype = TypeUtils.getArrayType(opcode);
Type retType = TypeUtils.getElementType(opcode);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getReturnParameter(), retType);
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, new Type[]{arrtype, Type.INT_TYPE});
if (vr.isValid()) {
lvs.freeze();
try {
int retValIndex = -1;
if (om.getReturnParameter() != -1) {
dupArrayValue(opcode);
retValIndex = lvs.newLocal(retType);
}
loadArguments(
new LocalVarArgProvider(vr.getArgIdx(INDEX_PTR), Type.INT_TYPE, argsIndex[INDEX_PTR]),
new LocalVarArgProvider(vr.getArgIdx(INSTANCE_PTR), arrtype, argsIndex[INSTANCE_PTR]),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getReturnParameter(), retType, retValIndex),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
}
};//
case ARRAY_SET:
//
return new ArrayAccessInstrumentor(mv, className, superName, access, name, desc) {
int[] argsIndex = new int[]{-1, -1, -1};
final private int INSTANCE_PTR = 0, INDEX_PTR = 1, VALUE_PTR = 2;
@Override
protected void onBeforeArrayStore(int opcode) {
Type elementType = TypeUtils.getElementType(opcode);
Type arrayType = TypeUtils.getArrayType(opcode);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, new Type[]{arrayType, Type.INT_TYPE, elementType});
if (vr.isValid()) {
lvs.freeze();
try {
if (!vr.isAny()) {
argsIndex[VALUE_PTR] = lvs.newLocal(elementType);
dup2();
argsIndex[INDEX_PTR] = lvs.newLocal(Type.INT_TYPE);
argsIndex[INSTANCE_PTR] = lvs.newLocal(TypeUtils.getArrayType(opcode));
loadLocal(elementType, argsIndex[VALUE_PTR]);
}
if (where == Where.BEFORE) {
loadArguments(
new LocalVarArgProvider(vr.getArgIdx(INSTANCE_PTR), arrayType, argsIndex[INSTANCE_PTR]),
new LocalVarArgProvider(vr.getArgIdx(INDEX_PTR), Type.INT_TYPE, argsIndex[INDEX_PTR]),
new LocalVarArgProvider(vr.getArgIdx(VALUE_PTR), elementType, argsIndex[VALUE_PTR]),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
}
} finally {
lvs.unfreeze();
}
}
}
@Override
protected void onAfterArrayStore(int opcode) {
if (where == Where.AFTER) {
Type elementType = TypeUtils.getElementType(opcode);
Type arrayType = TypeUtils.getArrayType(opcode);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, new Type[]{arrayType, Type.INT_TYPE, elementType});
if (vr.isValid()) {
lvs.freeze();
try {
loadArguments(
new LocalVarArgProvider(vr.getArgIdx(INSTANCE_PTR), arrayType, argsIndex[INSTANCE_PTR]),
new LocalVarArgProvider(vr.getArgIdx(INDEX_PTR), Type.INT_TYPE, argsIndex[INDEX_PTR]),
new LocalVarArgProvider(vr.getArgIdx(VALUE_PTR), elementType, argsIndex[VALUE_PTR]),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
}
};//
case CALL:
//
return new MethodCallInstrumentor(mv, className, superName, access, name, desc) {
private String localClassName = loc.getClazz();
private String localMethodName = loc.getMethod();
private int returnVarIndex = -1;
int[] backupArgsIndexes;
private void injectBtrace(ValidationResult vr, final String method, final Type[] callArgTypes, final Type returnType) {
ArgumentProvider[] actionArgs = new ArgumentProvider[actionArgTypes.length + 6];
for(int i=0;i -1) {
return; // invalid combination; a static method can not provide *this*
}
if (matches(localClassName, owner.replace('/', '.'))
&& matches(localMethodName, name)
&& typeMatches(loc.getType(), desc)) {
String method = (om.isTargetMethodOrFieldFqn() ? (owner + ".") : "") + name + (om.isTargetMethodOrFieldFqn() ? desc : "");
Type[] calledMethodArgs = Type.getArgumentTypes(desc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
if (where == Where.AFTER) {
addExtraTypeInfo(om.getReturnParameter(), Type.getReturnType(desc));
}
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, calledMethodArgs);
if (vr.isValid()) {
lvs.freeze();
try {
boolean isStaticCall = (opcode == INVOKESTATIC);
if (isStaticCall) {
if (om.getTargetInstanceParameter() != -1) {
return;
}
} else {
if (where == Where.BEFORE && name.equals(CONSTRUCTOR)) {
return;
}
}
// will store the call args into local variables
backupArgsIndexes = backupStack(lvs, Type.getArgumentTypes(desc), isStaticCall);
if (where == Where.BEFORE) {
injectBtrace(vr, method, Type.getArgumentTypes(desc), Type.getReturnType(desc));
}
// put the call args back on stack so the method call can find them
restoreStack(backupArgsIndexes, Type.getArgumentTypes(desc), isStaticCall);
} finally {
lvs.unfreeze();
}
}
}
}
@Override
protected void onAfterCallMethod(int opcode,
String owner, String name, String desc) {
if (isStatic() && om.getSelfParameter() != -1) {
return;
}
if (where == Where.AFTER
&& matches(localClassName, owner.replace('/', '.'))
&& matches(localMethodName, name)
&& typeMatches(loc.getType(), desc)) {
Type returnType = Type.getReturnType(desc);
Type[] calledMethodArgs = Type.getArgumentTypes(desc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getReturnParameter(), returnType);
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, calledMethodArgs);
if (vr.isValid()) {
lvs.freeze();
try {
String method = name + desc;
boolean withReturn = om.getReturnParameter() != -1 && returnType != Type.VOID_TYPE;
if (withReturn) {
// store the return value to a local variable
int index = lvs.newLocal(returnType);
returnVarIndex = index;
}
// will also retrieve the call args and the return value from the backup variables
injectBtrace(vr, method, calledMethodArgs, returnType);
if (withReturn) {
loadLocal(returnType, returnVarIndex); // restore the return value
}
} finally {
lvs.unfreeze();
}
}
}
}
};//
case CATCH:
//
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, isStatic(), actionArgTypes, new Type[]{exctype});
if (vr.isValid()) {
int index = -1;
lvs.freeze();
try {
if (!vr.isAny()) {
dup();
index = lvs.newLocal(exctype);
}
loadArguments(
new LocalVarArgProvider(vr.getArgIdx(0), exctype, index),
new ConstantArgProvider(om.getClassNameParameter(), className),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
};//
case CHECKCAST:
//
return new TypeCheckInstrumentor(mv, className, superName, access, name, desc) {
private void callAction(int opcode, String desc) {
if (opcode == Opcodes.CHECKCAST) {
// TODO not really usefull
// It would be better to check for the original and desired type
Type castType = Type.getObjectType(desc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, new Type[]{castType});
if (vr.isValid()) {
lvs.freeze();
try {
int castTypeIndex = -1;
if (!vr.isAny()) {
dup();
castTypeIndex = lvs.newLocal(castType);
}
loadArguments(
new LocalVarArgProvider(vr.getArgIdx(0), castType, castTypeIndex),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
}
@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);
}
}
};//
case ENTRY:
//
return new MethodEntryInstrumentor(mv, className, superName, access, name, desc) {
private void injectBtrace(ValidationResult vr) {
lvs.freeze();
try {
ArgumentProvider[] actionArgs = new ArgumentProvider[actionArgTypes.length + 3];
int ptr = isStatic() ? 0 : 1;
for(int i=0;i -1) {
return; // invalid combination; a static method can not provide *this*
}
Type[] calledMethodArgs = Type.getArgumentTypes(getDescriptor());
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, calledMethodArgs);
if (vr.isValid()) {
injectBtrace(vr);
}
}
@Override
protected void onMethodEntry() {
if (numActionArgs == 0) {
invokeBTraceAction(this, om);
} else {
callAction();
}
}
};//
case ERROR:
//
ErrorReturnInstrumentor eri = new ErrorReturnInstrumentor(mv, tsIndex, className, superName, access, name, desc) {
ValidationResult vr;
{
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
vr = validateArguments(om, isStatic(), actionArgTypes, new Type[]{TypeUtils.throwableType});
}
@Override
protected void onErrorReturn() {
if (vr.isValid()) {
int throwableIndex = -1;
lvs.freeze();
try {
if (om.getDurationParameter() != -1) {
usesTimeStamp = true;
// TODO: this is a nasty hack; should be in TimeStampGenerator but can't fit it there, no way :(
if (tsIndex[1] == -1) {
TimeStampHelper.generateTimeStampAccess(this, className);
tsIndex[1] = lvs.newLocal(Type.LONG_TYPE);
}
}
if (!vr.isAny()) {
dup();
throwableIndex = lvs.newLocal(TypeUtils.throwableType);
}
ArgumentProvider[] actionArgs = new ArgumentProvider[4 + (tsIndex != null ? 1 : 0)];
actionArgs[0] = new LocalVarArgProvider(vr.getArgIdx(0), TypeUtils.throwableType, throwableIndex);
actionArgs[1] = new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", "."));
actionArgs[2] = new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn()));
actionArgs[3] = new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0);
if (tsIndex != null) {
actionArgs[4] = new ArgumentProvider(om.getDurationParameter()) {
public void doProvide() {
if (tsIndex[0] != -1 && tsIndex[1] != -1) {
loadLocal(Type.LONG_TYPE, tsIndex[1]);
loadLocal(Type.LONG_TYPE, tsIndex[0]);
visitInsn(LSUB);
}
}
};
}
loadArguments(actionArgs);
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
@Override
public boolean usesTimeStamp() {
return vr.isValid() && om.getDurationParameter() != -1;
}
};
if (om.getDurationParameter() != -1) {
return new TimeStampGenerator(lvs, tsIndex, className, superName, access, name, desc, eri, new int[0]);
} else {
return eri;
}//
case FIELD_GET:
//
return new FieldAccessInstrumentor(mv, className, superName, access, name, desc) {
int calledInstanceIndex = -1;
private String targetClassName = loc.getClazz();
private String targetFieldName = (om.isTargetMethodOrFieldFqn() ? targetClassName + "." : "") + loc.getField();
@Override
protected void onBeforeGetField(int opcode, String owner,
String name, String desc) {
if (om.getTargetInstanceParameter() != -1 && isStaticAccess) {
return;
}
if (matches(targetClassName, owner.replace('/', '.'))
&& matches(targetFieldName, name)) {
Type fldType = Type.getType(desc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
if (where == Where.AFTER) {
addExtraTypeInfo(om.getReturnParameter(), fldType);
}
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, new Type[0]);
if (vr.isValid()) {
lvs.freeze();
try {
if (om.getTargetInstanceParameter() != -1) {
dup();
calledInstanceIndex = lvs.newLocal(TypeUtils.objectType);
}
if (where == Where.BEFORE) {
loadArguments(
new LocalVarArgProvider(om.getTargetInstanceParameter(), TypeUtils.objectType, calledInstanceIndex),
new ConstantArgProvider(om.getTargetMethodOrFieldParameter(), targetFieldName),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
}
} finally {
lvs.unfreeze();
}
}
}
}
@Override
protected void onAfterGetField(int opcode, String owner,
String name, String desc) {
if (om.getTargetInstanceParameter() != -1 && isStaticAccess) {
return;
}
if (where == Where.AFTER
&& matches(targetClassName, owner.replace('/', '.'))
&& matches(targetFieldName, name)) {
Type fldType = Type.getType(desc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getReturnParameter(), fldType);
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, new Type[0]);
if (vr.isValid()) {
int returnValIndex = -1;
lvs.freeze();
try {
if (om.getReturnParameter() != -1) {
dupValue(desc);
returnValIndex = lvs.newLocal(fldType);
}
loadArguments(
new LocalVarArgProvider(om.getTargetInstanceParameter(), TypeUtils.objectType, calledInstanceIndex),
new ConstantArgProvider(om.getTargetMethodOrFieldParameter(), targetFieldName),
new LocalVarArgProvider(om.getReturnParameter(), fldType, returnValIndex),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
}
};//
case FIELD_SET:
//
return new FieldAccessInstrumentor(mv, className, superName, access, name, desc) {
private String targetClassName = loc.getClazz();
private String targetFieldName = (om.isTargetMethodOrFieldFqn() ? targetClassName + "." : "") + loc.getField();
private int calledInstanceIndex = -1;
private int fldValueIndex = -1;
@Override
protected void onBeforePutField(int opcode, String owner,
String name, String desc) {
if (om.getTargetInstanceParameter() != -1 && isStaticAccess) {
return;
}
if (matches(targetClassName, owner.replace('/', '.'))
&& matches(targetFieldName, name)) {
Type fieldType = Type.getType(desc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, new Type[]{fieldType});
if (vr.isValid()) {
lvs.freeze();
try {
if (!vr.isAny()) {
fldValueIndex = lvs.newLocal(fieldType);
}
if (om.getTargetInstanceParameter() != -1) {
dup();
calledInstanceIndex = lvs.newLocal(TypeUtils.objectType);
}
if (!vr.isAny()) {
// need to put the set value back on stack
loadLocal(fieldType, fldValueIndex);
}
if (where == Where.BEFORE) {
loadArguments(
new LocalVarArgProvider(vr.getArgIdx(0), fieldType, fldValueIndex),
new LocalVarArgProvider(om.getTargetInstanceParameter(), TypeUtils.objectType, calledInstanceIndex),
new ConstantArgProvider(om.getTargetMethodOrFieldParameter(), targetFieldName),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
}
} finally {
lvs.unfreeze();
}
}
}
}
@Override
protected void onAfterPutField(int opcode,
String owner, String name, String desc) {
if (om.getTargetInstanceParameter() != -1 && isStaticAccess) {
return;
}
if (where == Where.AFTER
&& matches(targetClassName, owner.replace('/', '.'))
&& matches(targetFieldName, name)) {
Type fieldType = Type.getType(desc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, new Type[]{fieldType});
if (vr.isValid()) {
lvs.freeze();
try {
loadArguments(
new LocalVarArgProvider(vr.getArgIdx(0), fieldType, fldValueIndex),
new LocalVarArgProvider(om.getTargetInstanceParameter(), TypeUtils.objectType, calledInstanceIndex),
new ConstantArgProvider(om.getTargetMethodOrFieldParameter(), targetFieldName),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
}
};//
case INSTANCEOF:
//
return new TypeCheckInstrumentor(mv, className, superName, access, name, desc) {
private void callAction(int opcode, String desc) {
if (opcode == Opcodes.INSTANCEOF) {
// TODO not really usefull
// It would be better to check for the original and desired type
Type castType = Type.getObjectType(desc);
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, new Type[]{castType});
if (vr.isValid()) {
int castTypeIndex = -1;
lvs.freeze();
try {
if (!vr.isAny()) {
dup();
castTypeIndex = lvs.newLocal(castType);
}
loadArguments(
new LocalVarArgProvider(vr.getArgIdx(0), castType, castTypeIndex),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
}
@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);
}
}
};//
case LINE:
//
return new LineNumberInstrumentor(mv, className, superName, access, name, desc) {
private int onLine = loc.getLine();
private void callOnLine(int line) {
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, new Type[]{Type.INT_TYPE});
if (vr.isValid()) {
lvs.freeze();
try {
loadArguments(
new ConstantArgProvider(vr.getArgIdx(0), line),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
@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);
}
}
};//
case NEW:
//
return new ObjectAllocInstrumentor(mv, className, superName, access, name, desc) {
@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, isStatic(), actionArgTypes, new Type[]{TypeUtils.stringType});
if (vr.isValid()) {
lvs.freeze();
try {
loadArguments(
new ConstantArgProvider(vr.getArgIdx(0), extName),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
}
}
@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, isStatic(), actionArgTypes, new Type[]{TypeUtils.stringType});
if (vr.isValid()) {
int returnValIndex = -1;
lvs.freeze();
try {
if (om.getReturnParameter() != -1) {
dupValue(instType);
returnValIndex = lvs.newLocal(instType);
}
loadArguments(
new ConstantArgProvider(vr.getArgIdx(0), extName),
new LocalVarArgProvider(om.getReturnParameter(), instType, returnValIndex),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
}
}
};//
case NEWARRAY:
//
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 = TypeUtils.objectOrArrayType(loc.getClazz());
if (matches(type, desc)) {
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, new Type[]{TypeUtils.stringType, Type.INT_TYPE});
if (vr.isValid()) {
lvs.freeze();
try {
loadArguments(
new ConstantArgProvider(vr.getArgIdx(0), extName),
new ConstantArgProvider(vr.getArgIdx(1), dims),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
}
}
@Override
protected void onAfterArrayNew(String desc, int dims) {
if (where == Where.AFTER) {
String extName = TypeUtils.getJavaType(desc);
String type = TypeUtils.objectOrArrayType(loc.getClazz());
if (matches(type, desc)) {
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, isStatic(), actionArgTypes, new Type[]{TypeUtils.stringType, Type.INT_TYPE});
if (vr.isValid()) {
int returnValIndex = -1;
lvs.freeze();
try {
if (om.getReturnParameter() != -1) {
dupValue(instType);
returnValIndex = lvs.newLocal(instType);
}
loadArguments(
new ConstantArgProvider(vr.getArgIdx(0), extName),
new ConstantArgProvider(vr.getArgIdx(1), dims),
new LocalVarArgProvider(om.getReturnParameter(), instType, returnValIndex),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
}
}
};//
case RETURN:
//
if (where != Where.BEFORE) {
return mv;
}
MethodReturnInstrumentor mri = new MethodReturnInstrumentor(mv, tsIndex, className, superName, access, name, desc) {
int retValIndex;
ValidationResult vr;
{
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
addExtraTypeInfo(om.getReturnParameter(), getReturnType());
vr = validateArguments(om, isStatic(), actionArgTypes, Type.getArgumentTypes(getDescriptor()));
}
private void callAction(int retOpCode) {
if (!vr.isValid()) {
return;
}
lvs.freeze();
try {
if (om.getReturnParameter() != -1) {
dupReturnValue(retOpCode);
retValIndex = lvs.newLocal(getReturnType());
}
if (om.getDurationParameter() != -1) {
usesTimeStamp = true;
}
ArgumentProvider[] actionArgs = new ArgumentProvider[actionArgTypes.length + 4 + (tsIndex !=null ? 1 : 0)];
int ptr = isStatic() ? 0 : 1;
for(int i=0;i
case SYNC_ENTRY:
//
return new SynchronizedInstrumentor(mv, tsIndex, className, superName, access, name, desc) {
private void callAction() {
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, new Type[]{TypeUtils.objectType});
if (vr.isValid()) {
int index = -1;
lvs.freeze();
try {
if (!vr.isAny()) {
dup();
index = lvs.newLocal(TypeUtils.objectType);
}
loadArguments(
new LocalVarArgProvider(vr.getArgIdx(0), TypeUtils.objectType, index),
new ConstantArgProvider(om.getClassNameParameter(), className),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
@Override
protected void onBeforeSyncEntry() {
if (where == Where.BEFORE) {
callAction();
}
}
@Override
protected void onAfterSyncEntry() {
if (where == Where.AFTER) {
callAction();
}
}
@Override
protected void onBeforeSyncExit() {
}
@Override
protected void onAfterSyncExit() {
}
};//
case SYNC_EXIT:
//
return new SynchronizedInstrumentor(mv, tsIndex, className, superName, access, name, desc) {
private void callAction() {
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, new Type[]{TypeUtils.objectType});
if (vr.isValid()) {
int index = -1;
lvs.freeze();
try {
if (!vr.isAny()) {
dup();
index = lvs.newLocal(TypeUtils.objectType);
}
loadArguments(
new LocalVarArgProvider(vr.getArgIdx(0), TypeUtils.objectType, index),
new ConstantArgProvider(om.getClassNameParameter(), className),
new ConstantArgProvider(om.getMethodParameter(), getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
@Override
protected void onBeforeSyncEntry() {
}
@Override
protected void onAfterSyncEntry() {
}
@Override
protected void onBeforeSyncExit() {
if (where == Where.BEFORE) {
callAction();
}
}
@Override
protected void onAfterSyncExit() {
if (where == Where.AFTER) {
callAction();
}
}
};//
case THROW:
//
return new ThrowInstrumentor(mv, className, superName, access, name, desc) {
@Override
protected void onThrow() {
addExtraTypeInfo(om.getSelfParameter(), Type.getObjectType(className));
ValidationResult vr = validateArguments(om, isStatic(), actionArgTypes, new Type[]{TypeUtils.throwableType});
if (vr.isValid()) {
int throwableIndex = -1;
lvs.freeze();
try {
if (!vr.isAny()) {
dup();
throwableIndex = lvs.newLocal(TypeUtils.throwableType);
}
loadArguments(
new LocalVarArgProvider(vr.getArgIdx(0), TypeUtils.throwableType, throwableIndex),
new ConstantArgProvider(om.getClassNameParameter(), className.replace("/", ".")),
new ConstantArgProvider(om.getMethodParameter(),getName(om.isMethodFqn())),
new LocalVarArgProvider(om.getSelfParameter(), Type.getObjectType(className), 0));
invokeBTraceAction(this, om);
} finally {
lvs.unfreeze();
}
}
}
};//
}
return mv;
}
private void introduceTimeStampHelper() {
if (usesTimeStamp && !timeStampExisting) {
TimeStampHelper.generateTimeStampGetter(this);
}
}
public void visitEnd() {
int size = applicableOnMethods.size();
List mi = new ArrayList(size);
for (OnMethod om : calledOnMethods) {
mi.add(new MethodCopier.MethodInfo(om.getTargetName(),
om.getTargetDescriptor(),
getActionMethodName(om.getTargetName()),
ACC_STATIC | ACC_PRIVATE));
}
introduceTimeStampHelper();
MethodCopier copier = new MethodCopier(btraceClass, cv, mi) {
@Override
protected MethodVisitor addMethod(int access, String name, String desc,
String signature, String[] exceptions) {
desc = desc.replace(ANYTYPE_DESC, OBJECT_DESC);
if (signature != null) {
signature = signature.replace(ANYTYPE_DESC, OBJECT_DESC);
}
return super.addMethod(access, name, desc, signature, exceptions);
}
};
copier.visitEnd();
}
public static void main(String[] args) throws Exception {
if (args.length != 2) {
System.err.println("Usage: java com.sun.btrace.runtime.Instrumentor ]");
System.exit(1);
}
String className = args[0].replace('.', '/') + ".class";
FileInputStream fis = new FileInputStream(className);
byte[] buf = new byte[(int)new File(className).length()];
fis.read(buf);
fis.close();
ClassWriter writer = InstrumentUtils.newClassWriter();
Verifier verifier = new Verifier(new Preprocessor(writer));
InstrumentUtils.accept(new ClassReader(buf), verifier);
buf = writer.toByteArray();
FileOutputStream fos = new FileOutputStream(className);
fos.write(buf);
fos.close();
String targetClass = args[1].replace('.', '/') + ".class";
fis = new FileInputStream(targetClass);
writer = InstrumentUtils.newClassWriter();
ClassReader reader = new ClassReader(fis);
InstrumentUtils.accept(reader, new Instrumentor(null,
verifier.getClassName(), buf,
verifier.getOnMethods(), writer));
fos = new FileOutputStream(targetClass);
fos.write(writer.toByteArray());
}
private String getActionMethodName(String name) {
return Constants.BTRACE_METHOD_PREFIX +
btraceClassName.replace('/', '$') + "$" + name;
}
private void invokeBTraceAction(MethodInstrumentor mv, OnMethod om) {
mv.invokeStatic(className, getActionMethodName(om.getTargetName()),
om.getTargetDescriptor().replace(ANYTYPE_DESC, OBJECT_DESC));
calledOnMethods.add(om);
}
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 boolean isInArray(String[] candidates, String given) {
for (String c : candidates) {
if (c.equals(given)) {
return true;
}
}
return false;
}
private static void reportPatternSyntaxException(String pattern) {
System.err.println("btrace ERROR: invalid regex pattern - " + pattern);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy