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.ClassFileTools;
006    import edu.rice.cs.cunit.classFile.MethodInfo;
007    import edu.rice.cs.cunit.classFile.constantPool.MethodPoolInfo;
008    import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckMethodVisitor;
009    import edu.rice.cs.cunit.classFile.code.InstructionList;
010    import edu.rice.cs.cunit.classFile.code.Opcode;
011    import edu.rice.cs.cunit.classFile.code.instructions.ReferenceInstruction;
012    import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo;
013    import edu.rice.cs.cunit.util.IPredicate;
014    
015    /**
016     * Instrumentation strategy that adds a delay before calls to Object.notify and Object.notifyAll.
017     *
018     * @author Mathias Ricken
019     */
020    public class CallOnObjectNotifyStrategy extends AInsertAtOpcodeStrategy {
021        /**
022         * Name of the class containing the method to be called.
023         */
024        protected String _className;
025    
026        /**
027         * Name of the method to be called.
028         */
029        protected String _methodName;
030            
031        /**
032         * Name of the class containing the pre-enabled flag.
033         */
034        protected String _preVarClassName;
035    
036        /**
037         * Name of the pre-enabled flag.
038         */
039        protected String _preVarName;
040            
041        /**
042         * Name of the class containing the post-enabled flag.
043         */
044        protected String _postVarClassName;
045    
046        /**
047         * Name of the post-enabled flag.
048         */
049        protected String _postVarName;
050    
051        /**
052         * Create a new strategy.
053         * @param className name of the class containing the method to be called
054         * @param methodName name of the method to be called
055         * @param preVarClassName name of the class containing the pre-enabled flag
056         * @param preVarName name of the pre-enabled flag
057         * @param postVarClassName name of the class containing the post-enabled flag
058         * @param postVarName name of the post-enabled flag
059         */
060        public CallOnObjectNotifyStrategy(final String className, String methodName,
061                                          String preVarClassName, String preVarName,
062                                          String postVarClassName, String postVarName) {
063            super(new IPredicate<ClassFile>() {
064                public Boolean apply(ClassFile param) {
065                    // do not instrument the standard Java API
066                    return ClassFileTools.classNameMatches(param.getThisClassName(),
067                                                           "***", // any class, but none of the following
068                                                           "!java.***",
069                                                           "!javax.***",
070                                                           "!com.sun.***",
071                                                           "!edu.rice.cs.cunit.SyncPointBuffer",
072                                                           "!"+className.replace('/','.'),
073                                                           "!sun.***",
074                                                           "!sunw.***");
075                }
076            }, new IPredicate.Binary<ClassFile, MethodInfo>() {
077                public Boolean apply(ClassFile cf, MethodInfo param) {
078                    // instrument all methods
079                    return true;
080                }
081            }, new IPredicate.Ternary<ClassFile, MethodInfo, InstructionList>() {
082                public Boolean apply(ClassFile cf, MethodInfo mi, InstructionList il) {
083                    if (il.getOpcode() != Opcode.INVOKEVIRTUAL) {
084                        return false;
085                    }
086                    int methodIndex = ((ReferenceInstruction)il.getInstr()).getReference();
087                    MethodPoolInfo mpi = cf.getConstantPoolItem(methodIndex).execute(CheckMethodVisitor.singleton(), null);
088                    return (mpi.getClassInfo().getName().toString().equals("java/lang/Object")) &&
089                           ((mpi.getNameAndType().getName().toString().equals("notify")) || (mpi.getNameAndType().getName().toString().equals("notifyAll"))) &&
090                           (mpi.getNameAndType().getDescriptor().toString().equals("()V"));
091                }
092            }, OPCODE_NEVER);
093            _className = className;
094            _methodName = methodName;
095            _preVarClassName = preVarClassName;
096            _preVarName = preVarName; 
097            _postVarClassName = postVarClassName;
098            _postVarName = postVarName; 
099        }
100    
101        /**
102         * Add instructions to call SyncPointBuffer.randomDelay before calls to Object.notify and Object.notifyAll.
103         * @param cf class file
104         * @param mi method information
105         * @param il instruction list
106         */
107        public void insertInstructionsBefore(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
108            boolean result;
109    
110            ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0);
111            int getFlagIndex = 0;
112    
113            getFlagIndex = cf.addField(_preVarClassName, _preVarName, "Z", false, (short)0);
114            getFlagInstr.setReference(getFlagIndex);
115            
116            ReferenceInstruction sleepCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
117            int sleepCallIndex = 0;
118    
119            sleepCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V");
120            sleepCallInstr.setReference(sleepCallIndex);
121    
122            // bytecode looks like this:
123            // invokevirtual (java/lang/Object.notify/notifyAll) <-- we are here
124            // ...
125    
126            // insert instructions
127            il.insertBeforeInstr(getFlagInstr, mi.getCodeAttributeInfo());
128            result = il.advanceIndex();
129            assert result == true;
130            il.insertBeforeInstr(sleepCallInstr, mi.getCodeAttributeInfo());
131            result = il.advanceIndex();
132            assert result == true;
133    
134            // bytecode looks like this:
135            // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_PRE_NOTIFY)
136            // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.randomDelay)
137            // invokevirtual (java/lang/Object.notify/notifyAll) <-- we are here
138        }
139    
140        /**
141         * Add instructions to call SyncPointBuffer.randomDelay after calls to Object.notify and Object.notifyAll.
142         * @param cf class file
143         * @param mi method information
144         * @param il instruction list
145         */
146        public void insertInstructionsAfter(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
147            boolean result;
148    
149            ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0);
150            int getFlagIndex = 0;
151    
152            getFlagIndex = cf.addField(_postVarClassName, _postVarName, "Z", false, (short)0);
153            getFlagInstr.setReference(getFlagIndex);
154            
155            ReferenceInstruction sleepCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
156            int sleepCallIndex = 0;
157    
158            sleepCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V");
159            sleepCallInstr.setReference(sleepCallIndex);
160    
161            // bytecode looks like this:
162            // invokevirtual (java/lang/Object.notify/notifyAll) <-- we are here
163            // ...
164    
165            // advance past the instruction
166            result = il.advanceIndex();
167            assert result == true;
168    
169            // insert instructions
170            il.insertBeforeInstr(getFlagInstr, mi.getCodeAttributeInfo());
171            result = il.advanceIndex();
172            assert result == true;
173            
174            il.insertBeforeInstr(sleepCallInstr, mi.getCodeAttributeInfo());
175    
176            // do not advance past this new instruction, because the postcondition of this method is that we
177            // are exactly before the instruction that followed the original instruction
178    
179            // bytecode looks like this:
180            // ...
181            // invokevirtual (java/lang/Object.notify/notifyAll)
182            // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_POST_NOTIFY)
183            // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.randomDelay) <-- we are here
184        }
185        
186        /**
187         * Insert instructions at the end of the method.
188         * NOTE: The method is expected to move exactly to the last instruction that was inserted.
189         * If it moves to a place before the instruction, an infinite loop may occur.
190         * If it moves to a place after the instruction, future inserts may not occur.
191         * @param cf class file
192         * @param mi method information
193         * @param il instruction list
194         * @param insertBefore true if code was inserted before an opcode
195         * @param insertAfter true if code was inserted after an opcode
196         * @return true if code was inserted
197         */
198        public boolean insertEndOfMethod(final ClassFile cf, final MethodInfo mi, final InstructionList il,
199                                         boolean insertBefore, boolean insertAfter) {
200            return false;
201        }
202    
203        /**
204         * Modify the stack size and number of local variables so that the added instructions can execute. Called only once
205         * per method.
206         *
207         * @param cf           class file
208         * @param mi           method information
209         * @param il           instruction list
210         * @param insertBefore true if code was inserted before an opcode
211         * @param insertAfter  true if code was inserted after an opcode
212         * @param insertEndOfMethod true if code was inserted at the end of the method
213         */
214        public void modifyStackAndLocals(final ClassFile cf, final MethodInfo mi, final InstructionList il, boolean insertBefore,
215                                         boolean insertAfter, boolean insertEndOfMethod) {
216            // call to a static method with one parameters, one stack space required
217            // make sure we have at least one slot on the stack
218            CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties();
219            cProps.maxStack = Math.max(1, cProps.maxStack);
220            mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals);
221        }
222    }