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 }