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 }