001 package edu.rice.cs.cunit.instrumentors.delay; 002 003 import edu.rice.cs.cunit.instrumentors.util.AInsertAtOpcodeStrategy; 004 import edu.rice.cs.cunit.classFile.ClassFile; 005 import edu.rice.cs.cunit.classFile.ClassFileTools; 006 import edu.rice.cs.cunit.classFile.MethodInfo; 007 import edu.rice.cs.cunit.classFile.constantPool.MethodPoolInfo; 008 import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckMethodVisitor; 009 import edu.rice.cs.cunit.classFile.code.InstructionList; 010 import edu.rice.cs.cunit.classFile.code.Opcode; 011 import edu.rice.cs.cunit.classFile.code.instructions.ReferenceInstruction; 012 import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo; 013 import edu.rice.cs.cunit.util.IPredicate; 014 015 /** 016 * Instrumentation strategy that adds a delay before calls to Object.wait. 017 * 018 * @author Mathias Ricken 019 */ 020 public class CallOnObjectWaitStrategy extends AInsertAtOpcodeStrategy { 021 /** 022 * Name of the class containing the method to be called. 023 */ 024 protected String _className; 025 026 /** 027 * Name of the method to be called. 028 */ 029 protected String _methodName; 030 031 /** 032 * Name of the class containing the pre-enabled flag. 033 */ 034 protected String _preVarClassName; 035 036 /** 037 * Name of the pre-enabled flag. 038 */ 039 protected String _preVarName; 040 041 /** 042 * Name of the class containing the post-enabled flag. 043 */ 044 protected String _postVarClassName; 045 046 /** 047 * Name of the post-enabled flag. 048 */ 049 protected String _postVarName; 050 051 /** 052 * Create a new strategy. 053 * @param className name of the class containing the method to be called 054 * @param methodName name of the method to be called 055 * @param preVarClassName name of the class containing the pre-enabled flag 056 * @param preVarName name of the pre-enabled flag 057 * @param postVarClassName name of the class containing the post-enabled flag 058 * @param postVarName name of the post-enabled flag 059 */ 060 public CallOnObjectWaitStrategy(final String className, String methodName, 061 String preVarClassName, String preVarName, 062 String postVarClassName, String postVarName) { 063 super(new IPredicate<ClassFile>() { 064 public Boolean apply(ClassFile param) { 065 // do not instrument the standard Java API 066 return ClassFileTools.classNameMatches(param.getThisClassName(), 067 "***", // any class, but none of the following 068 "!java.***", 069 "!javax.***", 070 "!com.sun.***", 071 "!edu.rice.cs.cunit.SyncPointBuffer", 072 "!"+className.replace('/','.'), 073 "!sun.***", 074 "!sunw.***"); 075 } 076 }, new IPredicate.Binary<ClassFile, MethodInfo>() { 077 public Boolean apply(ClassFile cf, MethodInfo param) { 078 // instrument all methods 079 return true; 080 } 081 }, new IPredicate.Ternary<ClassFile, MethodInfo, InstructionList>() { 082 public Boolean apply(ClassFile cf, MethodInfo mi, InstructionList il) { 083 if (il.getOpcode() != Opcode.INVOKEVIRTUAL) { 084 return false; 085 } 086 int methodIndex = ((ReferenceInstruction)il.getInstr()).getReference(); 087 MethodPoolInfo mpi = cf.getConstantPoolItem(methodIndex).execute(CheckMethodVisitor.singleton(), null); 088 return (mpi.getClassInfo().getName().toString().equals("java/lang/Object")) && 089 (mpi.getNameAndType().getName().toString().equals("wait")) && 090 ((mpi.getNameAndType().getDescriptor().toString().equals("()V")) || 091 (mpi.getNameAndType().getDescriptor().toString().equals("(J)V")) || 092 (mpi.getNameAndType().getDescriptor().toString().equals("(JI)V"))); 093 } 094 }, OPCODE_NEVER); 095 _className = className; 096 _methodName = methodName; 097 _preVarClassName = preVarClassName; 098 _preVarName = preVarName; 099 _postVarClassName = postVarClassName; 100 _postVarName = postVarName; 101 } 102 103 /** 104 * Add instructions to call SyncPointBuffer.randomDelay before calls to Object.wait. 105 * @param cf class file 106 * @param mi method information 107 * @param il instruction list 108 */ 109 public void insertInstructionsBefore(final ClassFile cf, final MethodInfo mi, final InstructionList il) { 110 boolean result; 111 112 ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0); 113 int getFlagIndex = 0; 114 115 getFlagIndex = cf.addField(_preVarClassName, _preVarName, "Z", false, (short)0); 116 getFlagInstr.setReference(getFlagIndex); 117 118 ReferenceInstruction sleepCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0); 119 int sleepCallIndex = 0; 120 121 sleepCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V"); 122 sleepCallInstr.setReference(sleepCallIndex); 123 124 // bytecode looks like this: 125 // invokevirtual (java/lang/Object.wait) <-- we are here 126 // ... 127 128 // insert instructions 129 il.insertBeforeInstr(getFlagInstr, mi.getCodeAttributeInfo()); 130 result = il.advanceIndex(); 131 assert result == true; 132 il.insertBeforeInstr(sleepCallInstr, mi.getCodeAttributeInfo()); 133 result = il.advanceIndex(); 134 assert result == true; 135 136 // bytecode looks like this: 137 // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_PRE_WAIT) 138 // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.delayObjectWait) 139 // invokevirtual (java/lang/Object.wait) <-- we are here 140 } 141 142 /** 143 * Add instructions to call SyncPointBuffer.randomDelay after calls to Object.wait. 144 * @param cf class file 145 * @param mi method information 146 * @param il instruction list 147 */ 148 public void insertInstructionsAfter(final ClassFile cf, final MethodInfo mi, final InstructionList il) { 149 boolean result; 150 151 ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0); 152 int getFlagIndex = 0; 153 154 getFlagIndex = cf.addField(_postVarClassName, _postVarName, "Z", false, (short)0); 155 getFlagInstr.setReference(getFlagIndex); 156 157 ReferenceInstruction sleepCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0); 158 int sleepCallIndex = 0; 159 160 sleepCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V"); 161 sleepCallInstr.setReference(sleepCallIndex); 162 163 // bytecode looks like this: 164 // invokevirtual (java/lang/Object.wait) <-- we are here 165 // ... 166 167 // advance past the instruction 168 result = il.advanceIndex(); 169 assert result == true; 170 171 // insert instructions 172 il.insertBeforeInstr(getFlagInstr, mi.getCodeAttributeInfo()); 173 result = il.advanceIndex(); 174 assert result == true; 175 176 il.insertBeforeInstr(sleepCallInstr, mi.getCodeAttributeInfo()); 177 178 // do not advance past this new instruction, because the postcondition of this method is that we 179 // are exactly before the instruction that followed the original instruction 180 181 // bytecode looks like this: 182 // ... 183 // invokevirtual (java/lang/Object.wait) 184 // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_POST_WAIT) 185 // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.randomDelay) <-- we are here 186 } 187 188 /** 189 * Insert instructions at the end of the method. 190 * NOTE: The method is expected to move exactly to the last instruction that was inserted. 191 * If it moves to a place before the instruction, an infinite loop may occur. 192 * If it moves to a place after the instruction, future inserts may not occur. 193 * @param cf class file 194 * @param mi method information 195 * @param il instruction list 196 * @param insertBefore true if code was inserted before an opcode 197 * @param insertAfter true if code was inserted after an opcode 198 * @return true if code was inserted 199 */ 200 public boolean insertEndOfMethod(final ClassFile cf, final MethodInfo mi, final InstructionList il, 201 boolean insertBefore, boolean insertAfter) { 202 return false; 203 } 204 205 /** 206 * Modify the stack size and number of local variables so that the added instructions can execute. Called only once 207 * per method. 208 * 209 * @param cf class file 210 * @param mi method information 211 * @param il instruction list 212 * @param insertBefore true if code was inserted before an opcode 213 * @param insertAfter true if code was inserted after an opcode 214 * @param insertEndOfMethod true if code was inserted at the end of the method 215 */ 216 public void modifyStackAndLocals(final ClassFile cf, final MethodInfo mi, final InstructionList il, boolean insertBefore, 217 boolean insertAfter, boolean insertEndOfMethod) { 218 // call to a static method with one parameters, one stack space required 219 // make sure we have at least one slot on the stack 220 CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties(); 221 cProps.maxStack = Math.max(1, cProps.maxStack); 222 mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals); 223 } 224 }