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