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