View Javadoc

1   // Instrument.java, created Sun May 25  5 11:14:04 2003 by joewhaley
2   // Copyright (C) 2001-3 John Whaley <jwhaley@alum.mit.edu>
3   // Licensed under the terms of the GNU LGPL; see COPYING for details.
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             //cpa = new jq_ConstantPool.ConstantPoolRebuilder();
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                 // extract instructions of method.
183                 il = new Bytecodes.InstructionList(m);
184                 // extract exception tables and line numbers of method.
185                 List ex_table = new ArrayList(Arrays.asList(m.getExceptionTable(il)));
186                 Bytecodes.LineNumber[] line_num = m.getLineNumberTable(il);
187                 // add instrumentation.
188                 instrument(il, cpa);
189                 // add catch-all exception handler.
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                 // update constant pool.
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     // used by visitor object.
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             //il.insert(ih, obj);
312             Bytecodes.INVOKESTATIC is2 = new Bytecodes.INVOKESTATIC(afterinvoke);
313             il.append(ih, is2);
314             il.append(ih, p3);
315             //try {
316             //    il.delete(ih);
317             //} catch (Bytecodes.TargetLostException x) {
318             //    Assert.UNREACHABLE(x.toString());
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