001 package edu.rice.cs.cunit.instrumentors.delay;
002
003 import edu.rice.cs.cunit.classFile.ClassFile;
004 import edu.rice.cs.cunit.classFile.MethodInfo;
005 import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo;
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.GenericInstruction;
009 import edu.rice.cs.cunit.classFile.code.instructions.ReferenceInstruction;
010 import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo;
011 import edu.rice.cs.cunit.instrumentors.util.AInsertAtOpcodeStrategy;
012 import edu.rice.cs.cunit.util.IPredicate;
013
014 /**
015 * Instrumentation strategy that adds code to java.lang.Thread.join to call SyncPointBuffer.randomDelay.
016 *
017 * @author Mathias Ricken
018 */
019 public class CallOnThreadJoinStrategy extends AInsertAtOpcodeStrategy {
020 /**
021 * Name of the class containing the method to be called.
022 */
023 protected String _className;
024
025 /**
026 * Name of the method to be called.
027 */
028 protected String _methodName;
029
030 /**
031 * Name of the class containing the enabled flag.
032 */
033 protected String _varClassName;
034
035 /**
036 * Name of the enabled flag.
037 */
038 protected String _varName;
039
040 /**
041 * Create a new strategy.
042 * @param className name of the class containing the method to be called
043 * @param methodName name of the method to be called
044 * @param varClassName name of the class containing the enabled flag
045 * @param varName name of the enabled flag
046 */
047 public CallOnThreadJoinStrategy(String className, String methodName, String varClassName, String varName) {
048 super(new IPredicate<ClassFile>() {
049 public Boolean apply(ClassFile param) {
050 return param.getThisClassName().equals("java.lang.Thread");
051 }
052 }, new IPredicate.Binary<ClassFile, MethodInfo>() {
053 public Boolean apply(ClassFile cf, MethodInfo param) {
054 return param.getName().toString().equals("join") &&
055 param.getDescriptor().toString().equals("(J)V");
056 }
057 }, new IPredicate.Ternary<ClassFile, MethodInfo, InstructionList>() {
058 public Boolean apply(ClassFile cf, MethodInfo mi, InstructionList il) {
059 return Opcode.isReturn(il.getOpcode()) /*|| (il.getOpcode()==Opcode.ATHROW)*/ || (il.getIndex()==0);
060 }
061 }, OPCODE_NEVER);
062 _className = className;
063 _methodName = methodName;
064 _varClassName = varClassName;
065 _varName = varName;
066 }
067
068 /**
069 * Add instructions to call SyncPointBuffer.delayThreadExit.
070 * @param cf class file
071 * @param mi method information
072 * @param il instruction list
073 */
074 public void insertInstructionsBefore(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
075 boolean result;
076
077 ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0);
078 int getFlagIndex = 0;
079
080 getFlagIndex = cf.addField(_varClassName, _varName, "Z", false, (short)0);
081 getFlagInstr.setReference(getFlagIndex);
082
083 ReferenceInstruction exitCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
084 int exitCallIndex = 0;
085
086 exitCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V");
087 exitCallInstr.setReference(exitCallIndex);
088
089 // bytecode looks like this:
090 // ?ret <-- we are here
091 // ...
092 //
093 // or
094 //
095 // (beginning of method)
096 // ??? <-- we are here
097 // ...
098
099 // insert instructions
100 il.insertBeforeInstr(getFlagInstr, mi.getCodeAttributeInfo());
101 result = il.advanceIndex();
102 assert result == true;
103 il.insertBeforeInstr(exitCallInstr, mi.getCodeAttributeInfo());
104 result = il.advanceIndex();
105 assert result == true;
106
107 // bytecode looks like this:
108 // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_JOIN)
109 // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.randomDelay)
110 // ?ret <-- we are here
111 //
112 // or
113 //
114 // (beginning of method)
115 // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_JOIN)
116 // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.randomDelay)
117 // ??? <-- we are here
118 // ...
119 }
120
121 /**
122 * Insert instructions after.
123 *
124 * @param cf class file
125 * @param mi method information
126 * @param il instruction list
127 */
128 public void insertInstructionsAfter(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
129 // never going to be called
130 }
131
132 /**
133 * Insert instructions at the end of the method.
134 * NOTE: The method is expected to move exactly to the last instruction that was inserted.
135 * If it moves to a place before the instruction, an infinite loop may occur.
136 * If it moves to a place after the instruction, future inserts may not occur.
137 * @param cf class file
138 * @param mi method information
139 * @param il instruction list
140 * @param insertBefore true if code was inserted before an opcode
141 * @param insertAfter true if code was inserted after an opcode
142 * @return true if code was inserted
143 */
144 public boolean insertEndOfMethod(final ClassFile cf, final MethodInfo mi, final InstructionList il,
145 boolean insertBefore, boolean insertAfter) {
146 // insert exception handler
147 int exceptionEndPC = il.getPCFromIndex(il.getIndex()-1);
148 int exceptionHandlerPC = il.getPCFromIndex(il.getIndex());
149 insertInstructionsBefore(cf,mi,il);
150
151 // rethrow
152 CodeAttributeInfo codeAttr = mi.getCodeAttributeInfo();
153 il.insertInstr(new GenericInstruction(new byte[] {Opcode.ATHROW}), codeAttr);
154 // since we just inserted an instruction, we must be able to advance the PC
155 boolean result = il.advanceIndex();
156 assert result;
157
158 // update exceptions list
159 CodeAttributeInfo.ExceptionTableEntry[] excTable =
160 new CodeAttributeInfo.ExceptionTableEntry[codeAttr.getExceptionTableEntries().length+1];
161 System.arraycopy(codeAttr.getExceptionTableEntries(), 0, excTable, 0, codeAttr.getExceptionTableEntries().length);
162 excTable[excTable.length-1] = new CodeAttributeInfo.ExceptionTableEntry((short)0,
163 exceptionEndPC,
164 exceptionHandlerPC,
165 (short)0);
166 codeAttr.setExceptionTableEntries(excTable);
167
168 return true;
169 }
170
171 /**
172 * Modify the stack size and number of local variables so that the added instructions can execute. Called only once
173 * per method.
174 *
175 * @param cf class file
176 * @param mi method information
177 * @param il instruction list
178 * @param insertBefore true if code was inserted before an opcode
179 * @param insertAfter true if code was inserted after an opcode
180 * @param insertEndOfMethod true if code was inserted at the end of the method
181 */
182 public void modifyStackAndLocals(final ClassFile cf, final MethodInfo mi, final InstructionList il, boolean insertBefore,
183 boolean insertAfter, boolean insertEndOfMethod) {
184 // call to a static method with one parameters, one stack space required
185 // make sure we have at least one slot on the stack
186 CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties();
187 cProps.maxStack = Math.max(1, cProps.maxStack);
188 mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals);
189 }
190
191 /**
192 * Instrumentation of all classes is done.
193 */
194 public void done() {
195 // nothing to do
196 }
197 }