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.code.InstructionList; 006 import edu.rice.cs.cunit.classFile.code.Opcode; 007 import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo; 008 import edu.rice.cs.cunit.classFile.code.instructions.ReferenceInstruction; 009 import edu.rice.cs.cunit.classFile.constantPool.MethodPoolInfo; 010 import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckMethodVisitor; 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.start to call a static void method without parameters. 016 * 017 * @author Mathias Ricken 018 */ 019 public class CallOnThreadStartStrategy 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 CallOnThreadStartStrategy(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("start"); 055 } 056 }, new IPredicate.Ternary<ClassFile, MethodInfo, InstructionList>() { 057 public Boolean apply(ClassFile cf, MethodInfo mi, InstructionList il) { 058 if (il.getOpcode() != Opcode.INVOKESPECIAL) { 059 return false; 060 } 061 int methodIndex = ((ReferenceInstruction)il.getInstr()).getReference(); 062 MethodPoolInfo mpi = cf.getConstantPoolItem(methodIndex).execute(CheckMethodVisitor.singleton(), null); 063 return (mpi.getClassInfo().getName().toString().equals("java/lang/Thread")) && 064 (mpi.getNameAndType().getName().toString().equals("start0")) && 065 (mpi.getNameAndType().getDescriptor().toString().equals("()V")); 066 } 067 }, OPCODE_NEVER); 068 _className = className; 069 _methodName = methodName; 070 _varClassName = varClassName; 071 _varName = varName; 072 } 073 074 /** 075 * Add instructions to call SyncPointBuffer.delayThreadStart. 076 * @param cf class file 077 * @param mi method information 078 * @param il instruction list 079 */ 080 public void insertInstructionsBefore(final ClassFile cf, final MethodInfo mi, final InstructionList il) { 081 boolean result; 082 083 ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0); 084 int getFlagIndex = 0; 085 086 getFlagIndex = cf.addField(_varClassName, _varName, "Z", false, (short)0); 087 getFlagInstr.setReference(getFlagIndex); 088 089 ReferenceInstruction startCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0); 090 int startCallIndex = 0; 091 092 startCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V"); 093 startCallInstr.setReference(startCallIndex); 094 095 // bytecode looks like this: 096 // invokespecial (java/lang/Thread/start0) <-- 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(startCallInstr, 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_START) 109 // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.delayThreadStart) 110 // invokespecial (java/lang/Thread/start0) <-- we are here 111 } 112 113 /** 114 * Insert instructions after. 115 * 116 * @param cf class file 117 * @param mi method information 118 * @param il instruction list 119 */ 120 public void insertInstructionsAfter(final ClassFile cf, final MethodInfo mi, final InstructionList il) { 121 // never going to be called 122 } 123 124 /** 125 * Insert instructions at the end of the method. 126 * NOTE: The method is expected to move exactly to the last instruction that was inserted. 127 * If it moves to a place before the instruction, an infinite loop may occur. 128 * If it moves to a place after the instruction, future inserts may not occur. 129 * @param cf class file 130 * @param mi method information 131 * @param il instruction list 132 * @param insertBefore true if code was inserted before an opcode 133 * @param insertAfter true if code was inserted after an opcode 134 * @return true if code was inserted 135 */ 136 public boolean insertEndOfMethod(final ClassFile cf, final MethodInfo mi, final InstructionList il, 137 boolean insertBefore, boolean insertAfter) { 138 return false; 139 } 140 141 /** 142 * Modify the stack size and number of local variables so that the added instructions can execute. Called only once 143 * per method. 144 * 145 * @param cf class file 146 * @param mi method information 147 * @param il instruction list 148 * @param insertBefore true if code was inserted before an opcode 149 * @param insertAfter true if code was inserted after an opcode 150 * @param insertEndOfMethod true if code was inserted at the end of the method 151 */ 152 public void modifyStackAndLocals(final ClassFile cf, final MethodInfo mi, final InstructionList il, boolean insertBefore, 153 boolean insertAfter, boolean insertEndOfMethod) { 154 // call to a static method with one parameters, one stack space required 155 // make sure we have at least one slot on the stack 156 CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties(); 157 cProps.maxStack = Math.max(1, cProps.maxStack); 158 mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals); 159 } 160 }