001    package edu.rice.cs.cunit.instrumentors.delay;
002    
003    import edu.rice.cs.cunit.instrumentors.util.AInsertAtOpcodeStrategy;
004    import edu.rice.cs.cunit.classFile.ClassFile;
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.attributes.CodeAttributeInfo;
009    import edu.rice.cs.cunit.classFile.code.instructions.GenericInstruction;
010    import edu.rice.cs.cunit.classFile.code.instructions.ReferenceInstruction;
011    import edu.rice.cs.cunit.util.IPredicate;
012    
013    /**
014     * Instrumentation strategy that adds code to java.lang.Thread.run to call SyncPointBuffer.randomDelay.
015     *
016     * @author Mathias Ricken
017     */
018    public class CallOnThreadRunStrategy 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 enabled flag.
031         */
032        protected String _varClassName;
033    
034        /**
035         * Name of the enabled flag.
036         */
037        protected String _varName;
038    
039        /**
040         * Create a new strategy.
041         * @param className name of the class containing the method to be called
042         * @param methodName name of the method to be called
043         * @param varClassName name of the class containing the enabled flag
044         * @param varName name of the enabled flag
045         */
046        public CallOnThreadRunStrategy(String className, String methodName, String varClassName, String varName) {
047            super(new IPredicate<ClassFile>() {
048                public Boolean apply(ClassFile param) {
049                    return param.getThisClassName().equals("java.lang.Thread");
050                }
051            }, new IPredicate.Binary<ClassFile, MethodInfo>() {
052                public Boolean apply(ClassFile cf, MethodInfo param) {
053                    return param.getName().toString().equals("run");
054                }
055            }, new IPredicate.Ternary<ClassFile, MethodInfo, InstructionList>() {
056                public Boolean apply(ClassFile cf, MethodInfo mi, InstructionList il) {
057                    return il.getIndex()==0;
058                }
059            }, OPCODE_NEVER);
060            _className = className;
061            _methodName = methodName;
062            _varClassName = varClassName;
063            _varName = varName; 
064        }
065    
066        /**
067         * Add instructions to call SyncPointBuffer.delayThreadExit.
068         * @param cf class file
069         * @param mi method information
070         * @param il instruction list
071         */
072        public void insertInstructionsBefore(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
073            boolean result;
074    
075            ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0);
076            int getFlagIndex = 0;
077    
078            getFlagIndex = cf.addField(_varClassName, _varName, "Z", false, (short)0);
079            getFlagInstr.setReference(getFlagIndex);
080            
081            ReferenceInstruction sleepCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
082            int sleepCallIndex = 0;
083    
084            sleepCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V");
085            sleepCallInstr.setReference(sleepCallIndex);
086    
087            // bytecode looks like this:
088            // ?ret <-- we are here
089            // ...
090            
091            // insert instructions
092            il.insertBeforeInstr(getFlagInstr, mi.getCodeAttributeInfo());
093            result = il.advanceIndex();
094            assert result == true;
095            il.insertBeforeInstr(sleepCallInstr, mi.getCodeAttributeInfo());
096            result = il.advanceIndex();
097            assert result == true;
098    
099            // bytecode looks like this:
100            // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_RUN)
101            // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.randomDelay)
102            // ?ret <-- we are here
103        }
104    
105        /**
106         * Insert instructions after.
107         *
108         * @param cf class file
109         * @param mi method information
110         * @param il instruction list
111         */
112        public void insertInstructionsAfter(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
113            // never going to be called
114        }
115        
116        /**
117         * Insert instructions at the end of the method.
118         * NOTE: The method is expected to move exactly to the last instruction that was inserted.
119         * If it moves to a place before the instruction, an infinite loop may occur.
120         * If it moves to a place after the instruction, future inserts may not occur.
121         * @param cf class file
122         * @param mi method information
123         * @param il instruction list
124         * @param insertBefore true if code was inserted before an opcode
125         * @param insertAfter true if code was inserted after an opcode
126         * @return true if code was inserted
127         */
128        public boolean insertEndOfMethod(final ClassFile cf, final MethodInfo mi, final InstructionList il,
129                                         boolean insertBefore, boolean insertAfter) {
130            // insert exception handler
131            int exceptionEndPC = il.getPCFromIndex(il.getIndex()-1);
132            int exceptionHandlerPC = il.getPCFromIndex(il.getIndex());
133            insertInstructionsBefore(cf,mi,il);
134    
135            // rethrow
136            CodeAttributeInfo codeAttr = mi.getCodeAttributeInfo();
137            il.insertInstr(new GenericInstruction(new byte[] {Opcode.ATHROW}), codeAttr);
138            // since we just inserted an instruction, we must be able to advance the PC
139            boolean result = il.advanceIndex();
140            assert result;
141    
142            // update exceptions list
143            CodeAttributeInfo.ExceptionTableEntry[] excTable =
144                new CodeAttributeInfo.ExceptionTableEntry[codeAttr.getExceptionTableEntries().length+1];
145            System.arraycopy(codeAttr.getExceptionTableEntries(), 0, excTable, 0, codeAttr.getExceptionTableEntries().length);
146            excTable[excTable.length-1] = new CodeAttributeInfo.ExceptionTableEntry((short)0,
147                                                                                    exceptionEndPC,
148                                                                                    exceptionHandlerPC,
149                                                                                    (short)0);
150            codeAttr.setExceptionTableEntries(excTable);
151            
152            return true;
153        }
154    
155        /**
156         * Modify the stack size and number of local variables so that the added instructions can execute. Called only once
157         * per method.
158         *
159         * @param cf           class file
160         * @param mi           method information
161         * @param il           instruction list
162         * @param insertBefore true if code was inserted before an opcode
163         * @param insertAfter  true if code was inserted after an opcode
164         * @param insertEndOfMethod true if code was inserted at the end of the method
165         */
166        public void modifyStackAndLocals(final ClassFile cf, final MethodInfo mi, final InstructionList il, boolean insertBefore,
167                                         boolean insertAfter, boolean insertEndOfMethod) {
168            // call to a static method with one parameters, one stack space required
169            // make sure we have at least one slot on the stack
170            CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties();
171            cProps.maxStack = Math.max(1, cProps.maxStack);
172            mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals);
173        }
174    }