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.notify and Object.notifyAll. 017 * 018 * @author Mathias Ricken 019 */ 020 public class CallOnObjectNotifyStrategy 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 CallOnObjectNotifyStrategy(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("notify")) || (mpi.getNameAndType().getName().toString().equals("notifyAll"))) && 090 (mpi.getNameAndType().getDescriptor().toString().equals("()V")); 091 } 092 }, OPCODE_NEVER); 093 _className = className; 094 _methodName = methodName; 095 _preVarClassName = preVarClassName; 096 _preVarName = preVarName; 097 _postVarClassName = postVarClassName; 098 _postVarName = postVarName; 099 } 100 101 /** 102 * Add instructions to call SyncPointBuffer.randomDelay before calls to Object.notify and Object.notifyAll. 103 * @param cf class file 104 * @param mi method information 105 * @param il instruction list 106 */ 107 public void insertInstructionsBefore(final ClassFile cf, final MethodInfo mi, final InstructionList il) { 108 boolean result; 109 110 ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0); 111 int getFlagIndex = 0; 112 113 getFlagIndex = cf.addField(_preVarClassName, _preVarName, "Z", false, (short)0); 114 getFlagInstr.setReference(getFlagIndex); 115 116 ReferenceInstruction sleepCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0); 117 int sleepCallIndex = 0; 118 119 sleepCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V"); 120 sleepCallInstr.setReference(sleepCallIndex); 121 122 // bytecode looks like this: 123 // invokevirtual (java/lang/Object.notify/notifyAll) <-- we are here 124 // ... 125 126 // insert instructions 127 il.insertBeforeInstr(getFlagInstr, mi.getCodeAttributeInfo()); 128 result = il.advanceIndex(); 129 assert result == true; 130 il.insertBeforeInstr(sleepCallInstr, mi.getCodeAttributeInfo()); 131 result = il.advanceIndex(); 132 assert result == true; 133 134 // bytecode looks like this: 135 // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_PRE_NOTIFY) 136 // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.randomDelay) 137 // invokevirtual (java/lang/Object.notify/notifyAll) <-- we are here 138 } 139 140 /** 141 * Add instructions to call SyncPointBuffer.randomDelay after calls to Object.notify and Object.notifyAll. 142 * @param cf class file 143 * @param mi method information 144 * @param il instruction list 145 */ 146 public void insertInstructionsAfter(final ClassFile cf, final MethodInfo mi, final InstructionList il) { 147 boolean result; 148 149 ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0); 150 int getFlagIndex = 0; 151 152 getFlagIndex = cf.addField(_postVarClassName, _postVarName, "Z", false, (short)0); 153 getFlagInstr.setReference(getFlagIndex); 154 155 ReferenceInstruction sleepCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0); 156 int sleepCallIndex = 0; 157 158 sleepCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V"); 159 sleepCallInstr.setReference(sleepCallIndex); 160 161 // bytecode looks like this: 162 // invokevirtual (java/lang/Object.notify/notifyAll) <-- we are here 163 // ... 164 165 // advance past the instruction 166 result = il.advanceIndex(); 167 assert result == true; 168 169 // insert instructions 170 il.insertBeforeInstr(getFlagInstr, mi.getCodeAttributeInfo()); 171 result = il.advanceIndex(); 172 assert result == true; 173 174 il.insertBeforeInstr(sleepCallInstr, mi.getCodeAttributeInfo()); 175 176 // do not advance past this new instruction, because the postcondition of this method is that we 177 // are exactly before the instruction that followed the original instruction 178 179 // bytecode looks like this: 180 // ... 181 // invokevirtual (java/lang/Object.notify/notifyAll) 182 // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_POST_NOTIFY) 183 // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.randomDelay) <-- we are here 184 } 185 186 /** 187 * Insert instructions at the end of the method. 188 * NOTE: The method is expected to move exactly to the last instruction that was inserted. 189 * If it moves to a place before the instruction, an infinite loop may occur. 190 * If it moves to a place after the instruction, future inserts may not occur. 191 * @param cf class file 192 * @param mi method information 193 * @param il instruction list 194 * @param insertBefore true if code was inserted before an opcode 195 * @param insertAfter true if code was inserted after an opcode 196 * @return true if code was inserted 197 */ 198 public boolean insertEndOfMethod(final ClassFile cf, final MethodInfo mi, final InstructionList il, 199 boolean insertBefore, boolean insertAfter) { 200 return false; 201 } 202 203 /** 204 * Modify the stack size and number of local variables so that the added instructions can execute. Called only once 205 * per method. 206 * 207 * @param cf class file 208 * @param mi method information 209 * @param il instruction list 210 * @param insertBefore true if code was inserted before an opcode 211 * @param insertAfter true if code was inserted after an opcode 212 * @param insertEndOfMethod true if code was inserted at the end of the method 213 */ 214 public void modifyStackAndLocals(final ClassFile cf, final MethodInfo mi, final InstructionList il, boolean insertBefore, 215 boolean insertAfter, boolean insertEndOfMethod) { 216 // call to a static method with one parameters, one stack space required 217 // make sure we have at least one slot on the stack 218 CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties(); 219 cProps.maxStack = Math.max(1, cProps.maxStack); 220 mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals); 221 } 222 }