001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.tools.oth;
014    
015    import java.io.BufferedReader;
016    import java.io.FileReader;
017    import java.lang.reflect.InvocationTargetException;
018    import java.lang.reflect.Method;
019    import java.util.StringTokenizer;
020    import java.util.Vector;
021    import org.jikesrvm.VM;
022    import org.jikesrvm.Callbacks;
023    import org.jikesrvm.classloader.Atom;
024    import org.jikesrvm.classloader.RVMClass;
025    import org.jikesrvm.classloader.RVMClassLoader;
026    import org.jikesrvm.classloader.RVMMethod;
027    import org.jikesrvm.classloader.NormalMethod;
028    import org.jikesrvm.classloader.TypeReference;
029    import org.jikesrvm.compilers.baseline.BaselineCompiler;
030    import org.jikesrvm.compilers.common.CompiledMethod;
031    import org.jikesrvm.compilers.opt.OptimizingCompilerException;
032    import org.jikesrvm.compilers.opt.OptOptions;
033    import org.jikesrvm.compilers.opt.driver.CompilationPlan;
034    import org.jikesrvm.compilers.opt.driver.OptimizationPlanner;
035    import org.jikesrvm.compilers.opt.driver.OptimizingCompiler;
036    import org.jikesrvm.runtime.Magic;
037    import org.jikesrvm.runtime.Reflection;
038    import org.jikesrvm.runtime.Time;
039    
040    /**
041     * A test harness for the optimizing compiler.
042     * <p>
043     * The role of this class is to allow the optimizing compiler
044     * to be run as an "application" to enabling selective testing and
045     * debugging of the optimizing compiler.
046     * For example, the following command line:
047     * <br>
048     *   rvm -X:h=100 org.jikesrvm.tools.oth.OptTestHarness -oc:O2 -oc:phases=true
049     *      -class hanoi -er hanoi run  -
050     * <br>
051     * invokes the opt compiler at Opt level 2 and phases=true to compile
052     * the class hanoi, it then executes the run method of class hanoi.
053     * <p>
054     * Any command that can be given to the optimizing compiler via -X:irc:<cmd>
055     * can be given to the optimizing compiler by org.jikesrvm.tools.oth.OptTestHarness via -oc:<cmd>.
056     * In addition, the org.jikesrvm.tools.oth.OptTestHarness supports the following commands:
057     * -useBootOptions           Use the same OptOptions as the bootimage compiler.
058     * -longcommandline <filename>    Read commands (one per line) from a file
059     * +baseline                      Switch default compiler to baseline
060     * -baseline                      Switch default compiler to optimizing
061     * -load  <class    >             Load a class
062     * -class <class>                 Load a class and compile all its methods
063     * -method <class> <method> [-|<descrip>] Compile method with default compiler
064     * -methodOpt <class> <method> [-|<descrip>] Compile method with opt compiler
065     * -methodBase <class> <method> [-|<descrip>] Compile method with base compiler
066     * -er <class> <method> [-|<descrip>] {args} Compile with default compiler and execute a method
067     * -performance                   Show performance results
068     */
069    class OptTestHarness {
070      static boolean DISABLE_CLASS_LOADING = false;
071      static boolean EXECUTE_WITH_REFLECTION = false;
072      static boolean EXECUTE_MAIN = false;
073      /** Default value for for compiling opt/baseline */
074      static boolean BASELINE = false;
075    
076      /**
077       * Should we print the address of compiled methods (useful for
078       * debugging)
079       */
080      static boolean PRINT_CODE_ADDRESS = true;
081    
082      /** Record and show performance of executed methods, if any */
083      static Performance perf;
084    
085      static ClassLoader cl;
086    
087      // Keep baseline and opt methods separate in list of methods
088      // to be compiled
089      static Vector<RVMMethod> optMethodVector = null;
090      static Vector<OptOptions> optOptionsVector = null;
091      static Vector<RVMMethod> baselineMethodVector = null;
092    
093      static java.lang.reflect.Method reflectoid;
094      static Object[] reflectMethodArgs;
095      static Vector<Method> reflectoidVector;
096      static Vector<RVMMethod> reflectMethodVector;
097      static Vector<Object[]> reflectMethodArgsVector;
098    
099      static RVMClass mainClass;
100      static String[] mainArgs;
101    
102      static int parseMethodArgs(TypeReference[] argDesc, String[] args, int i, Object[] methodArgs) {
103        try {
104          for (int argNum = 0; argNum < argDesc.length; ++argNum) {
105            if (argDesc[argNum].isBooleanType()) {
106              methodArgs[argNum] = Boolean.valueOf(args[++i]);
107            } else if (argDesc[argNum].isByteType()) {
108              methodArgs[argNum] = Byte.valueOf(args[++i]);
109            } else if (argDesc[argNum].isShortType()) {
110              methodArgs[argNum] = Short.valueOf(args[++i]);
111            } else if (argDesc[argNum].isIntType()) {
112              methodArgs[argNum] = Integer.valueOf(args[++i]);
113            } else if (argDesc[argNum].isLongType()) {
114              methodArgs[argNum] = Long.valueOf(args[++i]);
115            } else if (argDesc[argNum].isFloatType()) {
116              methodArgs[argNum] = Float.valueOf(args[++i]);
117            } else if (argDesc[argNum].isDoubleType()) {
118              methodArgs[argNum] = Double.valueOf(args[++i]);
119            } else if (argDesc[argNum].isCharType()) {
120              methodArgs[argNum] = args[++i].charAt(0);
121            } else if (argDesc[argNum].isClassType()) {
122              // TODO
123              System.err.println("Parsing args of type " + argDesc[argNum] + " not implemented");
124            } else if (argDesc[argNum].isArrayType()) {
125              TypeReference element = argDesc[argNum].getArrayElementType();
126              if (element.equals(TypeReference.JavaLangString)) {
127                String[] array = new String[args.length - i - 1];
128                for (int j = 0; j < array.length; j++) {
129                  array[j] = args[++i];
130                }
131                methodArgs[argNum] = array;
132              } else {// TODO
133                System.err.println("Parsing args of array of " + element + " not implemented");
134              }
135            }
136          }
137        } catch (ArrayIndexOutOfBoundsException e) {
138          throw new InternalError("Error: not enough method arguments specified on command line after -er");
139        }
140        return i;
141      }
142    
143      // if "methdesc" is "-", find the first method with "methname" in "klass",
144      // otherwise, find the method whose signature matches "methdesc"
145      static RVMMethod findDeclaredOrFirstMethod(RVMClass klass, String methname, String methdesc) {
146        if (klass == null) return null;
147        Atom methodName = Atom.findOrCreateAsciiAtom(methname);
148        Atom methodDesc = methdesc.equals("-") ? null : Atom.findOrCreateAsciiAtom(methdesc);
149    
150        for (RVMMethod method : klass.getDeclaredMethods()) {
151          if (method.getName() == methodName && ((methodDesc == null) || (methodDesc == method.getDescriptor()))) {
152            return method;
153          }
154        }
155        if (methodDesc == null) {
156          System.err.println("No method named " + methodName + " found in class " + klass);
157        } else {
158          System.err.println("No method matching " + methodName + " " + methodDesc + " found in class " + klass);
159        }
160        return null;
161      }
162    
163      static RVMClass loadClass(String s) throws ClassNotFoundException {
164        if (s.startsWith("./")) s = s.substring(2, s.length());
165        if (s.endsWith(".java")) s = s.substring(0, s.length() - 5);
166        if (s.endsWith(".class")) s = s.substring(0, s.length() - 6);
167    
168        // parse the class signature
169        if (s.startsWith("L") && s.endsWith(";")) {
170          s = s.substring(1, s.length() - 1);
171        }
172    
173        s = s.replace('.', '/');
174    
175        return (RVMClass) java.lang.JikesRVMSupport.getTypeForClass(Class.forName(s, true, cl));
176      }
177    
178      static void printFormatString() {
179        System.err.println("Format: rvm org.jikesrvm.tools.oth.OptTestHarness { <command> }");
180      }
181    
182      private static void processClass(RVMClass klass, OptOptions opts) {
183        RVMMethod[] methods = klass.getDeclaredMethods();
184        for (RVMMethod method : methods) {
185          if (!method.isAbstract() && !method.isNative()) {
186            processMethod(method, opts);
187          }
188        }
189      }
190    
191      // Wrapper applying default decision regarding opt/baseline
192      private static void processMethod(RVMMethod method, OptOptions opts) {
193        processMethod(method, opts, BASELINE);
194      }
195    
196      private static void processMethod(RVMMethod method, OptOptions opts, boolean isBaseline) {
197        if (isBaseline) {
198          // Method to be baseline compiled
199          if (!baselineMethodVector.contains(method)) {
200            baselineMethodVector.addElement(method);
201          }
202        } else if (!optMethodVector.contains(method)) {
203          // Method to be opt compiled
204          optMethodVector.addElement(method);
205          optOptionsVector.addElement(opts);
206        }
207      }
208    
209      // process the command line option
210      static OptOptions options = new OptOptions();
211    
212      private static void processOptionString(String[] args) {
213        for (int i = 0, n = args.length; i < n; i++) {
214          try {
215            String arg = args[i];
216            if (arg.startsWith("-oc:") && options.processAsOption("-X:irc:", arg.substring(4))) {
217              // handled in processAsOption
218            } else if (arg.equals("-useBootOptions")) {
219              OptimizingCompiler.setBootOptions(options);
220            } else if (arg.equals("-longcommandline")) {
221              // the -longcommandline option reads options from a file.
222              // use for cases when the command line is too long for AIX
223              i++;
224              BufferedReader in = new BufferedReader(new FileReader(args[i]));
225              StringBuilder s = new StringBuilder("");
226              while (in.ready()) {
227                String line = in.readLine().trim();
228                if (!line.startsWith("#")) {
229                  s.append(line);
230                  s.append(" ");
231                }
232              }
233              in.close();
234              StringTokenizer t = new StringTokenizer(s.toString());
235              String[] av = new String[t.countTokens()];
236              for (int j = 0; j < av.length; j++) {
237                av[j] = t.nextToken();
238              }
239              processOptionString(av);
240            } else if (arg.equals("+baseline")) {
241              BASELINE = true;
242            } else if (arg.equals("-baseline")) {
243              BASELINE = false;
244            } else if (arg.equals("-load")) {
245              loadClass(args[++i]);
246            } else if (arg.equals("-class")) {
247              RVMClass klass = loadClass(args[++i]);
248              processClass(klass, options);
249              options = options.dup();
250            } else if (arg.equals("-method") || arg.equals("-methodOpt") || arg.equals("-methodBase")) {
251              // Default for this method is determined by BASELINE var
252              boolean isBaseline = BASELINE;
253              // Unless specified by these options
254              if (arg.equals("-methodOpt")) {
255                isBaseline = false;
256              }
257              if (arg.equals("-methodBase")) {
258                isBaseline = true;
259              }
260    
261              RVMClass klass = null;
262              try {
263                klass = loadClass(args[++i]);
264              } catch (Exception e) {
265                System.err.println("WARNING: Skipping method from " + args[i - 1]);
266              }
267              if (klass == null) continue;
268              String name = args[++i];
269              String desc = args[++i];
270              RVMMethod method = findDeclaredOrFirstMethod(klass, name, desc);
271              if (method == null || method.isAbstract() || method.isNative()) {
272                System.err.println("WARNING: Skipping method " + args[i - 2] + "." + name);
273              } else {
274                processMethod(method, options, isBaseline);
275              }
276              options = options.dup();
277            } else if (arg.equals("-performance")) {
278              perf = new Performance();
279            } else if (arg.equals("-disableClassLoading")) {
280              DISABLE_CLASS_LOADING = true;
281            } else if (arg.equals("-er")) {
282              EXECUTE_WITH_REFLECTION = true;
283              RVMClass klass = loadClass(args[++i]);
284              String name = args[++i];
285              String desc = args[++i];
286              NormalMethod method = (NormalMethod) findDeclaredOrFirstMethod(klass, name, desc);
287              CompiledMethod cm = null;
288              if (BASELINE) {
289                cm = BaselineCompiler.compile(method);
290              } else {
291                CompilationPlan cp =
292                    new CompilationPlan(method, OptimizationPlanner.createOptimizationPlan(options), null, options);
293                try {
294                  cm = OptimizingCompiler.compile(cp);
295                } catch (Throwable e) {
296                  System.err.println("SKIPPING method:" + method + "Due to exception: " + e);
297                }
298              }
299              if (cm != null) {
300                method.replaceCompiledMethod(cm);
301                if (PRINT_CODE_ADDRESS)
302                  VM.sysWriteln("Method: " + method + " compiled code: ", Magic.objectAsAddress(cm.getEntryCodeArray()));
303              }
304              TypeReference[] argDesc = method.getDescriptor().parseForParameterTypes(klass.getClassLoader());
305              Object[] reflectMethodArgs = new Object[argDesc.length];
306              i = parseMethodArgs(argDesc, args, i, reflectMethodArgs);
307              java.lang.reflect.Method reflectoid = java.lang.reflect.JikesRVMSupport.createMethod(method);
308              reflectoidVector.addElement(reflectoid);
309              reflectMethodVector.addElement(method);
310              reflectMethodArgsVector.addElement(reflectMethodArgs);
311              options = options.dup();
312            } else if (arg.equals("-main")) {
313              EXECUTE_MAIN = true;
314              i++;
315              mainClass = loadClass(args[i]);
316              i++;
317              mainArgs = new String[args.length - i];
318              for (int j = 0, z = mainArgs.length; j < z; j++) {
319                mainArgs[j] = args[i+j];
320              }
321              break;
322            } else {
323              System.err.println("Unrecognized argument: " + arg + " - ignored");
324            }
325          } catch (ArrayIndexOutOfBoundsException e) {
326            System.err.println("Uncaught ArrayIndexOutOfBoundsException, possibly" +
327                               " not enough command-line arguments - aborting");
328            printFormatString();
329            e.printStackTrace(System.err);
330            break;
331          } catch (Exception e) {
332            System.err.println(e);
333            e.printStackTrace(System.err);
334            break;
335          }
336        }
337      }
338    
339      private static void compileMethodsInVector() {
340        // Compile all baseline methods first
341        int size = baselineMethodVector.size();
342        VM.sysWrite("Compiling " + size + " methods baseline\n");
343        // Compile all methods in baseline vector
344        for (int i = 0; i < size; i++) {
345          NormalMethod method = (NormalMethod) baselineMethodVector.elementAt(i);
346          CompiledMethod cm = null;
347          cm = BaselineCompiler.compile(method);
348          method.replaceCompiledMethod(cm);
349          if (PRINT_CODE_ADDRESS)
350            VM.sysWriteln("Method: " + method + " compiled code: ", Magic.objectAsAddress(cm.getEntryCodeArray()));
351        }
352    
353        // Now compile all methods in opt vector
354        size = optMethodVector.size();
355        VM.sysWrite("Compiling " + size + " methods opt\n");
356        for (int i = 0; i < size; i++) {
357          NormalMethod method = (NormalMethod) optMethodVector.elementAt(i);
358          OptOptions opts = optOptionsVector.elementAt(i);
359          try {
360            CompiledMethod cm = null;
361            CompilationPlan cp =
362                new CompilationPlan(method, OptimizationPlanner.createOptimizationPlan(opts), null, opts);
363            cm = OptimizingCompiler.compile(cp);
364            method.replaceCompiledMethod(cm);
365            if (PRINT_CODE_ADDRESS)
366              VM.sysWriteln("Method: " + method + " compiled code: ", Magic.objectAsAddress(cm.getEntryCodeArray()));
367          } catch (OptimizingCompilerException e) {
368            if (e.isFatal && VM.ErrorsFatal) {
369              e.printStackTrace();
370              VM.sysFail("Internal vm error: " + e.toString());
371            } else {
372              System.err.println("SKIPPING opt-compilation of " + method + ":\n  " + e.getMessage());
373              if (opts.PRINT_METHOD) {
374                e.printStackTrace();
375              }
376            }
377          }
378        }
379      }
380    
381      private static void executeCommand() throws InvocationTargetException, IllegalAccessException {
382        compileMethodsInVector();
383    
384        if (EXECUTE_WITH_REFLECTION) {
385    
386          if (DISABLE_CLASS_LOADING) {
387            RVMClass.classLoadingDisabled = true;
388          }
389    
390          int size = reflectoidVector.size();
391          for (int i = 0; i < size; i++) {
392            reflectoid = reflectoidVector.elementAt(i);
393            reflectMethodArgs = reflectMethodArgsVector.elementAt(i);
394            RVMMethod method = reflectMethodVector.elementAt(i);
395            VM.sysWrite("**** START OF EXECUTION of " + method + " ****.\n");
396            Object result = null;
397            if (perf != null) perf.reset();
398            result = reflectoid.invoke(null, reflectMethodArgs);
399            if (perf != null) perf.stop();
400            VM.sysWrite("**** END OF EXECUTION of " + method + " ****.\n");
401            VM.sysWrite("**** RESULT: " + result + "\n");
402          }
403          EXECUTE_WITH_REFLECTION = false;
404        }
405    
406        if (EXECUTE_MAIN) {
407          RVMMethod mainMethod = mainClass.findMainMethod();
408          if (mainMethod == null) {
409            // no such method
410            System.err.println(mainClass + " doesn't have a \"public static void main(String[])\" method to execute\n");
411            return;
412          }
413          VM.sysWrite("**** START OF EXECUTION of " + mainMethod + " ****.\n");
414          Reflection.invoke(mainMethod, null, null, new Object[]{mainArgs}, true);
415          VM.sysWrite("**** END OF EXECUTION of " + mainMethod + " ****.\n");
416        }
417      }
418    
419      public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
420        cl = RVMClassLoader.getApplicationClassLoader();
421        optMethodVector = new Vector<RVMMethod>(50);
422        optOptionsVector = new Vector<OptOptions>(50);
423        baselineMethodVector = new Vector<RVMMethod>(50);
424        reflectoidVector = new Vector<Method>(10);
425        reflectMethodVector = new Vector<RVMMethod>(10);
426        reflectMethodArgsVector = new Vector<Object[]>(10);
427        if (!OptimizingCompiler.isInitialized()) {
428          OptimizingCompiler.init(options);
429        }
430        processOptionString(args);
431        if (perf != null) {
432          Callbacks.addExitMonitor(perf);
433        }
434        executeCommand();
435        if (perf != null) {
436          perf.show();
437        }
438      }
439    
440      private static class Performance implements Callbacks.ExitMonitor {
441        private long start = 0;
442        private long end = 0;
443    
444        void reset() { start = Time.nanoTime(); }
445    
446        void stop() { if (end == 0) end = Time.nanoTime(); }
447    
448        void show() {
449          stop();  // In case we got here due to a System.exit
450          System.out.println("");
451          System.out.println("Performance of executed method");
452          System.out.println("------------------------------");
453          System.out.print("Elapsed wallclock time: ");
454          System.out.print(Time.nanosToMillis(end - start));
455          System.out.println(" msec");
456        }
457    
458        public void notifyExit(int discard) { show(); }
459      }
460    }