001 package edu.rice.cs.cunit.instrumentors;
002
003 import edu.rice.cs.cunit.SyncPointBuffer;
004 import edu.rice.cs.cunit.classFile.ClassFile;
005 import edu.rice.cs.cunit.classFile.MethodInfo;
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.GenericInstruction;
010 import edu.rice.cs.cunit.classFile.code.instructions.ReferenceInstruction;
011 import edu.rice.cs.cunit.classFile.constantPool.*;
012 import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckClassVisitor;
013 import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckMethodVisitor;
014 import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckUTFVisitor;
015 import edu.rice.cs.cunit.util.Types;
016
017 /**
018 * Instrumentation strategy that adds code to java.lang.Thread to maintain a unique thread ID and an "old thread"
019 * flag.
020 *
021 * NOTE: This instrumentor has to be run AFTER the CompactSynchronizedDebugStrategy, otherwise the MONITORENTER and
022 * MONITOREXIT instructions will generate multiple sync points.
023 *
024 * @author Mathias Ricken
025 */
026 public class AssignThreadIDStrategy implements IInstrumentationStrategy {
027 /**
028 * Instrument the class.
029 *
030 * @param cf class file info
031 */
032 public void instrument(final ClassFile cf) {
033 // check if this is the target class
034 if (cf.getThisClassName().equals("java.lang.Thread")) {
035 int threadID = cf.addField(cf.getThisClass().getName().toString(), "$$$threadID$$$", "J", true,
036 (short)(ClassFile.ACC_PUBLIC|ClassFile.ACC_TRANSIENT));
037 int oldThread = cf.addField(cf.getThisClass().getName().toString(), "$$$oldThread$$$", "Z", true,
038 (short)(ClassFile.ACC_PUBLIC|ClassFile.ACC_TRANSIENT));
039 int nextThreadID = cf.addField("edu/rice/cs/cunit/SyncPointBuffer", "_nextThreadID", "J", false, (short)0);
040
041 for(MethodInfo mi: cf.getMethods()) {
042 if (mi.getName().toString().equals("<init>")) {
043 processCtor(cf, mi, threadID, nextThreadID, oldThread);
044 }
045 }
046 }
047 }
048
049 /**
050 * Process this constructor.
051 * @param cf class file info
052 * @param mi method info
053 * @param threadID index of the threadID field
054 * @param nextThreadID index of the nextThreadID field
055 * @param oldThread index of the oldThread field
056 */
057 protected void processCtor(final ClassFile cf, final MethodInfo mi, int threadID, int nextThreadID, int oldThread) {
058 CodeAttributeInfo codeAttr = mi.getCodeAttributeInfo();
059 InstructionList il = new InstructionList(codeAttr.getCode());
060
061 // check if this ctor calls other ctors
062 boolean ctorCalled = false;
063 do {
064 if (il.getOpcode()==Opcode.INVOKESPECIAL) {
065 ReferenceInstruction ri = (ReferenceInstruction)il.getInstr();
066 short method = Types.shortFromBytes(ri.getBytecode(), 1);
067 MethodPoolInfo mpi = cf.getConstantPoolItem(method).execute(CheckMethodVisitor.singleton(), null);
068 if ((mpi.getClassInfo().getName().toString().equals(cf.getThisClass().getName().toString())) &&
069 (mpi.getNameAndType().getName().toString().equals("<init>"))) {
070 ctorCalled = true;
071 break;
072 }
073 }
074 } while(il.advanceIndex());
075
076 if (ctorCalled) {
077 // a ctor call was found, do not add code to this ctor
078 return;
079 }
080
081 // inserts the equivalent of the Java following code, with sync points for the MONITORENTER and MONITOREXIT
082 // synchronized(edu.rice.cs.cunit.SyncPointBuffer.class) {
083 // $$$threadID$$$ = ++edu.rice.cs.cunit.SyncPointBuffer._nextThreadID;
084 // }
085
086 // TODO: How to handle thread ID overflow?
087
088 // beginning of the method current/max stack usage
089 // 0 (pc 0): ldc2_w SP.THREADID_TRYMONITORENTER 2/ 2 <11L*2>
090 // 1 (pc 3): invokestatic (java/lang/Thread.currentThread) 3/ 3 <11L*2> <threadobj>
091 // 2 (pc 6): getfield (java/lang/Thread.$$$threadID$$$) 4/ 4 <11L*2> <tid*2>
092 // 3 (pc 9): invokestatic (edu/rice/cs/cunit/SyncPointBuffer.compactAdd) 0/ 4
093
094 // 4 (pc 12): ldc_w (edu.rice.cs.cunit.SyncPointBuffer) // put class on stack 1/ 4 <lockobj>
095 // 5 (pc 15): monitorenter // and lock 0/ 4
096
097 // 6 (pc 16): ldc2_w SP.THREADID_MONITORENTER 2/ 4 <9L*2>
098 // 7 (pc 19): invokestatic (java/lang/Thread.currentThread) 3/ 4 <9L*2> <threadobj>
099 // 8 (pc 22): getfield (java/lang/Thread.$$$threadID$$$) 4/ 4 <9L*2> <tid*2>
100 // 9 (pc 25): invokestatic (edu/rice/cs/cunit/SyncPointBuffer.compactAdd) 0/ 4
101
102 // 10 (pc 28): aload_0 1/ 4 <this>
103 // 11 (pc 29): getstatic (edu.rice.cs.cunit.SyncPointBuffer._nextThreadID) 3/ 4 <this> <nexttid*2>
104 // 12 (pc 32): dup2 5/ 5 <this> <nexttid*2> <nextoid*2>
105 // 13 (pc 33): lconst_1 7/ 7 <this> <nexttid*2> <nexttid*2> <1L*2>
106 // 14 (pc 34): ladd 5/ 7 <this> <nexttid*2> <nexttid+1*2>
107 // 15 (pc 35): putstatic (edu.rice.cs.cunit.SyncPointBuffer._nextThreadID) 3/ 7 <this> <nexttid*2>
108 // 16 (pc 38): putfield (this.$$$threadID$$$) 0/ 7
109
110 // 17 (pc 41): ldc2_w SP.THREADID_MONITOREXIT 2/ 7 <10L*2>
111 // 18 (pc 44): invokestatic (java/lang/Thread.currentThread) 3/ 7 <10L*2> <threadobj>
112 // 19 (pc 47): getfield (java/lang/Thread.$$$threadID$$$) 4/ 7 <10L*2> <tid*2>
113 // 20 (pc 50): invokestatic (edu/rice/cs/cunit/SyncPointBuffer.compactAdd) 0/ 7
114
115 // 21 (pc 53): ldc_w (edu.rice.cs.cunit.SyncPointBuffer) // put class on stack 1/ 7 <lockobj>
116 // 22 (pc 56): monitorexit // and unlock 0/ 7
117
118 il.setIndex(0);
119 ConstantPool cp = cf.getConstantPool();
120
121 int threadIDIndex = cf.addField("java/lang/Thread", "$$$threadID$$$", "J", true,
122 (short)(ClassFile.ACC_PUBLIC | ClassFile.ACC_TRANSIENT));
123 int nextThreadIDIndex = cf.addField("edu/rice/cs/cunit/SyncPointBuffer", "_nextThreadID", "J", false,
124 (short)0);
125
126 ReferenceInstruction addCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
127 ReferenceInstruction loadMonitorEnterCodeIndexInstr = new ReferenceInstruction(Opcode.LDC2_W, (short)0);
128 ReferenceInstruction loadMonitorTryEnterCodeIndexInstr = new ReferenceInstruction(Opcode.LDC2_W, (short)0);
129 ReferenceInstruction loadMonitorExitCodeIndexInstr = new ReferenceInstruction(Opcode.LDC2_W, (short)0);
130 ReferenceInstruction getThreadIDInstr = new ReferenceInstruction(Opcode.GETFIELD, (short)0);
131 ReferenceInstruction currentThreadCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
132
133 int addCallIndex = cf.addMethodToConstantPool("edu/rice/cs/cunit/SyncPointBuffer",
134 "compactAdd",
135 "(JJ)V");
136 addCallInstr.setReference(addCallIndex);
137
138 // add the code for entering a synchronized block
139 int monitorEnterCodeIndex = cf.addLongToConstantPool(SyncPointBuffer.SP.THREADID_MONITORENTER.intValue());
140 loadMonitorEnterCodeIndexInstr.setReference(monitorEnterCodeIndex);
141
142 // add the code for trying to enter a synchronized block
143 int monitorTryEnterCodeIndex = cf.addLongToConstantPool(SyncPointBuffer.SP.THREADID_TRYMONITORENTER.intValue());
144 loadMonitorTryEnterCodeIndexInstr.setReference(monitorTryEnterCodeIndex);
145
146 // add the field for the thread ID
147 getThreadIDInstr.setReference(threadIDIndex);
148
149 // add the names for the call to Thread.currentThread()
150 int currentThreadCallIndex = cf.addMethodToConstantPool("java/lang/Thread",
151 "currentThread",
152 "()Ljava/lang/Thread;");
153 currentThreadCallInstr.setReference(currentThreadCallIndex);
154
155 // add a new name
156 AUTFPoolInfo sprClassName = new ASCIIPoolInfo("edu/rice/cs/cunit/SyncPointBuffer", cp);
157 int[] l = cf.addConstantPoolItems(new APoolInfo[]{sprClassName});
158 sprClassName = cf.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
159
160 // add a new class
161 ClassPoolInfo sprClass = new ClassPoolInfo(sprClassName, cp);
162 l = cf.addConstantPoolItems(new APoolInfo[]{sprClass});
163 sprClass = cf.getConstantPoolItem(l[0]).execute(CheckClassVisitor.singleton(), null);
164 int sprClassIndex = l[0];
165
166 // add the code for leaving a synchronized block
167 int monitorExitCodeIndex = cf.addLongToConstantPool(SyncPointBuffer.SP.THREADID_MONITOREXIT.intValue());
168 loadMonitorExitCodeIndexInstr.setReference(monitorExitCodeIndex);
169
170 boolean result;
171
172 // insert call to compactDebugAdd for THREADID_TRYMONITORENTER sync point
173 il.insertBeforeInstr(loadMonitorTryEnterCodeIndexInstr, mi.getCodeAttributeInfo());
174 result = il.advanceIndex();
175 assert result == true;
176 il.insertBeforeInstr(currentThreadCallInstr, mi.getCodeAttributeInfo());
177 result = il.advanceIndex();
178 assert result == true;
179 il.insertBeforeInstr(getThreadIDInstr, mi.getCodeAttributeInfo());
180 result = il.advanceIndex();
181 assert result == true;
182 il.insertBeforeInstr(addCallInstr, mi.getCodeAttributeInfo());
183 result = il.advanceIndex();
184 assert result == true;
185
186 // insert ldc_w (edu.rice.cs.cunit.SyncPointBuffer)
187 byte[] instr = new byte[]{Opcode.LDC_W, 0, 0};
188 Types.bytesFromShort((short)(sprClassIndex & 0xffff), instr, 1);
189 il.insertBeforeInstr(new GenericInstruction(instr), codeAttr);
190 result = il.advanceIndex();
191 assert result == true; // since we just inserted an instruction, we must be able to advance the PC
192
193 // insert monitorenter
194 il.insertBeforeInstr(new GenericInstruction(new byte[]{Opcode.MONITORENTER}), codeAttr);
195 result = il.advanceIndex();
196 assert result == true;
197
198 // insert call to compactDebugAdd for THREADID_TRYMONITORENTER sync point
199 il.insertBeforeInstr(loadMonitorEnterCodeIndexInstr, mi.getCodeAttributeInfo());
200 result = il.advanceIndex();
201 assert result == true;
202 il.insertBeforeInstr(currentThreadCallInstr, mi.getCodeAttributeInfo());
203 result = il.advanceIndex();
204 assert result == true;
205 il.insertBeforeInstr(getThreadIDInstr, mi.getCodeAttributeInfo());
206 result = il.advanceIndex();
207 assert result == true;
208 il.insertBeforeInstr(addCallInstr, mi.getCodeAttributeInfo());
209 result = il.advanceIndex();
210 assert result == true;
211
212 // insert aload_0
213 // this must happen here, or we can't get the instance for putfield in the right place on the stack
214 il.insertBeforeInstr(new GenericInstruction(new byte[]{Opcode.ALOAD_0}), codeAttr);
215 result = il.advanceIndex();
216 assert result == true;
217
218 // insert getstatic (edu.rice.cs.cunit.SyncPointBuffer._nextThreadID)
219 instr[0] = Opcode.GETSTATIC;
220 Types.bytesFromShort((short)(nextThreadIDIndex & 0xffff), instr, 1);
221 il.insertBeforeInstr(new GenericInstruction(instr), codeAttr);
222 result = il.advanceIndex();
223 assert result == true;
224
225 // insert dup2
226 il.insertBeforeInstr(new GenericInstruction(new byte[]{Opcode.DUP2}), codeAttr);
227 result = il.advanceIndex();
228 assert result == true;
229
230 // insert lconst_1
231 il.insertBeforeInstr(new GenericInstruction(new byte[]{Opcode.LCONST_1}), codeAttr);
232 result = il.advanceIndex();
233 assert result == true;
234
235 // insert ladd
236 il.insertBeforeInstr(new GenericInstruction(new byte[]{Opcode.LADD}), codeAttr);
237 result = il.advanceIndex();
238 assert result == true;
239
240 // insert putstatic (edu.rice.cs.cunit.SyncPointBuffer._nextThreadID)
241 instr[0] = Opcode.PUTSTATIC;
242 il.insertBeforeInstr(new GenericInstruction(instr), codeAttr);
243 result = il.advanceIndex();
244 assert result == true;
245
246 // insert putfield (this.$$$threadID$$$)
247 instr[0] = Opcode.PUTFIELD;
248 Types.bytesFromShort((short)(threadIDIndex & 0xffff), instr, 1);
249 il.insertBeforeInstr(new GenericInstruction(instr), codeAttr);
250 result = il.advanceIndex();
251 assert result == true;
252
253 // insert call to compactDebugAdd for OBJID_TRYMONITORENTER sync point
254 il.insertBeforeInstr(loadMonitorExitCodeIndexInstr, mi.getCodeAttributeInfo());
255 result = il.advanceIndex();
256 assert result == true;
257 il.insertBeforeInstr(currentThreadCallInstr, mi.getCodeAttributeInfo());
258 result = il.advanceIndex();
259 assert result == true;
260 il.insertBeforeInstr(getThreadIDInstr, mi.getCodeAttributeInfo());
261 result = il.advanceIndex();
262 assert result == true;
263 il.insertBeforeInstr(addCallInstr, mi.getCodeAttributeInfo());
264 result = il.advanceIndex();
265 assert result == true;
266
267 // insert ldc_w (edu.rice.cs.cunit.SyncPointBuffer)
268 instr[0] = Opcode.LDC_W;
269 Types.bytesFromShort((short)(sprClassIndex & 0xffff), instr, 1);
270 il.insertBeforeInstr(new GenericInstruction(instr), codeAttr);
271 result = il.advanceIndex();
272 assert result == true;
273
274 // insert monitorexit
275 il.insertBeforeInstr(new GenericInstruction(new byte[]{Opcode.MONITOREXIT}), codeAttr);
276 result = il.advanceIndex();
277 assert result == true;
278
279 // write code back
280 codeAttr.setCode(il.getCode());
281
282 // make sure stack size is at least reqdStackSize
283 CodeAttributeInfo.CodeProperties codeProps = mi.getCodeAttributeInfo().getProperties();
284 codeProps.maxStack = (short)Math.max(7, codeProps.maxStack);
285 mi.getCodeAttributeInfo().setProperties(codeProps.maxStack, codeProps.maxLocals);
286 }
287
288 /**
289 * Instrumentation of all classes is done.
290 */
291 public void done() {
292 // nothing to do
293 }
294 }