001 package edu.rice.cs.cunit.instrumentors.record;
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.util.IPredicate;
011 import edu.rice.cs.cunit.instrumentors.util.AInsertAtOpcodeStrategy;
012
013 /**
014 * Instrumentation strategy that adds code to call SyncPointBuffer.compactAdd and write a code-tid pair.
015 *
016 * @author Mathias Ricken
017 */
018 public class CompactRecordBufferCodeStrategy extends AInsertAtOpcodeStrategy {
019 /**
020 * Code to write to the buffer.
021 */
022 protected long _code;
023
024 /**
025 * Create a new strategy with the specified predicates.
026 * @param classPredicate predicate that decides if this class should be instrumented
027 * @param methodPredicate predicate that decides if this method should be instrumented
028 * @param opcodePredicate predicate that decides if this opcode should be instrumented
029 * @param code code to write to the buffer
030 */
031 public CompactRecordBufferCodeStrategy(IPredicate<ClassFile> classPredicate,
032 IPredicate.Binary<ClassFile, MethodInfo> methodPredicate,
033 IPredicate.Ternary<ClassFile, MethodInfo, InstructionList> opcodePredicate,
034 long code) {
035 super(classPredicate, methodPredicate, opcodePredicate, OPCODE_NEVER);
036 _code = code;
037 }
038
039 /**
040 * Add instructions to call SyncPointBuffer. compactThreadExit.
041 * @param cf class file
042 * @param mi method information
043 * @param il instruction list
044 */
045 public void insertInstructionsBefore(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
046 boolean result;
047
048 ReferenceInstruction addCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
049 ReferenceInstruction loadThreadExitCodeIndexInstr = new ReferenceInstruction(Opcode.LDC2_W, (short)0);
050 ReferenceInstruction getThreadIDInstr = new ReferenceInstruction(Opcode.GETFIELD, (short)0);
051 GenericInstruction loadThisInstr = new GenericInstruction(new byte[]{Opcode.ALOAD_0});
052 int addCallIndex = 0;
053 int threadExitCodeIndex = 0;
054 int threadIDIndex = 0;
055
056 addCallIndex = cf.addMethodToConstantPool("edu/rice/cs/cunit/SyncPointBuffer",
057 "compactAdd",
058 "(JJ)V");
059 addCallInstr.setReference(addCallIndex);
060
061 // add the code for a synchronized block
062 threadExitCodeIndex = cf.addLongToConstantPool(_code);
063 loadThreadExitCodeIndexInstr.setReference(threadExitCodeIndex);
064
065 // add the field for the thread ID
066 threadIDIndex = cf.addField("java/lang/Thread", "$$$threadID$$$", "J", false, (short)0);
067 getThreadIDInstr.setReference(threadIDIndex);
068
069 // bytecode looks like this:
070 // ?ret <-- we are here
071 // ...
072
073 // insert instructions
074 il.insertBeforeInstr(addCallInstr, mi.getCodeAttributeInfo());
075 il.insertBeforeInstr(getThreadIDInstr, mi.getCodeAttributeInfo());
076 il.insertBeforeInstr(loadThisInstr, mi.getCodeAttributeInfo());
077 il.insertBeforeInstr(loadThreadExitCodeIndexInstr, mi.getCodeAttributeInfo());
078
079 // the next 4 advancePCs must return true, since we wrote them ourselves
080 result = il.advanceIndex(4);
081 assert result == true;
082
083 // bytecode looks like this:
084 // ldc2_w SP_THREADEXIT
085 // aload_0
086 // getfield (java/lang/Thread.$$$threadID$$$)
087 // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.compactAdd)
088 // ?ret <-- we are here
089 // ...
090 }
091
092 /**
093 * Insert instructions after.
094 *
095 * @param cf class file
096 * @param mi method information
097 * @param il instruction list
098 */
099 public void insertInstructionsAfter(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
100 // never going to be called
101 }
102
103 /**
104 * Insert instructions at the end of the method.
105 * NOTE: The method is expected to move exactly to the last instruction that was inserted.
106 * If it moves to a place before the instruction, an infinite loop may occur.
107 * If it moves to a place after the instruction, future inserts may not occur.
108 * @param cf class file
109 * @param mi method information
110 * @param il instruction list
111 * @param insertBefore true if code was inserted before an opcode
112 * @param insertAfter true if code was inserted after an opcode
113 * @return true if code was inserted
114 */
115 public boolean insertEndOfMethod(final ClassFile cf, final MethodInfo mi, final InstructionList il,
116 boolean insertBefore, boolean insertAfter) {
117 return false;
118 }
119
120 /**
121 * Modify the stack size and number of local variables so that the added instructions can execute. Called only once
122 * per method.
123 *
124 * @param cf class file
125 * @param mi method information
126 * @param il instruction list
127 * @param insertBefore true if code was inserted before an opcode
128 * @param insertAfter true if code was inserted after an opcode
129 * @param insertEndOfMethod true if code was inserted at the end of the method
130 */
131 public void modifyStackAndLocals(final ClassFile cf, final MethodInfo mi, final InstructionList il, boolean insertBefore,
132 boolean insertAfter, boolean insertEndOfMethod) {
133 // make sure state size is at least reqdStackSize
134 CodeAttributeInfo.CodeProperties cp = mi.getCodeAttributeInfo().getProperties();
135 cp.maxStack += 4;
136 mi.getCodeAttributeInfo().setProperties(cp.maxStack, cp.maxLocals);
137 }
138 }