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 }