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.ClassFileTools;
006    import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo;
007    import edu.rice.cs.cunit.classFile.code.InstructionList;
008    import edu.rice.cs.cunit.classFile.code.Opcode;
009    import edu.rice.cs.cunit.classFile.code.instructions.ReferenceInstruction;
010    import edu.rice.cs.cunit.classFile.constantPool.ConstantPool;
011    import edu.rice.cs.cunit.SyncPointBuffer;
012    import edu.rice.cs.cunit.instrumentors.IInstrumentationStrategy;
013    
014    /**
015     * Instrumentor for synchronized blocks, using the compact sync point list.
016     * <p/>
017     * A synchronized block like <code> synchronized(o) { abc(); } </code> gets translated into <code>
018     * synchronized(o) { SyncPointBuffer.compactAdd(SP_MONITORENTER,tid); abc();
019     * SyncPointBuffer.compactAdd(SP_MONITOREXIT,tid); }</code>
020     *
021     * @author Mathias Ricken
022     */
023    public class CompactSynchronizedBlockStrategy implements IInstrumentationStrategy {
024        /**
025         * Instrument the class.
026         *
027         * @param cf class file info
028         */
029        public void instrument(ClassFile cf) {
030            //Debug.out.println("\tInstrumenting synchronized blocks");
031            // process all methods in this class
032    
033            if (!isClassInstrumentable(cf.getThisClassName())) {
034                return;
035            }
036    
037            ConstantPool cp = cf.getConstantPool();
038            ReferenceInstruction addCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
039            ReferenceInstruction loadMonitorEnterCodeIndexInstr = new ReferenceInstruction(Opcode.LDC2_W, (short)0);
040            ReferenceInstruction loadMonitorExitCodeIndexInstr = new ReferenceInstruction(Opcode.LDC2_W, (short)0);
041            ReferenceInstruction getThreadIDInstr = new ReferenceInstruction(Opcode.GETFIELD, (short)0);
042            ReferenceInstruction currentThreadCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
043            int addCallIndex = 0;
044            int monitorEnterCodeIndex = 0;
045            int monitorExitCodeIndex = 0;
046            int threadIDIndex = 0;
047            int currentThreadCallIndex = 0;
048    
049            for(MethodInfo mi : cf.getMethods()) {
050                if ((mi.getAccessFlags() & (ClassFile.ACC_NATIVE | ClassFile.ACC_ABSTRACT)) == 0) {
051                    // not a native method, should have a body
052                    //Debug.out.println("\t"+mi);
053                    boolean changed = false;
054                    InstructionList il = new InstructionList(mi.getCodeAttributeInfo().getCode());
055    
056                    // instrument monitorenter
057                    while(il.findOpcode(Opcode.MONITORENTER)) {
058                        // instruction found
059                        changed = true;
060    
061                        // see if we need to add the names and constant
062                        if (addCallIndex == 0) {
063                            addCallIndex = cf.addMethodToConstantPool("edu/rice/cs/cunit/SyncPointBuffer", "compactAdd", "(JJ)V");
064                            addCallInstr.setReference(addCallIndex);
065    
066                            // add the code for a synchronized block
067                            monitorEnterCodeIndex = cf.addLongToConstantPool(SyncPointBuffer.SP.MONITORENTER.intValue());
068                            loadMonitorEnterCodeIndexInstr.setReference(monitorEnterCodeIndex);
069    
070                            // add the field for the thread ID
071                            threadIDIndex = cf.addField("java/lang/Thread", "$$$threadID$$$", "J", false, (short)0);
072                            getThreadIDInstr.setReference(threadIDIndex);
073    
074                            // add the names for the call to Thread.currentThread()
075                            currentThreadCallIndex = cf.addMethodToConstantPool("java/lang/Thread", "currentThread", "()Ljava/lang/Thread;");
076                            currentThreadCallInstr.setReference(currentThreadCallIndex);
077                        }
078    
079                        // bytecode looks like this:
080                        // monitorenter <-- we are here
081                        // ...
082    
083                        // advance past the monitorenter instruction
084                        il.advanceIndex();
085    
086                        // insert instructions
087                        il.insertBeforeInstr(addCallInstr, mi.getCodeAttributeInfo());
088                        il.insertBeforeInstr(getThreadIDInstr, mi.getCodeAttributeInfo());
089                        il.insertBeforeInstr(currentThreadCallInstr, mi.getCodeAttributeInfo());
090                        il.insertBeforeInstr(loadMonitorEnterCodeIndexInstr, mi.getCodeAttributeInfo());
091    
092                        // the next 4 advancePCs must return true, since we wrote them ourselves
093                        boolean result = il.advanceIndex(4);
094                        assert result == true;
095    
096                        // bytecode looks like this:
097                        // monitorenter
098                        // ldc2_w SP_MONITORENTER
099                        // invokestatic (java/lang/Thread.currentThread)
100                        // getfield (java/lang/Thread.$$$threadID$$$)
101                        // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.compactAdd)
102                        // ... <-- we are here
103                    }
104    
105                    // instrument monitorexit
106                    il.setIndex(0);
107                    while(il.findOpcode(Opcode.MONITOREXIT)) {
108                        // instruction found
109                        changed = true;
110    
111                        // see if we need to add the names
112                        if (monitorExitCodeIndex == 0) {
113                            // add the code for leaving a synchronized block
114                            monitorExitCodeIndex = cf.addLongToConstantPool(SyncPointBuffer.SP.MONITOREXIT.intValue());
115                            loadMonitorExitCodeIndexInstr.setReference(monitorExitCodeIndex);
116                        }
117    
118                        // get the load instruction
119                        // bytecode looks like this:
120                        // ...
121                        // aload
122                        // monitorexit <-- we are here
123                        // ...
124    
125                        // insert instructions
126                        il.insertBeforeInstr(addCallInstr, mi.getCodeAttributeInfo());
127                        il.insertBeforeInstr(getThreadIDInstr, mi.getCodeAttributeInfo());
128                        il.insertBeforeInstr(currentThreadCallInstr, mi.getCodeAttributeInfo());
129                        il.insertBeforeInstr(loadMonitorExitCodeIndexInstr, mi.getCodeAttributeInfo());
130    
131                        // the next 4 advancePCs must return true, since we rewinded by one and added two
132                        boolean result = il.advanceIndex(4);
133                        assert result == true;
134    
135                        // now bytecode looks like this:
136                        // ...
137                        // ldc2_w SP_MONITOREXIT
138                        // invokestatic (java/lang/Thread.currentThread)
139                        // getfield (java/lang/Thread.$$$threadID$$$)
140                        // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.compactAdd)
141                        // monitorexit <-- we are here
142                        // ...
143    
144                        // try to advance past the monitorenter instruction
145                        if (!il.advanceIndex()) {
146                            break;
147                        }
148                    }
149    
150                    if (changed) {
151                        // write code back
152                        mi.getCodeAttributeInfo().setCode(il.getCode());
153    
154                        // make sure we have four more slots on the stack
155                        CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties();
156                        cProps.maxStack += 4;
157                        mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals);
158                    }
159                }
160            }
161        }
162    
163        public CompactSynchronizedBlockStrategy() {
164            // nothing to do
165        }
166    
167    
168        /**
169         * Return true if the class specified by the class name can be instrumented with the synchronized block instrumentor.
170         * @param className name of the class
171         * @return true if the class can be instrumented
172         */
173        public static boolean isClassInstrumentable(String className) {
174            // TODO: figure out why these classes have to be excluded from CompactSynchronizedBlockStrategy
175            return ClassFileTools.classNameMatches(className,
176                                                   "***",
177    
178                                                   "!java.lang.ClassLoader",
179                                                   "!java.lang.ThreadGroup",
180    
181                                                   "!java.lang.ref.***",
182    //                                               "!java.lang.ref.Reference",
183    //                                               "!java.lang.ref.ReferenceQueue",
184    //                                               "!java.lang.ref.ReferenceQueue\\$Lock",
185    
186                                                   "!java.lang.ref.Reference",
187                                                   "!java.lang.ref.ReferenceQueue",
188                                                   "!java.lang.ref.ReferenceQueue\\$Lock",
189    
190                                                   "!java.security.Permission",
191    
192                                                   "!java.util.AbstractCollection",
193                                                   "!java.util.AbstractMap",
194                                                   "!java.util.Vector",
195    
196                                                   "!sun.reflect.[n-zN-Z]*");
197        }
198    
199        /**
200         * Instrumentation of all classes is done.
201         */
202        public void done() {
203            // nothing to do
204        }
205    }