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 }