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    }