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 }