001 package edu.rice.cs.cunit.instrumentors.delay; 002 003 import edu.rice.cs.cunit.classFile.ClassFile; 004 import edu.rice.cs.cunit.classFile.ClassFileTools; 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.code.instructions.ReferenceInstruction; 009 import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo; 010 import edu.rice.cs.cunit.instrumentors.util.AInsertAtOpcodeStrategy; 011 import edu.rice.cs.cunit.util.IPredicate; 012 013 /** 014 * Instrumentation strategy that adds code before and after a monitorenter instruction. 015 * 016 * @author Mathias Ricken 017 */ 018 public class CallOnSynchronizedBlockStrategy 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 pre-enabled flag. 031 */ 032 protected String _preVarClassName; 033 034 /** 035 * Name of the pre-enabled flag. 036 */ 037 protected String _preVarName; 038 039 /** 040 * Name of the class containing the post-enabled flag. 041 */ 042 protected String _postVarClassName; 043 044 /** 045 * Name of the post-enabled flag. 046 */ 047 protected String _postVarName; 048 049 /** 050 * Create a new strategy. 051 * @param className name of the class containing the method to be called 052 * @param methodName name of the method to be called 053 * @param preVarClassName name of the class containing the pre-enabled flag 054 * @param preVarName name of the pre-enabled flag 055 * @param postVarClassName name of the class containing the post-enabled flag 056 * @param postVarName name of the post-enabled flag 057 */ 058 public CallOnSynchronizedBlockStrategy(final String className, String methodName, 059 String preVarClassName, String preVarName, 060 String postVarClassName, String postVarName) { 061 super(new IPredicate<ClassFile>() { 062 public Boolean apply(ClassFile param) { 063 // do not instrument the standard Java API 064 return ClassFileTools.classNameMatches(param.getThisClassName(), 065 "***", // any class, but none of the following 066 "!java.***", 067 "!javax.***", 068 "!com.sun.***", 069 "!edu.rice.cs.cunit.SyncPointBuffer", 070 "!"+className.replace('/','.'), 071 "!sun.***", 072 "!sunw.***"); 073 } 074 }, new IPredicate.Binary<ClassFile, MethodInfo>() { 075 public Boolean apply(ClassFile cf, MethodInfo param) { 076 // instrument all methods 077 return true; 078 } 079 }, new IPredicate.Ternary<ClassFile, MethodInfo, InstructionList>() { 080 public Boolean apply(ClassFile cf, MethodInfo mi, InstructionList il) { 081 return (il.getOpcode() == Opcode.MONITORENTER); 082 } 083 }, new IPredicate.Ternary<ClassFile, MethodInfo, InstructionList>() { 084 public Boolean apply(ClassFile cf, MethodInfo mi, InstructionList il) { 085 return (il.getOpcode() == Opcode.MONITORENTER) || (il.getOpcode() == Opcode.MONITOREXIT); 086 } 087 }); 088 _className = className; 089 _methodName = methodName; 090 _preVarClassName = preVarClassName; 091 _preVarName = preVarName; 092 _postVarClassName = postVarClassName; 093 _postVarName = postVarName; 094 } 095 096 /** 097 * Add instructions to call SyncPointBuffer.randomDelay before a monitorenter. 098 * @param cf class file 099 * @param mi method information 100 * @param il instruction list 101 */ 102 public void insertInstructionsBefore(final ClassFile cf, final MethodInfo mi, final InstructionList il) { 103 boolean result; 104 105 ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0); 106 int getFlagIndex = 0; 107 108 getFlagIndex = cf.addField(_preVarClassName, _preVarName, "Z", false, (short)0); 109 getFlagInstr.setReference(getFlagIndex); 110 111 ReferenceInstruction sleepCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0); 112 int sleepCallIndex = 0; 113 114 sleepCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V"); 115 sleepCallInstr.setReference(sleepCallIndex); 116 117 // bytecode looks like this: 118 // monitorenter <-- we are here 119 // ... 120 121 // insert instructions 122 il.insertBeforeInstr(getFlagInstr, mi.getCodeAttributeInfo()); 123 result = il.advanceIndex(); 124 assert result == true; 125 il.insertBeforeInstr(sleepCallInstr, mi.getCodeAttributeInfo()); 126 result = il.advanceIndex(); 127 assert result == true; 128 129 // bytecode looks like this: 130 // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_PRE_MONITOR) 131 // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.randomDelay) 132 // monitorenter <-- we are here 133 } 134 135 /** 136 * Add instructions to call SyncPointBuffer.randomDelay after a monitorenter. 137 * @param cf class file 138 * @param mi method information 139 * @param il instruction list 140 */ 141 public void insertInstructionsAfter(final ClassFile cf, final MethodInfo mi, final InstructionList il) { 142 boolean result; 143 144 ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0); 145 int getFlagIndex = 0; 146 147 getFlagIndex = cf.addField(_postVarClassName, _postVarName, "Z", false, (short)0); 148 getFlagInstr.setReference(getFlagIndex); 149 150 ReferenceInstruction sleepCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0); 151 int sleepCallIndex = 0; 152 153 sleepCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V"); 154 sleepCallInstr.setReference(sleepCallIndex); 155 156 // bytecode looks like this: 157 // monitorenter/monitorexit <-- we are here 158 // ... 159 160 // advance past the monitorenter/monitorexit instruction 161 result = il.advanceIndex(); 162 assert result == true; 163 164 // insert instructions 165 il.insertBeforeInstr(getFlagInstr, mi.getCodeAttributeInfo()); 166 result = il.advanceIndex(); 167 assert result == true; 168 169 il.insertBeforeInstr(sleepCallInstr, mi.getCodeAttributeInfo()); 170 171 // do not advance past this new instruction, because the postcondition of this method is that we 172 // are exactly before the instruction that followed the original instruction 173 174 // bytecode looks like this: 175 // ... 176 // monitorenter/exit 177 // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_POST_MONITOR) 178 // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.randomDelay) <-- we are here 179 } 180 181 /** 182 * Insert instructions at the end of the method. 183 * NOTE: The method is expected to move exactly to the last instruction that was inserted. 184 * If it moves to a place before the instruction, an infinite loop may occur. 185 * If it moves to a place after the instruction, future inserts may not occur. 186 * @param cf class file 187 * @param mi method information 188 * @param il instruction list 189 * @param insertBefore true if code was inserted before an opcode 190 * @param insertAfter true if code was inserted after an opcode 191 * @return true if code was inserted 192 */ 193 public boolean insertEndOfMethod(final ClassFile cf, final MethodInfo mi, final InstructionList il, 194 boolean insertBefore, boolean insertAfter) { 195 return false; 196 } 197 198 /** 199 * Modify the stack size and number of local variables so that the added instructions can execute. Called only once 200 * per method. 201 * 202 * @param cf class file 203 * @param mi method information 204 * @param il instruction list 205 * @param insertBefore true if code was inserted before an opcode 206 * @param insertAfter true if code was inserted after an opcode 207 * @param insertEndOfMethod true if code was inserted at the end of the method 208 */ 209 public void modifyStackAndLocals(final ClassFile cf, final MethodInfo mi, final InstructionList il, boolean insertBefore, 210 boolean insertAfter, boolean insertEndOfMethod) { 211 // call to a static method with one parameters, one stack space required 212 // make sure we have at least one slot on the stack 213 CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties(); 214 cProps.maxStack = Math.max(1, cProps.maxStack); 215 mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals); 216 } 217 }