001 package edu.rice.cs.cunit.instrumentors;
002
003 import edu.rice.cs.cunit.classFile.ClassFile;
004 import edu.rice.cs.cunit.classFile.MethodInfo;
005 import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo;
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.GenericInstruction;
009 import edu.rice.cs.cunit.classFile.code.instructions.ReferenceInstruction;
010 import edu.rice.cs.cunit.classFile.constantPool.MethodPoolInfo;
011 import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckMethodVisitor;
012
013 /**
014 * Instrumentation strategy to replace static calls to certain marker methods with inlined bytecode.
015 * 1) "invokestatic SyncPointBuffer.monitorEnter" -> "monitorenter".
016 * 2) "invokestatic SyncPointBuffer.monitorExit" -> "monitorexit".
017 * 3) "invokestatic SyncPointBuffer.isOldThread"
018 * -> "invokestatic java/lang/Thread.currentThread; getfield java/lang/Thread.$$$oldThread$$$"
019 * 4) "invokestatic SyncPointBuffer.setOldThread"
020 * -> "iconst_1; invokestatic java/lang/Thread.currentThread; putfield java/lang/Thread.$$$oldThread$$$"
021 * @author Mathias Ricken
022 */
023 public class MarkerInlineStrategy implements IInstrumentationStrategy {
024 /**
025 * monitorenter instruction.
026 */
027 private final GenericInstruction _monitorEnter = new GenericInstruction(new byte[]{Opcode.MONITORENTER});
028
029 /**
030 * monitorexit instruction.
031 */
032 private final GenericInstruction _monitorExit = new GenericInstruction(new byte[]{Opcode.MONITOREXIT});
033
034 /**
035 * iconst_1 instruction.
036 */
037 private final GenericInstruction _iconst_1 = new GenericInstruction(new byte[]{Opcode.ICONST_1});
038
039 /**
040 * getfield java/lang/Thread.$$$oldThread$$$ instruction.
041 */
042 final private ReferenceInstruction _getOldThreadFieldInstr = new ReferenceInstruction(Opcode.GETFIELD, (short)0);
043
044 /**
045 * putfield java/lang/Thread.$$$oldThread$$$ instruction.
046 */
047 final private ReferenceInstruction _putOldThreadFieldInstr = new ReferenceInstruction(Opcode.PUTFIELD, (short)0);
048
049 /**
050 * invokestatic java/lang/Thread.currentThread()Ljava/lang/Thread; instruction
051 */
052 final private ReferenceInstruction _invokeCurrentThreadInstr
053 = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
054
055 /**
056 * True if names still have to be added.
057 */
058 private boolean _addNames;
059
060 /**
061 * Instrument the class.
062 *
063 * @param cf class file info
064 */
065 public void instrument(ClassFile cf) {
066 _addNames = true;
067 for(MethodInfo mi : cf.getMethods()) {
068 if ((mi.getAccessFlags() & (ClassFile.ACC_NATIVE | ClassFile.ACC_ABSTRACT)) == 0) {
069 // not a native method, should have a body
070 boolean changed = false;
071 int stackIncrease = 0;
072 InstructionList il = new InstructionList(mi.getCodeAttributeInfo().getCode());
073
074 // instrument monitorenter
075 do {
076 if (il.getOpcode() == Opcode.INVOKESTATIC) {
077 ReferenceInstruction ri = (ReferenceInstruction)il.getInstr();
078 MethodPoolInfo ref = cf.getConstantPoolItem(ri.getReference())
079 .execute(CheckMethodVisitor.singleton(), null);
080 if (ref.getClassInfo().getName().toString().equals("edu/rice/cs/cunit/SyncPointBuffer")) {
081 if ((ref.getNameAndType().getName().toString().equals("monitorEnter")) &&
082 (ref.getNameAndType().getDescriptor().toString().equals("(Ljava/lang/Object;)V"))) {
083 // insert the instruction while keeping jump targets the same
084 il.insertInstr(_monitorEnter, mi.getCodeAttributeInfo());
085 boolean ret = il.advanceIndex();
086 assert ret; // we inserted an instruction, so we should be able to advance
087 il.deleteInstr(mi.getCodeAttributeInfo());
088 ret = il.rewindIndex();
089 assert ret; // we inserted an instruction, so we should be able to rewind
090 changed = true;
091 }
092 else if ((ref.getNameAndType().getName().toString().equals("monitorExit")) &&
093 (ref.getNameAndType().getDescriptor().toString()
094 .equals("(Ljava/lang/Object;)V"))) {
095 // insert the instruction while keeping jump targets the same
096 il.insertInstr(_monitorExit, mi.getCodeAttributeInfo());
097 boolean ret = il.advanceIndex();
098 assert ret; // we inserted an instruction, so we should be able to advance
099 il.deleteInstr(mi.getCodeAttributeInfo());
100 ret = il.rewindIndex();
101 assert ret; // we inserted an instruction, so we should be able to rewind
102 changed = true;
103 }
104 else if ((ref.getNameAndType().getName().toString().equals("isOldThread")) &&
105 (ref.getNameAndType().getDescriptor().toString().equals("()Z"))) {
106 addNamesIfNecessary(cf);
107 // insert the instructions while keeping jump targets the same
108 il.insertInstr(_getOldThreadFieldInstr, mi.getCodeAttributeInfo());
109 il.insertInstr(_invokeCurrentThreadInstr, mi.getCodeAttributeInfo());
110 boolean ret = il.advanceIndex(2);
111 assert ret; // we inserted two instructions, so we should be able to advance
112 il.deleteInstr(mi.getCodeAttributeInfo());
113 ret = il.rewindIndex();
114 assert ret; // we inserted instructions, so we should be able to rewind
115 changed = true;
116 stackIncrease = Math.max(stackIncrease, 1);
117 }
118 else if ((ref.getNameAndType().getName().toString().equals("setOldThread")) &&
119 (ref.getNameAndType().getDescriptor().toString().equals("()V"))) {
120 addNamesIfNecessary(cf);
121 // insert the instruction while keeping jump targets the same
122 il.insertInstr(_putOldThreadFieldInstr, mi.getCodeAttributeInfo());
123 il.insertInstr(_iconst_1, mi.getCodeAttributeInfo());
124 il.insertInstr(_invokeCurrentThreadInstr, mi.getCodeAttributeInfo());
125 boolean ret = il.advanceIndex(3);
126 assert ret; // we inserted three instructions, so we should be able to advance
127 il.deleteInstr(mi.getCodeAttributeInfo());
128 ret = il.rewindIndex();
129 assert ret; // we inserted instructions, so we should be able to rewind
130 changed = true;
131 stackIncrease = Math.max(stackIncrease, 2);
132 }
133 }
134 }
135 } while(il.advanceIndex());
136 if (changed) {
137 // write code back
138 mi.getCodeAttributeInfo().setCode(il.getCode());
139
140 // make sure we have four more slots on the stack
141 CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties();
142 cProps.maxStack += stackIncrease;
143 mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals);
144 }
145 }
146 }
147 }
148
149 /**
150 * Add names to the constant pool if necessary.
151 * @param cf class file
152 */
153 private void addNamesIfNecessary(ClassFile cf) {
154 // see if we need to add the names and constant
155 if (_addNames) {
156 _addNames = false;
157 int oldThreadFieldIndex = cf.addField("java/lang/Thread", "$$$oldThread$$$", "Z", false, (short)0);
158 _getOldThreadFieldInstr.setReference(oldThreadFieldIndex);
159 _putOldThreadFieldInstr.setReference(oldThreadFieldIndex);
160
161 // add the names for the call to Thread.currentThread()
162 int currentThreadMethodIndex = cf.addMethodToConstantPool("java/lang/Thread",
163 "currentThread",
164 "()Ljava/lang/Thread;");
165 _invokeCurrentThreadInstr.setReference(currentThreadMethodIndex);
166 }
167 }
168
169 /**
170 * Instrumentation of all classes is done.
171 */
172 public void done() {
173 // nothing to do
174 }
175 }