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