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