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 }