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.join to call SyncPointBuffer.randomDelay.
016     *
017     * @author Mathias Ricken
018     */
019    public class CallOnThreadJoinStrategy 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 CallOnThreadJoinStrategy(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("join") &&
055                        param.getDescriptor().toString().equals("(J)V");
056                }
057            }, new IPredicate.Ternary<ClassFile, MethodInfo, InstructionList>() {
058                public Boolean apply(ClassFile cf, MethodInfo mi, InstructionList il) {
059                    return Opcode.isReturn(il.getOpcode()) /*|| (il.getOpcode()==Opcode.ATHROW)*/ || (il.getIndex()==0);
060                }
061            }, OPCODE_NEVER);
062            _className = className;
063            _methodName = methodName;
064            _varClassName = varClassName;
065            _varName = varName; 
066        }
067    
068        /**
069         * Add instructions to call SyncPointBuffer.delayThreadExit.
070         * @param cf class file
071         * @param mi method information
072         * @param il instruction list
073         */
074        public void insertInstructionsBefore(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
075            boolean result;
076            
077            ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0);
078            int getFlagIndex = 0;
079    
080            getFlagIndex = cf.addField(_varClassName, _varName, "Z", false, (short)0);
081            getFlagInstr.setReference(getFlagIndex);
082    
083            ReferenceInstruction exitCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
084            int exitCallIndex = 0;
085    
086            exitCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V");
087            exitCallInstr.setReference(exitCallIndex);
088    
089            // bytecode looks like this:
090            // ?ret <-- we are here
091            // ...
092            //
093            // or
094            //
095            // (beginning of method)
096            // ??? <-- 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(exitCallInstr, 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_JOIN)
109            // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.randomDelay)
110            // ?ret <-- we are here
111            //
112            // or
113            //
114            // (beginning of method)
115            // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_JOIN)
116            // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.randomDelay)
117            // ??? <-- we are here
118            // ...
119        }
120    
121        /**
122         * Insert instructions after.
123         *
124         * @param cf class file
125         * @param mi method information
126         * @param il instruction list
127         */
128        public void insertInstructionsAfter(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
129            // never going to be called
130        }
131        
132        /**
133         * Insert instructions at the end of the method.
134         * NOTE: The method is expected to move exactly to the last instruction that was inserted.
135         * If it moves to a place before the instruction, an infinite loop may occur.
136         * If it moves to a place after the instruction, future inserts may not occur.
137         * @param cf class file
138         * @param mi method information
139         * @param il instruction list
140         * @param insertBefore true if code was inserted before an opcode
141         * @param insertAfter true if code was inserted after an opcode
142         * @return true if code was inserted
143         */
144        public boolean insertEndOfMethod(final ClassFile cf, final MethodInfo mi, final InstructionList il,
145                                         boolean insertBefore, boolean insertAfter) {
146            // insert exception handler
147            int exceptionEndPC = il.getPCFromIndex(il.getIndex()-1);
148            int exceptionHandlerPC = il.getPCFromIndex(il.getIndex());
149            insertInstructionsBefore(cf,mi,il);
150    
151            // rethrow
152            CodeAttributeInfo codeAttr = mi.getCodeAttributeInfo();
153            il.insertInstr(new GenericInstruction(new byte[] {Opcode.ATHROW}), codeAttr);
154            // since we just inserted an instruction, we must be able to advance the PC
155            boolean result = il.advanceIndex();
156            assert result;
157    
158            // update exceptions list
159            CodeAttributeInfo.ExceptionTableEntry[] excTable =
160                new CodeAttributeInfo.ExceptionTableEntry[codeAttr.getExceptionTableEntries().length+1];
161            System.arraycopy(codeAttr.getExceptionTableEntries(), 0, excTable, 0, codeAttr.getExceptionTableEntries().length);
162            excTable[excTable.length-1] = new CodeAttributeInfo.ExceptionTableEntry((short)0,
163                                                                                    exceptionEndPC,
164                                                                                    exceptionHandlerPC,
165                                                                                    (short)0);
166            codeAttr.setExceptionTableEntries(excTable);
167            
168            return true;
169        }
170    
171        /**
172         * Modify the stack size and number of local variables so that the added instructions can execute. Called only once
173         * per method.
174         *
175         * @param cf           class file
176         * @param mi           method information
177         * @param il           instruction list
178         * @param insertBefore true if code was inserted before an opcode
179         * @param insertAfter  true if code was inserted after an opcode
180         * @param insertEndOfMethod true if code was inserted at the end of the method
181         */
182        public void modifyStackAndLocals(final ClassFile cf, final MethodInfo mi, final InstructionList il, boolean insertBefore,
183                                         boolean insertAfter, boolean insertEndOfMethod) {
184            // call to a static method with one parameters, one stack space required
185            // make sure we have at least one slot on the stack
186            CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties();
187            cProps.maxStack = Math.max(1, cProps.maxStack);
188            mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals);
189        }
190    
191        /**
192         * Instrumentation of all classes is done.
193         */
194        public void done() {
195            // nothing to do
196        }
197    }