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