1
2
3
4 package joeq.Main;
5
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.HashMap;
9 import java.util.Iterator;
10 import java.util.LinkedList;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.jar.JarEntry;
14 import java.util.jar.JarOutputStream;
15 import java.io.DataOutputStream;
16 import java.io.FileOutputStream;
17 import java.io.IOException;
18 import joeq.Class.PrimordialClassLoader;
19 import joeq.Class.jq_Class;
20 import joeq.Class.jq_ConstantPool;
21 import joeq.Class.jq_Field;
22 import joeq.Class.jq_Method;
23 import joeq.Class.jq_MethodVisitor;
24 import joeq.Class.jq_StaticMethod;
25 import joeq.Class.jq_Type;
26 import joeq.Class.jq_TypeVisitor;
27 import joeq.Compiler.BytecodeAnalysis.Bytecodes;
28 import jwutil.collections.AppendIterator;
29 import jwutil.strings.Strings;
30
31 /***
32 * Instrument
33 *
34 * @author John Whaley
35 * @version $Id: Instrument.java 2244 2005-04-19 08:55:04Z joewhaley $
36 */
37 public abstract class Instrument {
38
39 static JarOutputStream jos;
40
41 public static void main(String[] args) throws Exception {
42 HostedVM.initialize();
43
44 initialize();
45
46 if (args.length == 0) {
47 System.out.println("Usage: java joeq.Main.Instrument <classnames>");
48 return;
49 }
50
51 System.out.println("Class path is "+PrimordialClassLoader.loader.classpathToString());
52
53 String jarfilename = System.getProperty("jarname", "app.jar");
54 FileOutputStream fos = new FileOutputStream(jarfilename);
55 jos = new JarOutputStream(fos);
56
57 List classes;
58 if (args[0].equals("-@")) {
59 java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
60 classes = new LinkedList();
61 for (;;) {
62 String s = in.readLine();
63 if (s == null || s.length() == 0) break;
64 classes.add(s);
65 }
66 } else {
67 classes = Arrays.asList(args);
68 }
69
70 ClassVisitor cv = new ClassVisitor();
71 for (Iterator i = classes.iterator(); i.hasNext(); ) {
72 String arg = (String) i.next();
73 if (arg.endsWith(".class"))
74 arg = arg.substring(0, arg.length()-6);
75 int j = arg.indexOf("[Loaded ");
76 if (j >= 0) {
77 int endIndex = arg.indexOf(" from ");
78 if (endIndex < 0) endIndex = arg.length()-1;
79 arg = arg.substring(j+8, endIndex);
80 }
81 try {
82 jq_Class c = (jq_Class) jq_Type.parseType(arg);
83 c.load();
84 cv.visitClass(c);
85 } catch (Throwable x) {
86 System.err.println("Error while instrumenting class "+arg+": "+x.toString());
87 x.printStackTrace();
88 }
89 }
90
91 jos.close();
92 }
93
94 static void initialize() {
95 instrument_class = (jq_Class) PrimordialClassLoader.loader.getOrCreateBSType("LInstrumentationCallbacks;");
96 instrument_class.load();
97 getstatic = instrument_class.getOrCreateStaticMethod("getstatic", "(Ljava/lang/String;ILjava/lang/String;)V");
98 putstatic = instrument_class.getOrCreateStaticMethod("putstatic", "(Ljava/lang/String;ILjava/lang/String;)V");
99 getfield = instrument_class.getOrCreateStaticMethod("getfield", "(Ljava/lang/Object;Ljava/lang/String;ILjava/lang/String;)V");
100 putfield = instrument_class.getOrCreateStaticMethod("putfield", "(Ljava/lang/Object;Ljava/lang/String;ILjava/lang/String;)V");
101 arrayload = instrument_class.getOrCreateStaticMethod("arrayload", "(Ljava/lang/Object;ILjava/lang/String;I)V");
102 arraystore = instrument_class.getOrCreateStaticMethod("arraystore", "(Ljava/lang/Object;ILjava/lang/String;I)V");
103 entered = instrument_class.getOrCreateStaticMethod("entered", "(Ljava/lang/String;)V");
104 returned = instrument_class.getOrCreateStaticMethod("returned", "(Ljava/lang/String;I)V");
105 thrown = instrument_class.getOrCreateStaticMethod("thrown", "(Ljava/lang/String;)V");
106 beforeinvoke = instrument_class.getOrCreateStaticMethod("beforeinvoke", "(Ljava/lang/String;)V");
107 afterinvoke = instrument_class.getOrCreateStaticMethod("afterinvoke", "(Ljava/lang/String;)V");
108 }
109
110 public static boolean trace = true;
111
112 public static class ClassVisitor
113 extends jq_TypeVisitor.EmptyVisitor {
114
115 jq_ConstantPool.ConstantPoolRebuilder cpa;
116 jq_MethodVisitor mv;
117
118 ClassVisitor() {
119 this.mv = new MethodVisitor();
120 }
121
122 public void visitClass(jq_Class k) {
123 if (trace) System.out.print(k.toString()+"\r");
124
125 k.removeAttribute("InnerClasses");
126
127
128 cpa = k.rebuildConstantPool(true);
129
130 map = new HashMap();
131 Iterator it = new AppendIterator(Arrays.asList(k.getDeclaredStaticMethods()).iterator(),
132 Arrays.asList(k.getDeclaredInstanceMethods()).iterator());
133 while (it.hasNext()) {
134 method = (jq_Method) it.next();
135 mv.visitMethod(method);
136 if (method.getBytecode() != null) {
137 method.setMaxStack((char)(method.getMaxStack()+4));
138 method.setMaxLocals((char)(method.getMaxLocals()+2));
139 }
140 }
141
142 jq_ConstantPool new_cp = cpa.finish();
143 k.remakeAttributes(cpa);
144 k.setCP(new_cp);
145
146 for (Iterator i=map.entrySet().iterator(); i.hasNext(); ) {
147 Map.Entry e = (Map.Entry) i.next();
148 jq_Method m = (jq_Method) e.getKey();
149 List v = (List) e.getValue();
150 Bytecodes.InstructionList il = (Bytecodes.InstructionList) v.get(0);
151 Bytecodes.CodeException[] ex_table = (Bytecodes.CodeException[]) v.get(1);
152 Bytecodes.LineNumber[] line_num = (Bytecodes.LineNumber[]) v.get(2);
153 m.setCode(il, ex_table, line_num, cpa);
154 }
155
156 try {
157 String filename = k.getName().replace('.', '/') + ".class";
158 JarEntry e = new JarEntry(filename);
159 jos.putNextEntry(e);
160 DataOutputStream d = new DataOutputStream(jos);
161 k.dump(d);
162 d.flush(); jos.flush();
163 jos.closeEntry();
164 } catch (IOException x) {
165 System.err.println("IO exception when writing to class "+k);
166 }
167
168 if (trace) System.out.println(Strings.left(k.toString(), 78));
169 }
170
171 Map map;
172
173 public class MethodVisitor extends jq_MethodVisitor.EmptyVisitor {
174 public void visitMethod(jq_Method m) {
175 m.removeAttribute("Exceptions");
176
177 byte[] bc = m.getBytecode();
178 if (bc == null) {
179 return;
180 }
181 if (trace) System.out.print(Strings.left(m.toString(), 78)+"\r");
182
183 il = new Bytecodes.InstructionList(m);
184
185 List ex_table = new ArrayList(Arrays.asList(m.getExceptionTable(il)));
186 Bytecodes.LineNumber[] line_num = m.getLineNumberTable(il);
187
188 instrument(il, cpa);
189
190 Bytecodes.InstructionHandle e_start = il.getStart();
191 Bytecodes.InstructionHandle e_end = il.getEnd();
192 Bytecodes.InstructionHandle eh_entry = addExceptionHandler(il, cpa);
193 Bytecodes.CodeException eh = new Bytecodes.CodeException(e_start, e_end, null, eh_entry);
194 ex_table.add(eh);
195
196 cpa.addCode(il);
197
198 ArrayList my_list = new ArrayList();
199 my_list.add(il);
200 my_list.add(ex_table.toArray(new Bytecodes.CodeException[ex_table.size()]));
201 my_list.add(line_num);
202
203 map.put(m, my_list);
204 }
205 }
206 }
207
208 static jq_Class instrument_class;
209 static jq_StaticMethod getstatic;
210 static jq_StaticMethod putstatic;
211 static jq_StaticMethod getfield;
212 static jq_StaticMethod putfield;
213 static jq_StaticMethod arrayload;
214 static jq_StaticMethod arraystore;
215 static jq_StaticMethod entered;
216 static jq_StaticMethod returned;
217 static jq_StaticMethod thrown;
218 static jq_StaticMethod beforeinvoke;
219 static jq_StaticMethod afterinvoke;
220
221
222 static jq_Method method;
223 static Bytecodes.InstructionList il;
224 static Bytecodes.InstructionHandle ih;
225 static int bc_index;
226 static String s1;
227 static jq_ConstantPool.ConstantPoolRebuilder cpadder;
228
229 static Bytecodes.Visitor v = new Bytecodes.EmptyVisitor() {
230 Bytecodes.InstructionHandle pushLocation() {
231 Bytecodes.PUSH p1 = new Bytecodes.PUSH(cpadder, s1);
232 Bytecodes.InstructionHandle ih1 = il.insert(ih, p1);
233 Bytecodes.PUSH p2 = new Bytecodes.PUSH(bc_index);
234 il.insert(ih, p2);
235 return ih1;
236 }
237 void pushFieldName(Bytecodes.FieldInstruction obj) {
238 jq_Field f = obj.getField();
239 String s2 = f.toString();
240 Bytecodes.PUSH p3 = new Bytecodes.PUSH(cpadder, s2);
241 il.insert(ih, p3);
242 }
243
244 public void visitGETSTATIC(Bytecodes.GETSTATIC obj) {
245 Bytecodes.InstructionHandle ih1 = pushLocation();
246 pushFieldName(obj);
247 Bytecodes.INVOKESTATIC is = new Bytecodes.INVOKESTATIC(getstatic);
248 il.insert(ih, is);
249 il.redirectBranches(ih, ih1);
250 }
251
252 public void visitPUTSTATIC(Bytecodes.PUTSTATIC obj) {
253 Bytecodes.InstructionHandle ih1 = pushLocation();
254 pushFieldName(obj);
255 Bytecodes.INVOKESTATIC is = new Bytecodes.INVOKESTATIC(putstatic);
256 il.insert(ih, is);
257 il.redirectBranches(ih, ih1);
258 }
259
260 public void visitGETFIELD(Bytecodes.GETFIELD obj) {
261 Bytecodes.DUP d = new Bytecodes.DUP();
262 Bytecodes.InstructionHandle ih1 = il.insert(ih, d);
263 pushLocation();
264 pushFieldName(obj);
265 Bytecodes.INVOKESTATIC is = new Bytecodes.INVOKESTATIC(getfield);
266 il.insert(ih, is);
267 il.redirectBranches(ih, ih1);
268 }
269
270 public void visitPUTFIELD(Bytecodes.PUTFIELD obj) {
271 Bytecodes.Instruction d;
272 Bytecodes.InstructionHandle ih1;
273 if (obj.getType().getReferenceSize() == 8) {
274 d = new Bytecodes.DUP2_X1();
275 ih1 = il.insert(ih, d);
276 d = new Bytecodes.POP2();
277 il.insert(ih, d);
278 d = new Bytecodes.DUP_X2();
279 il.insert(ih, d);
280 } else {
281 d = new Bytecodes.DUP2();
282 ih1 = il.insert(ih, d);
283 d = new Bytecodes.POP();
284 il.insert(ih, d);
285 }
286 pushLocation();
287 pushFieldName(obj);
288 Bytecodes.INVOKESTATIC is = new Bytecodes.INVOKESTATIC(putfield);
289 il.insert(ih, is);
290 il.redirectBranches(ih, ih1);
291 }
292
293 public void visitReturnInstruction(Bytecodes.ReturnInstruction obj) {
294 Bytecodes.InstructionHandle ih1;
295 ih1 = pushLocation();
296 Bytecodes.INVOKESTATIC is = new Bytecodes.INVOKESTATIC(returned);
297 il.insert(ih, is);
298 il.redirectBranches(ih, ih1);
299 }
300
301 public void visitInvokeInstruction(Bytecodes.InvokeInstruction obj) {
302 jq_Class c = obj.getMethod().getDeclaringClass();
303 if (c == instrument_class) return;
304 Bytecodes.InstructionHandle ih1;
305 String s2 = c.getName()+'.'+obj.getMethodName();
306 Bytecodes.PUSH p3 = new Bytecodes.PUSH(cpadder, s2);
307 ih1 = il.insert(ih, p3);
308 Bytecodes.INVOKESTATIC is = new Bytecodes.INVOKESTATIC(beforeinvoke);
309 il.insert(ih, is);
310 il.redirectBranches(ih, ih1);
311
312 Bytecodes.INVOKESTATIC is2 = new Bytecodes.INVOKESTATIC(afterinvoke);
313 il.append(ih, is2);
314 il.append(ih, p3);
315
316
317
318
319
320 }
321
322 public void visitArrayInstruction(Bytecodes.ArrayInstruction obj) {
323 jq_StaticMethod m;
324 Bytecodes.InstructionHandle ih1;
325 if (obj instanceof Bytecodes.IASTORE ||
326 obj instanceof Bytecodes.AASTORE ||
327 obj instanceof Bytecodes.FASTORE ||
328 obj instanceof Bytecodes.BASTORE ||
329 obj instanceof Bytecodes.CASTORE ||
330 obj instanceof Bytecodes.SASTORE) {
331 m = arraystore;
332 Bytecodes.Instruction d;
333 d = new Bytecodes.DUP_X2();
334 ih1 = il.insert(ih, d);
335 d = new Bytecodes.POP();
336 il.insert(ih, d);
337 d = new Bytecodes.DUP2_X1();
338 il.insert(ih, d);
339 } else if (obj instanceof Bytecodes.LASTORE) {
340 m = arraystore;
341 Bytecodes.Instruction d;
342 d = new Bytecodes.LSTORE(method.getMaxLocals());
343 ih1 = il.insert(ih, d);
344 d = new Bytecodes.DUP2();
345 il.insert(ih, d);
346 pushLocation();
347 Bytecodes.INVOKESTATIC is = new Bytecodes.INVOKESTATIC(m);
348 il.insert(ih, is);
349 d = new Bytecodes.LLOAD(method.getMaxLocals());
350 il.insert(ih, d);
351 il.redirectBranches(ih, ih1);
352 return;
353 } else if (obj instanceof Bytecodes.DASTORE) {
354 m = arraystore;
355 Bytecodes.Instruction d;
356 d = new Bytecodes.DSTORE(method.getMaxLocals());
357 ih1 = il.insert(ih, d);
358 d = new Bytecodes.DUP2();
359 il.insert(ih, d);
360 pushLocation();
361 Bytecodes.INVOKESTATIC is = new Bytecodes.INVOKESTATIC(m);
362 il.insert(ih, is);
363 d = new Bytecodes.DLOAD(method.getMaxLocals());
364 il.insert(ih, d);
365 il.redirectBranches(ih, ih1);
366 return;
367 } else if (obj instanceof Bytecodes.IALOAD ||
368 obj instanceof Bytecodes.AALOAD ||
369 obj instanceof Bytecodes.FALOAD ||
370 obj instanceof Bytecodes.BALOAD ||
371 obj instanceof Bytecodes.CALOAD ||
372 obj instanceof Bytecodes.SALOAD ||
373 obj instanceof Bytecodes.LALOAD ||
374 obj instanceof Bytecodes.DALOAD
375 ) {
376 m = arrayload;
377 Bytecodes.Instruction d;
378 d = new Bytecodes.DUP2();
379 ih1 = il.insert(ih, d);
380 } else {
381 return;
382 }
383 pushLocation();
384 Bytecodes.INVOKESTATIC is = new Bytecodes.INVOKESTATIC(m);
385 il.insert(ih, is);
386 il.redirectBranches(ih, ih1);
387 }
388 };
389
390 public static Bytecodes.InstructionHandle addExceptionHandler(Bytecodes.InstructionList il, jq_ConstantPool.ConstantPoolRebuilder cp) {
391 Bytecodes.PUSH p1 = new Bytecodes.PUSH(cpadder, s1);
392 Bytecodes.InstructionHandle eh_ih = il.append(p1);
393 Bytecodes.INVOKESTATIC is = new Bytecodes.INVOKESTATIC(thrown);
394 il.append(is);
395 Bytecodes.ATHROW at = new Bytecodes.ATHROW();
396 il.append(at);
397 return eh_ih;
398 }
399 public static void instrument(Bytecodes.InstructionList il, jq_ConstantPool.ConstantPoolRebuilder cp) {
400 cpadder = cp;
401 s1 = method.getDeclaringClass()+"."+method.getName()+method.getDesc();
402
403 Bytecodes.INVOKESTATIC is = new Bytecodes.INVOKESTATIC(entered);
404 il.insert(is);
405 Bytecodes.PUSH p1 = new Bytecodes.PUSH(cpadder, s1+"@-1");
406 il.insert(p1);
407
408 for (ih = il.getStart(); ih != null; ih = ih.getNext()) {
409 bc_index = ih.getPosition();
410 ih.accept(v);
411 }
412 }
413
414 }
415