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 }