10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package jdk.tools.jaotc; 25 26 import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC; 27 import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode; 28 import static org.graalvm.compiler.hotspot.meta.HotSpotAOTProfilingPlugin.Options.TieredAOT; 29 30 import java.io.BufferedReader; 31 import java.io.File; 32 import java.io.FileNotFoundException; 33 import java.io.FileReader; 34 import java.io.FileWriter; 35 import java.io.IOException; 36 import java.io.InputStream; 37 import java.io.InputStreamReader; 38 import java.io.PrintWriter; 39 import java.lang.management.ManagementFactory; 40 import java.lang.management.MemoryUsage; 41 import java.nio.file.Path; 42 import java.nio.file.Paths; 43 import java.text.MessageFormat; 44 import java.util.ArrayList; 45 import java.util.Date; 46 import java.util.HashSet; 47 import java.util.LinkedList; 48 import java.util.List; 49 import java.util.ListIterator; 50 import java.util.Set; 51 import java.util.stream.Stream; 52 53 import jdk.tools.jaotc.binformat.BinaryContainer; 54 import jdk.tools.jaotc.binformat.ByteContainer; 55 import jdk.tools.jaotc.collect.*; 56 import jdk.tools.jaotc.collect.classname.ClassNameSourceProvider; 57 import jdk.tools.jaotc.collect.directory.DirectorySourceProvider; 58 import jdk.tools.jaotc.collect.jar.JarSourceProvider; 59 import jdk.tools.jaotc.collect.module.ModuleSourceProvider; 60 import jdk.tools.jaotc.utils.Timer; 61 62 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; 63 import org.graalvm.compiler.api.runtime.GraalJVMCICompiler; 64 import org.graalvm.compiler.debug.DebugContext; 65 import org.graalvm.compiler.debug.DebugContext.Activation; 66 import org.graalvm.compiler.hotspot.CompilerConfigurationFactory; 67 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; 68 import org.graalvm.compiler.hotspot.HotSpotGraalCompilerFactory; 69 import org.graalvm.compiler.hotspot.HotSpotGraalOptionValues; 70 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider; 71 import org.graalvm.compiler.hotspot.HotSpotHostBackend; 72 import org.graalvm.compiler.java.GraphBuilderPhase; 73 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; 74 import org.graalvm.compiler.options.OptionValues; 75 import org.graalvm.compiler.phases.BasePhase; 76 import org.graalvm.compiler.phases.PhaseSuite; 77 import org.graalvm.compiler.phases.tiers.HighTierContext; 78 import org.graalvm.compiler.printer.GraalDebugHandlersFactory; 79 import org.graalvm.compiler.runtime.RuntimeProvider; 80 81 import jdk.vm.ci.meta.MetaAccessProvider; 82 import jdk.vm.ci.meta.ResolvedJavaMethod; 83 import jdk.vm.ci.meta.ResolvedJavaType; 84 import jdk.vm.ci.runtime.JVMCI; 85 86 public class Main implements LogPrinter { 87 static class BadArgs extends Exception { 88 private static final long serialVersionUID = 1L; 89 final String key; 90 final Object[] args; 91 boolean showUsage; 92 93 BadArgs(String key, Object... args) { 94 super(MessageFormat.format(key, args)); 95 this.key = key; 96 this.args = args; 97 } 98 99 BadArgs showUsage(boolean b) { 100 showUsage = b; 101 return this; 102 } 103 } 104 105 abstract static class Option { 106 final String help; 107 final boolean hasArg; 108 final String[] aliases; 109 110 Option(String help, boolean hasArg, String... aliases) { 111 this.help = help; 112 this.hasArg = hasArg; 113 this.aliases = aliases; 114 } 115 116 boolean isHidden() { 117 return false; 118 } 119 120 boolean matches(String opt) { 121 for (String a : aliases) { 122 if (a.equals(opt)) { 123 return true; 124 } else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) { 125 return true; 126 } 127 } 128 return false; 129 } 130 131 boolean ignoreRest() { 132 return false; 133 } 134 135 abstract void process(Main task, String opt, String arg) throws BadArgs; 136 } 137 138 static Option[] recognizedOptions = {new Option(" --output <file> Output file name", true, "--output") { 139 @Override 140 void process(Main task, String opt, String arg) { 141 String name = arg; 142 task.options.outputName = name; 143 } 144 }, new Option(" --class-name <class names> List of classes to compile", true, "--class-name", "--classname") { 145 @Override 146 void process(Main task, String opt, String arg) { 147 task.options.files.addAll(ClassSearch.makeList(ClassNameSourceProvider.TYPE, arg)); 148 } 149 }, new Option(" --jar <jarfiles> List of jar files to compile", true, "--jar") { 150 @Override 151 void process(Main task, String opt, String arg) { 152 task.options.files.addAll(ClassSearch.makeList(JarSourceProvider.TYPE, arg)); 153 } 154 }, new Option(" --module <modules> List of modules to compile", true, "--module") { 155 @Override 156 void process(Main task, String opt, String arg) { 157 task.options.files.addAll(ClassSearch.makeList(ModuleSourceProvider.TYPE, arg)); 158 } 159 }, new Option(" --directory <dirs> List of directories where to search for files to compile", true, "--directory") { 160 @Override 161 void process(Main task, String opt, String arg) { 162 task.options.files.addAll(ClassSearch.makeList(DirectorySourceProvider.TYPE, arg)); 163 } 164 }, new Option(" --search-path <dirs> List of directories where to search for specified files", true, "--search-path") { 165 @Override 166 void process(Main task, String opt, String arg) { 167 String[] elements = arg.split(":"); 168 task.options.searchPath.add(elements); 169 } 170 }, new Option(" --compile-commands <file> Name of file with compile commands", true, "--compile-commands") { 171 @Override 172 void process(Main task, String opt, String arg) { 173 task.options.methodList = arg; 174 } 175 }, new Option(" --compile-for-tiered Generate profiling code for tiered compilation", false, "--compile-for-tiered") { 176 @Override 177 void process(Main task, String opt, String arg) { 178 task.options.tiered = true; 179 } 180 }, new Option(" --compile-with-assertions Compile with java assertions", false, "--compile-with-assertions") { 181 @Override 182 void process(Main task, String opt, String arg) { 183 task.options.compileWithAssertions = true; 184 } 185 }, new Option(" --compile-threads <number> Number of compilation threads to be used", true, "--compile-threads", "--threads") { 186 @Override 187 void process(Main task, String opt, String arg) { 188 int threads = Integer.parseInt(arg); 189 final int available = Runtime.getRuntime().availableProcessors(); 190 if (threads <= 0) { 191 task.warning("invalid number of threads specified: {0}, using: {1}", threads, available); 192 threads = available; 193 } 194 if (threads > available) { 195 task.warning("too many threads specified: {0}, limiting to: {1}", threads, available); 196 } 197 task.options.threads = Integer.min(threads, available); 198 } 199 }, new Option(" --ignore-errors Ignores all exceptions thrown during class loading", false, "--ignore-errors") { 200 @Override 201 void process(Main task, String opt, String arg) { 202 task.options.ignoreClassLoadingErrors = true; 203 } 204 }, new Option(" --exit-on-error Exit on compilation errors", false, "--exit-on-error") { 205 @Override 206 void process(Main task, String opt, String arg) { 207 task.options.exitOnError = true; 208 } 209 }, new Option(" --info Print information during compilation", false, "--info") { 210 @Override 211 void process(Main task, String opt, String arg) throws BadArgs { 212 task.options.info = true; 213 } 214 }, new Option(" --verbose Print verbose information", false, "--verbose") { 215 @Override 216 void process(Main task, String opt, String arg) throws BadArgs { 217 task.options.info = true; 218 task.options.verbose = true; 219 } 220 }, new Option(" --debug Print debug information", false, "--debug") { 221 @Override 222 void process(Main task, String opt, String arg) throws BadArgs { 223 task.options.info = true; 224 task.options.verbose = true; 225 task.options.debug = true; 226 } 227 }, new Option(" --help Print this usage message", false, "--help") { 228 @Override 229 void process(Main task, String opt, String arg) { 230 task.options.help = true; 231 } 232 }, new Option(" --version Version information", false, "--version") { 233 @Override 234 void process(Main task, String opt, String arg) { 235 task.options.version = true; 236 } 237 }, new Option(" --linker-path Full path to linker executable", true, "--linker-path") { 238 @Override 239 void process(Main task, String opt, String arg) { 240 task.options.linkerpath = arg; 241 } 242 }, new Option(" -J<flag> Pass <flag> directly to the runtime system", false, "-J") { 243 @Override 244 void process(Main task, String opt, String arg) { 245 } 246 }}; 247 248 public static class Options { 249 public List<SearchFor> files = new LinkedList<>(); 250 public String outputName = defaultOutputName(); 251 public String methodList; 252 public List<ClassSource> sources = new ArrayList<>(); 253 public String linkerpath = null; 254 public SearchPath searchPath = new SearchPath(); 255 256 /** 257 * We don't see scaling beyond 16 threads. 258 */ 259 private static final int COMPILER_THREADS = 16; 260 261 public int threads = Integer.min(COMPILER_THREADS, Runtime.getRuntime().availableProcessors()); 262 263 public boolean ignoreClassLoadingErrors; 264 public boolean exitOnError; 265 public boolean info; 266 public boolean verbose; 267 public boolean debug; 268 public boolean help; 269 public boolean version; 270 public boolean compileWithAssertions; 271 public boolean tiered; 272 273 private static String defaultOutputName() { 274 String osName = System.getProperty("os.name"); 275 String name = "unnamed."; 276 String ext; 277 278 switch (osName) { 279 case "Linux": 280 case "SunOS": 281 ext = "so"; 282 break; 283 case "Mac OS X": 284 ext = "dylib"; 285 break; 286 default: 287 if (osName.startsWith("Windows")) { 288 ext = "dll"; 289 } else { 290 ext = "so"; 291 } 292 } 293 294 return name + ext; 295 } 296 } 297 298 /* package */final Options options = new Options(); 299 300 /** 301 * Logfile. 302 */ 303 private static FileWriter logFile = null; 304 305 private static final int EXIT_OK = 0; // No errors. 306 private static final int EXIT_CMDERR = 2; // Bad command-line arguments and/or switches. 307 private static final int EXIT_ABNORMAL = 4; // Terminated abnormally. 308 309 private static final String PROGNAME = "jaotc"; 310 311 private static final String JVM_VERSION = System.getProperty("java.runtime.version"); 312 313 public static void main(String[] args) throws Exception { 314 Main t = new Main(); 315 final int exitCode = t.run(args); 316 System.exit(exitCode); 317 } 318 319 private int run(String[] args) { 320 if (log == null) { 321 log = new PrintWriter(System.out); 322 } 323 324 try { 325 handleOptions(args); 326 if (options.help) { 327 showHelp(); 328 return EXIT_OK; 329 } 330 if (options.version) { 331 showVersion(); 332 return EXIT_OK; 333 } 334 335 printlnInfo("Compiling " + options.outputName + "..."); 336 final long start = System.currentTimeMillis(); 337 if (!run()) { 338 return EXIT_ABNORMAL; 339 } 340 final long end = System.currentTimeMillis(); 341 printlnInfo("Total time: " + (end - start) + " ms"); 342 343 return EXIT_OK; 344 } catch (BadArgs e) { 345 reportError(e.key, e.args); 346 if (e.showUsage) { 347 showUsage(); 348 } 349 return EXIT_CMDERR; 350 } catch (Exception e) { 351 e.printStackTrace(); 352 return EXIT_ABNORMAL; 353 } finally { 354 log.flush(); 355 } 356 } 357 358 private static String humanReadableByteCount(long bytes) { 359 int unit = 1024; 360 361 if (bytes < unit) { 362 return bytes + " B"; 363 } 364 365 int exp = (int) (Math.log(bytes) / Math.log(unit)); 366 char pre = "KMGTPE".charAt(exp - 1); 367 return String.format("%.1f %cB", bytes / Math.pow(unit, exp), pre); 368 } 369 370 void printMemoryUsage() { 371 if (options.verbose) { 372 MemoryUsage memusage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage(); 373 float freeratio = 1f - (float) memusage.getUsed() / memusage.getCommitted(); 374 log.format(" [used: %-7s, comm: %-7s, freeRatio ~= %.1f%%]", 375 humanReadableByteCount(memusage.getUsed()), 376 humanReadableByteCount(memusage.getCommitted()), 377 freeratio * 100); 378 } 379 } 380 381 /** 382 * Visual Studio supported versions Search Order is: VS2013, VS2015, VS2012 383 */ 384 public enum VSVERSIONS { 385 VS2013("VS120COMNTOOLS", "C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\bin\\amd64\\link.exe"), 386 VS2015("VS140COMNTOOLS", "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\amd64\\link.exe"), 387 VS2012("VS110COMNTOOLS", "C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\VC\\bin\\amd64\\link.exe"); 388 389 private final String envvariable; 390 private final String wkp; 391 392 VSVERSIONS(String envvariable, String wellknownpath) { 393 this.envvariable = envvariable; 394 this.wkp = wellknownpath; 395 } 396 397 String EnvVariable() { 398 return envvariable; 399 } 400 401 String WellKnownPath() { 402 return wkp; 403 } 404 } 405 406 /** 407 * Search for Visual Studio link.exe Search Order is: VS2013, VS2015, VS2012 408 */ 409 private static String getWindowsLinkPath() { 410 String link = "\\VC\\bin\\amd64\\link.exe"; 411 412 /** 413 * First try searching the paths pointed to by the VS environment variables. 414 */ 415 for (VSVERSIONS vs : VSVERSIONS.values()) { 416 String vspath = System.getenv(vs.EnvVariable()); 417 if (vspath != null) { 418 File commonTools = new File(vspath); 419 File vsRoot = commonTools.getParentFile().getParentFile(); 420 File linkPath = new File(vsRoot, link); 421 if (linkPath.exists()) 422 return linkPath.getPath(); 423 } 424 } 425 426 /** 427 * If we didn't find via the VS environment variables, try the well known paths 428 */ 429 for (VSVERSIONS vs : VSVERSIONS.values()) { 430 String wkp = vs.WellKnownPath(); 431 if (new File(wkp).exists()) { 432 return wkp; 433 } 434 } 435 436 return null; 437 } 438 439 @SuppressWarnings("try") 440 private boolean run() throws Exception { 441 openLog(); 442 443 try { 444 CompilationSpec compilationRestrictions = collectSpecifiedMethods(); 445 446 Set<Class<?>> classesToCompile = new HashSet<>(); 447 448 try (Timer t = new Timer(this, "")) { 449 FileSupport fileSupport = new FileSupport(); 450 ClassSearch lookup = new ClassSearch(); 451 lookup.addProvider(new ModuleSourceProvider()); 452 lookup.addProvider(new ClassNameSourceProvider(fileSupport)); 453 lookup.addProvider(new JarSourceProvider()); 454 lookup.addProvider(new DirectorySourceProvider(fileSupport)); 455 456 List<LoadedClass> found = null; 457 try { 458 found = lookup.search(options.files, options.searchPath); 459 } catch (InternalError e) { 460 reportError(e); 461 return false; 462 } 463 464 for (LoadedClass loadedClass : found) { 465 classesToCompile.add(loadedClass.getLoadedClass()); 466 } 467 468 printInfo(classesToCompile.size() + " classes found"); 469 } 470 471 OptionValues graalOptions = HotSpotGraalOptionValues.HOTSPOT_OPTIONS; 472 // Setting -Dgraal.TieredAOT overrides --compile-for-tiered 473 if (!TieredAOT.hasBeenSet(graalOptions)) { 474 graalOptions = new OptionValues(graalOptions, TieredAOT, options.tiered); 475 } 476 graalOptions = new OptionValues(graalOptions, GeneratePIC, true, ImmutableCode, true); 477 GraalJVMCICompiler graalCompiler = HotSpotGraalCompilerFactory.createCompiler(JVMCI.getRuntime(), graalOptions, CompilerConfigurationFactory.selectFactory(null, graalOptions)); 478 HotSpotGraalRuntimeProvider runtime = (HotSpotGraalRuntimeProvider) graalCompiler.getGraalRuntime(); 479 HotSpotHostBackend backend = (HotSpotHostBackend) runtime.getCapability(RuntimeProvider.class).getHostBackend(); 480 MetaAccessProvider metaAccess = backend.getProviders().getMetaAccess(); 481 GraalFilters filters = new GraalFilters(metaAccess); 482 483 List<AOTCompiledClass> classes; 484 485 try (Timer t = new Timer(this, "")) { 486 classes = collectMethodsToCompile(classesToCompile, compilationRestrictions, filters, metaAccess); 487 } 488 489 // Free memory! 490 try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) { 491 printMemoryUsage(); 492 compilationRestrictions = null; 493 classesToCompile = null; 494 System.gc(); 495 } 496 497 AOTBackend aotBackend = new AOTBackend(this, graalOptions, backend, filters); 498 SnippetReflectionProvider snippetReflection = aotBackend.getProviders().getSnippetReflection(); 499 AOTCompiler compiler = new AOTCompiler(this, graalOptions, aotBackend, options.threads); 500 classes = compiler.compileClasses(classes); 501 502 GraalHotSpotVMConfig graalHotSpotVMConfig = runtime.getVMConfig(); 503 PhaseSuite<HighTierContext> graphBuilderSuite = aotBackend.getGraphBuilderSuite(); 504 ListIterator<BasePhase<? super HighTierContext>> iterator = graphBuilderSuite.findPhase(GraphBuilderPhase.class); 505 GraphBuilderConfiguration graphBuilderConfig = ((GraphBuilderPhase) iterator.previous()).getGraphBuilderConfig(); 506 507 // Free memory! 508 try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) { 509 printMemoryUsage(); 510 aotBackend = null; 511 compiler = null; 512 System.gc(); 513 } 514 515 BinaryContainer binaryContainer = new BinaryContainer(graalOptions, graalHotSpotVMConfig, graphBuilderConfig, JVM_VERSION); 516 DataBuilder dataBuilder = new DataBuilder(this, backend, classes, binaryContainer); 517 518 try (DebugContext debug = DebugContext.create(graalOptions, new GraalDebugHandlersFactory(snippetReflection)); Activation a = debug.activate()) { 519 dataBuilder.prepareData(debug); 520 } 521 522 // Print information about section sizes 523 printContainerInfo(binaryContainer.getHeaderContainer().getContainer()); 524 printContainerInfo(binaryContainer.getConfigContainer()); 525 printContainerInfo(binaryContainer.getKlassesOffsetsContainer()); 526 printContainerInfo(binaryContainer.getMethodsOffsetsContainer()); 527 printContainerInfo(binaryContainer.getKlassesDependenciesContainer()); 528 printContainerInfo(binaryContainer.getStubsOffsetsContainer()); 529 printContainerInfo(binaryContainer.getMethodMetadataContainer()); 530 printContainerInfo(binaryContainer.getCodeContainer()); 531 printContainerInfo(binaryContainer.getCodeSegmentsContainer()); 532 printContainerInfo(binaryContainer.getConstantDataContainer()); 533 printContainerInfo(binaryContainer.getMetaspaceGotContainer()); 534 printContainerInfo(binaryContainer.getMetadataGotContainer()); 535 printContainerInfo(binaryContainer.getMethodStateContainer()); 536 printContainerInfo(binaryContainer.getOopGotContainer()); 537 printContainerInfo(binaryContainer.getMetaspaceNamesContainer()); 538 539 // Free memory! 540 try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) { 541 printMemoryUsage(); 542 backend = null; 543 for (AOTCompiledClass aotCompClass : classes) { 544 aotCompClass.clear(); 545 } 546 classes.clear(); 547 classes = null; 548 dataBuilder = null; 549 binaryContainer.freeMemory(); 550 System.gc(); 551 } 552 553 String name = options.outputName; 554 String objectFileName = name; 555 556 String libraryFileName = name; 557 558 String linkerCmd; 559 String linkerPath; 560 String osName = System.getProperty("os.name"); 561 562 switch (osName) { 563 case "Linux": 564 if (name.endsWith(".so")) { 565 objectFileName = name.substring(0, name.length() - ".so".length()); 566 } 567 linkerPath = (options.linkerpath != null) ? options.linkerpath : "ld"; 568 linkerCmd = linkerPath + " -shared -z noexecstack -o " + libraryFileName + " " + objectFileName; 569 break; 570 case "SunOS": 571 if (name.endsWith(".so")) { 572 objectFileName = name.substring(0, name.length() - ".so".length()); 573 } 574 objectFileName = objectFileName + ".o"; 575 linkerPath = (options.linkerpath != null) ? options.linkerpath : "ld"; 576 linkerCmd = linkerPath + " -shared -o " + libraryFileName + " " + objectFileName; 577 break; 578 case "Mac OS X": 579 if (name.endsWith(".dylib")) { 580 objectFileName = name.substring(0, name.length() - ".dylib".length()); 581 } 582 objectFileName = objectFileName + ".o"; 583 linkerPath = (options.linkerpath != null) ? options.linkerpath : "ld"; 584 linkerCmd = linkerPath + " -dylib -o " + libraryFileName + " " + objectFileName; 585 break; 586 default: 587 if (osName.startsWith("Windows")) { 588 if (name.endsWith(".dll")) { 589 objectFileName = name.substring(0, name.length() - ".dll".length()); 590 } 591 objectFileName = objectFileName + ".obj"; 592 linkerPath = (options.linkerpath != null) ? options.linkerpath : getWindowsLinkPath(); 593 if (linkerPath == null) { 594 throw new InternalError("Can't locate Microsoft Visual Studio amd64 link.exe"); 595 } 596 linkerCmd = linkerPath + " /DLL /OPT:NOREF /NOLOGO /NOENTRY" + " /OUT:" + libraryFileName + " " + objectFileName; 597 break; 598 } else { 599 throw new InternalError("Unsupported platform: " + osName); 600 } 601 } 602 603 try (Timer t = new Timer(this, "Creating binary: " + objectFileName)) { 604 binaryContainer.createBinary(objectFileName, JVM_VERSION); 605 } 606 607 // Free memory! 608 try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) { 609 printMemoryUsage(); 610 binaryContainer = null; 611 System.gc(); 612 } 613 614 try (Timer t = new Timer(this, "Creating shared library: " + libraryFileName)) { 615 Process p = Runtime.getRuntime().exec(linkerCmd); 616 final int exitCode = p.waitFor(); 617 if (exitCode != 0) { 618 InputStream stderr = p.getErrorStream(); 619 BufferedReader br = new BufferedReader(new InputStreamReader(stderr)); 620 Stream<String> lines = br.lines(); 621 StringBuilder sb = new StringBuilder(); 622 lines.iterator().forEachRemaining(e -> sb.append(e)); 623 throw new InternalError(sb.toString()); 624 } 625 File objFile = new File(objectFileName); 626 if (objFile.exists()) { 627 if (!objFile.delete()) { 628 throw new InternalError("Failed to delete " + objectFileName + " file"); 629 } 630 } 631 // Make non-executable for all. 632 File libFile = new File(libraryFileName); 633 if (libFile.exists() && !osName.startsWith("Windows")) { 634 if (!libFile.setExecutable(false, false)) { 635 throw new InternalError("Failed to change attribute for " + libraryFileName + " file"); 636 } 637 } 638 } 639 640 printVerbose("Final memory "); 641 printMemoryUsage(); 642 printlnVerbose(""); 643 644 } finally { 645 closeLog(); 646 } 647 return true; 648 } 649 650 private void addMethods(AOTCompiledClass aotClass, ResolvedJavaMethod[] methods, CompilationSpec compilationRestrictions, GraalFilters filters) { 651 for (ResolvedJavaMethod m : methods) { 652 addMethod(aotClass, m, compilationRestrictions, filters); 653 } 654 } 655 656 private void addMethod(AOTCompiledClass aotClass, ResolvedJavaMethod method, CompilationSpec compilationRestrictions, GraalFilters filters) { 657 // Don't compile native or abstract methods. 658 if (!method.hasBytecodes()) { 659 return; 660 } 661 if (!compilationRestrictions.shouldCompileMethod(method)) { 662 return; 663 } 664 if (!filters.shouldCompileMethod(method)) { 665 return; 666 } 667 668 aotClass.addMethod(method); 669 printlnVerbose(" added " + method.getName() + method.getSignature().toMethodDescriptor()); 670 } 671 672 private void printContainerInfo(ByteContainer container) { 673 printlnVerbose(container.getContainerName() + ": " + container.getByteStreamSize() + " bytes"); 674 } 675 676 PrintWriter log; 677 678 private void handleOptions(String[] args) throws BadArgs { 679 if (args.length == 0) { 680 options.help = true; 681 return; 682 } 683 684 // Make checkstyle happy. 685 int i = 0; 686 for (; i < args.length; i++) { 687 String arg = args[i]; 688 689 if (arg.charAt(0) == '-') { 690 Option option = getOption(arg); 691 String param = null; 692 693 if (option.hasArg) { 694 if (arg.startsWith("--") && arg.indexOf('=') > 0) { 695 param = arg.substring(arg.indexOf('=') + 1, arg.length()); 696 } else if (i + 1 < args.length) { 697 param = args[++i]; 698 } 699 700 if (param == null || param.isEmpty() || param.charAt(0) == '-') { 701 throw new BadArgs("missing argument for option: {0}", arg).showUsage(true); 702 } 703 } 704 705 option.process(this, arg, param); 706 707 if (option.ignoreRest()) { 708 break; 709 } 710 } else { 711 options.files.add(new SearchFor(arg)); 712 } 713 } 714 } 715 716 private static Option getOption(String name) throws BadArgs { 717 for (Option o : recognizedOptions) { 718 if (o.matches(name)) { 719 return o; 720 } 721 } 722 throw new BadArgs("unknown option: {0}", name).showUsage(true); 723 } 724 725 public void printInfo(String message) { 726 if (options.info) { 727 log.print(message); 728 log.flush(); 729 } 730 } 731 732 public void printlnInfo(String message) { 733 if (options.info) { 734 log.println(message); 735 log.flush(); 736 } 737 } 738 739 public void printVerbose(String message) { 740 if (options.verbose) { 741 log.print(message); 742 log.flush(); 743 } 744 } 745 746 public void printlnVerbose(String message) { 747 if (options.verbose) { 748 log.println(message); 749 log.flush(); 750 } 751 } 752 753 public void printDebug(String message) { 754 if (options.debug) { 755 log.print(message); 756 log.flush(); 757 } 758 } 759 760 public void printlnDebug(String message) { 761 if (options.debug) { 762 log.println(message); 763 log.flush(); 764 } 765 } 766 767 public void printError(String message) { 768 log.println("Error: " + message); 769 log.flush(); 770 } 771 772 private void reportError(Throwable e) { 773 log.println("Error: " + e.getMessage()); 774 if (options.info) { 775 e.printStackTrace(log); 776 } 777 log.flush(); 778 } 779 780 private void reportError(String key, Object... args) { 781 printError(MessageFormat.format(key, args)); 782 } 783 784 private void warning(String key, Object... args) { 785 log.println("Warning: " + MessageFormat.format(key, args)); 786 log.flush(); 787 } 788 789 private void showUsage() { 790 log.println("Usage: " + PROGNAME + " <options> list"); 791 log.println("use --help for a list of possible options"); 792 } 793 794 private void showHelp() { 795 log.println("Usage: " + PROGNAME + " <options> list"); 796 log.println(); 797 log.println(" list A : separated list of class names, modules, jar files"); 798 log.println(" or directories which contain class files."); 799 log.println(); 800 log.println("where options include:"); 801 for (Option o : recognizedOptions) { 802 String name = o.aliases[0].substring(1); // there must always be at least one name 803 name = name.charAt(0) == '-' ? name.substring(1) : name; 804 if (o.isHidden() || name.equals("h")) { 805 continue; 806 } 807 log.println(o.help); 808 } 809 } 810 811 private void showVersion() { 812 log.println(PROGNAME + " " + JVM_VERSION); 813 } 814 815 /** 816 * Collect all method we should compile. 817 * 818 * @return array list of AOT classes which have compiled methods. 819 */ 820 private List<AOTCompiledClass> collectMethodsToCompile(Set<Class<?>> classesToCompile, CompilationSpec compilationRestrictions, GraalFilters filters, MetaAccessProvider metaAccess) { 821 int total = 0; 822 int count = 0; 823 List<AOTCompiledClass> classes = new ArrayList<>(); 824 825 for (Class<?> c : classesToCompile) { 826 ResolvedJavaType resolvedJavaType = metaAccess.lookupJavaType(c); 827 if (filters.shouldCompileAnyMethodInClass(resolvedJavaType)) { 828 AOTCompiledClass aotClass = new AOTCompiledClass(resolvedJavaType); 829 printlnVerbose(" Scanning " + c.getName()); 830 831 // Constructors 832 try { 833 ResolvedJavaMethod[] ctors = resolvedJavaType.getDeclaredConstructors(); 834 addMethods(aotClass, ctors, compilationRestrictions, filters); 835 total += ctors.length; 836 } catch (Throwable e) { 837 // If we are running in JCK mode we ignore all exceptions. 838 if (options.ignoreClassLoadingErrors) { 839 printError(c.getName() + ": " + e); 840 } else { 841 throw new InternalError(e); 842 } 843 } 844 845 // Methods 846 try { 847 ResolvedJavaMethod[] methods = resolvedJavaType.getDeclaredMethods(); 848 addMethods(aotClass, methods, compilationRestrictions, filters); 849 total += methods.length; 850 } catch (Throwable e) { 851 // If we are running in JCK mode we ignore all exceptions. 852 if (options.ignoreClassLoadingErrors) { 853 printError(c.getName() + ": " + e); 854 } else { 855 throw new InternalError(e); 856 } 857 } 858 859 // Class initializer 860 try { 861 ResolvedJavaMethod clinit = resolvedJavaType.getClassInitializer(); 862 if (clinit != null) { 863 addMethod(aotClass, clinit, compilationRestrictions, filters); 864 total++; 865 } 866 } catch (Throwable e) { 867 // If we are running in JCK mode we ignore all exceptions. 868 if (options.ignoreClassLoadingErrors) { 869 printError(c.getName() + ": " + e); 870 } else { 871 throw new InternalError(e); 872 } 873 } 874 875 // Found any methods to compile? Add the class. 876 if (aotClass.hasMethods()) { 877 classes.add(aotClass); 878 count += aotClass.getMethodCount(); 879 } 880 } 881 } 882 printInfo(total + " methods total, " + count + " methods to compile"); 883 return classes; 884 } 885 886 /** 887 * If a file with compilation limitations is specified using the java property 888 * jdk.tools.jaotc.compile.method.list, read the file's contents and collect the restrictions. 889 */ 890 private CompilationSpec collectSpecifiedMethods() { 891 CompilationSpec compilationRestrictions = new CompilationSpec(); 892 String methodListFileName = options.methodList; 893 894 if (methodListFileName != null && !methodListFileName.equals("")) { 895 try { 896 FileReader methListFile = new FileReader(methodListFileName); 897 BufferedReader readBuf = new BufferedReader(methListFile); 898 String line = null; 899 while ((line = readBuf.readLine()) != null) { 900 String trimmedLine = line.trim(); 901 if (!trimmedLine.startsWith("#")) { 902 String[] components = trimmedLine.split(" "); 903 if (components.length == 2) { 904 String directive = components[0]; 905 String pattern = components[1]; 906 switch (directive) { 907 case "compileOnly": 908 compilationRestrictions.addCompileOnlyPattern(pattern); 909 break; 910 case "exclude": 911 compilationRestrictions.addExcludePattern(pattern); 912 break; 913 default: 914 System.out.println("Unrecognized command " + directive + ". Ignoring\n\t" + line + "\n encountered in " + methodListFileName); 915 } 916 } else { 917 if (!trimmedLine.equals("")) { 918 System.out.println("Ignoring malformed line:\n\t " + line + "\n"); 919 } 920 } 921 } 922 } 923 readBuf.close(); 924 } catch (FileNotFoundException e) { 925 throw new InternalError("Unable to open method list file: " + methodListFileName, e); 926 } catch (IOException e) { 927 throw new InternalError("Unable to read method list file: " + methodListFileName, e); 928 } 929 } 930 931 return compilationRestrictions; 932 } 933 934 private static void openLog() { 935 int v = Integer.getInteger("jdk.tools.jaotc.logCompilation", 0); 936 if (v == 0) { 937 logFile = null; 938 return; 939 } 940 // Create log file in current directory 941 String fileName = "aot_compilation" + new Date().getTime() + ".log"; 942 Path logFilePath = Paths.get("./", fileName); 943 String logFileName = logFilePath.toString(); 944 try { 945 // Create file to which we do not append 946 logFile = new FileWriter(logFileName, false); 947 } catch (IOException e) { 948 System.out.println("Unable to open logfile :" + logFileName + "\nNo logs will be created"); 949 logFile = null; 950 } 951 } 952 953 public static void writeLog(String str) { 954 if (logFile != null) { 955 try { 956 logFile.write(str + "\n"); 957 logFile.flush(); 958 } catch (IOException e) { 959 // Print to console 960 System.out.println(str + "\n"); 961 } 962 } 963 } 964 965 public static void closeLog() { 966 if (logFile != null) { 967 try { 968 logFile.close(); 969 } catch (IOException e) { 970 // Do nothing 971 } 972 } 973 } 974 } | 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package jdk.tools.jaotc; 25 26 import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC; 27 import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode; 28 import static org.graalvm.compiler.hotspot.meta.HotSpotAOTProfilingPlugin.Options.TieredAOT; 29 30 import java.io.PrintWriter; 31 import java.text.MessageFormat; 32 import java.util.List; 33 import java.util.ListIterator; 34 import java.util.Set; 35 36 import jdk.tools.jaotc.binformat.BinaryContainer; 37 import jdk.tools.jaotc.Options.Option; 38 39 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; 40 import org.graalvm.compiler.api.runtime.GraalJVMCICompiler; 41 import org.graalvm.compiler.debug.DebugContext; 42 import org.graalvm.compiler.debug.DebugContext.Activation; 43 import org.graalvm.compiler.hotspot.CompilerConfigurationFactory; 44 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; 45 import org.graalvm.compiler.hotspot.HotSpotGraalCompilerFactory; 46 import org.graalvm.compiler.hotspot.HotSpotGraalOptionValues; 47 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider; 48 import org.graalvm.compiler.hotspot.HotSpotHostBackend; 49 import org.graalvm.compiler.java.GraphBuilderPhase; 50 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; 51 import org.graalvm.compiler.options.OptionValues; 52 import org.graalvm.compiler.phases.BasePhase; 53 import org.graalvm.compiler.phases.PhaseSuite; 54 import org.graalvm.compiler.phases.tiers.HighTierContext; 55 import org.graalvm.compiler.printer.GraalDebugHandlersFactory; 56 import org.graalvm.compiler.runtime.RuntimeProvider; 57 58 import jdk.vm.ci.meta.MetaAccessProvider; 59 import jdk.vm.ci.meta.ResolvedJavaMethod; 60 import jdk.vm.ci.runtime.JVMCI; 61 62 public final class Main { 63 64 final Options options = new Options(); 65 private PrintWriter log; 66 LogPrinter printer; 67 GraalFilters filters; 68 69 private static final int EXIT_OK = 0; // No errors. 70 private static final int EXIT_CMDERR = 2; // Bad command-line arguments and/or switches. 71 private static final int EXIT_ABNORMAL = 4; // Terminated abnormally. 72 73 private static final String PROGNAME = "jaotc"; 74 75 private static final String JVM_VERSION = System.getProperty("java.runtime.version"); 76 77 public static void main(String[] args) throws Exception { 78 Main t = new Main(); 79 final int exitCode = t.run(args); 80 System.exit(exitCode); 81 } 82 83 private int run(String[] args) { 84 log = new PrintWriter(System.out); 85 printer = new LogPrinter(this, log); 86 87 try { 88 Options.handleOptions(this, args); 89 if (options.help) { 90 showHelp(); 91 return EXIT_OK; 92 } 93 if (options.version) { 94 showVersion(); 95 return EXIT_OK; 96 } 97 98 printer.printlnInfo("Compiling " + options.outputName + "..."); 99 final long start = System.currentTimeMillis(); 100 if (!run()) { 101 return EXIT_ABNORMAL; 102 } 103 final long end = System.currentTimeMillis(); 104 printer.printlnInfo("Total time: " + (end - start) + " ms"); 105 106 return EXIT_OK; 107 } catch (Options.BadArgs e) { 108 printer.reportError(e.key, e.args); 109 if (e.showUsage) { 110 showUsage(); 111 } 112 return EXIT_CMDERR; 113 } catch (Exception e) { 114 e.printStackTrace(); 115 return EXIT_ABNORMAL; 116 } finally { 117 log.flush(); 118 } 119 } 120 121 @SuppressWarnings("try") 122 private boolean run() throws Exception { 123 LogPrinter.openLog(); 124 125 try { 126 127 final Linker linker = new Linker(this); 128 final String objectFileName = linker.objFile(); 129 final Collector collector = new Collector(this); 130 Set<Class<?>> classesToCompile; 131 132 try (Timer t = new Timer(this, "")) { 133 classesToCompile = collector.collectClassesToCompile(); 134 printer.printInfo(classesToCompile.size() + " classes found"); 135 } 136 137 OptionValues graalOptions = HotSpotGraalOptionValues.HOTSPOT_OPTIONS; 138 // Setting -Dgraal.TieredAOT overrides --compile-for-tiered 139 if (!TieredAOT.hasBeenSet(graalOptions)) { 140 graalOptions = new OptionValues(graalOptions, TieredAOT, options.tiered); 141 } 142 graalOptions = new OptionValues(graalOptions, GeneratePIC, true, ImmutableCode, true); 143 GraalJVMCICompiler graalCompiler = HotSpotGraalCompilerFactory.createCompiler(JVMCI.getRuntime(), graalOptions, CompilerConfigurationFactory.selectFactory(null, graalOptions)); 144 HotSpotGraalRuntimeProvider runtime = (HotSpotGraalRuntimeProvider) graalCompiler.getGraalRuntime(); 145 HotSpotHostBackend backend = (HotSpotHostBackend) runtime.getCapability(RuntimeProvider.class).getHostBackend(); 146 MetaAccessProvider metaAccess = backend.getProviders().getMetaAccess(); 147 filters = new GraalFilters(metaAccess); 148 149 List<AOTCompiledClass> classes; 150 151 try (Timer t = new Timer(this, "")) { 152 classes = collector.collectMethodsToCompile(classesToCompile, metaAccess); 153 } 154 155 // Free memory! 156 try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) { 157 printer.printMemoryUsage(); 158 classesToCompile = null; 159 System.gc(); 160 } 161 162 AOTBackend aotBackend = new AOTBackend(this, graalOptions, backend); 163 SnippetReflectionProvider snippetReflection = aotBackend.getProviders().getSnippetReflection(); 164 AOTCompiler compiler = new AOTCompiler(this, graalOptions, aotBackend, options.threads); 165 classes = compiler.compileClasses(classes); 166 167 GraalHotSpotVMConfig graalHotSpotVMConfig = runtime.getVMConfig(); 168 PhaseSuite<HighTierContext> graphBuilderSuite = aotBackend.getGraphBuilderSuite(); 169 ListIterator<BasePhase<? super HighTierContext>> iterator = graphBuilderSuite.findPhase(GraphBuilderPhase.class); 170 GraphBuilderConfiguration graphBuilderConfig = ((GraphBuilderPhase) iterator.previous()).getGraphBuilderConfig(); 171 172 // Free memory! 173 try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) { 174 printer.printMemoryUsage(); 175 aotBackend = null; 176 compiler = null; 177 System.gc(); 178 } 179 180 BinaryContainer binaryContainer = new BinaryContainer(graalOptions, graalHotSpotVMConfig, graphBuilderConfig, JVM_VERSION); 181 DataBuilder dataBuilder = new DataBuilder(this, backend, classes, binaryContainer); 182 183 try (DebugContext debug = DebugContext.create(graalOptions, new GraalDebugHandlersFactory(snippetReflection)); Activation a = debug.activate()) { 184 dataBuilder.prepareData(debug); 185 } 186 187 // Print information about section sizes 188 printer.containersInfo(binaryContainer); 189 190 // Free memory! 191 try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) { 192 printer.printMemoryUsage(); 193 backend = null; 194 for (AOTCompiledClass aotCompClass : classes) { 195 aotCompClass.clear(); 196 } 197 classes.clear(); 198 classes = null; 199 dataBuilder = null; 200 binaryContainer.freeMemory(); 201 System.gc(); 202 } 203 204 try (Timer t = new Timer(this, "Creating binary: " + objectFileName)) { 205 binaryContainer.createBinary(objectFileName); 206 } 207 208 // Free memory! 209 try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) { 210 printer.printMemoryUsage(); 211 binaryContainer = null; 212 System.gc(); 213 } 214 215 try (Timer t = new Timer(this, "Creating shared library: " + linker.libFile())) { 216 linker.link(); 217 } 218 219 printer.printVerbose("Final memory "); 220 printer.printMemoryUsage(); 221 printer.printlnVerbose(""); 222 223 } finally { 224 LogPrinter.closeLog(); 225 } 226 return true; 227 } 228 229 void handleError(ResolvedJavaMethod resolvedMethod, Throwable e, String message) { 230 String methodName = JavaMethodInfo.uniqueMethodName(resolvedMethod); 231 232 if (options.debug) { 233 printer.printError("Failed compilation: " + methodName + ": " + e); 234 } 235 236 // Ignore some exceptions when meta-compiling Graal. 237 if (GraalFilters.shouldIgnoreException(e)) { 238 return; 239 } 240 241 LogPrinter.writeLog("Failed compilation of method " + methodName + message); 242 243 if (!options.debug) { 244 printer.printError("Failed compilation: " + methodName + ": " + e); 245 } 246 247 if (options.verbose) { 248 e.printStackTrace(log); 249 } 250 251 if (options.exitOnError) { 252 System.exit(1); 253 } 254 } 255 256 void warning(String key, Object... args) { 257 log.println("Warning: " + MessageFormat.format(key, args)); 258 log.flush(); 259 } 260 261 private void showUsage() { 262 log.println("Usage: " + PROGNAME + " <options> list"); 263 log.println("use --help for a list of possible options"); 264 log.flush(); 265 } 266 267 private void showHelp() { 268 log.println("Usage: " + PROGNAME + " <options> list"); 269 log.println(); 270 log.println(" list A : separated list of class names, modules, jar files"); 271 log.println(" or directories which contain class files."); 272 log.println(); 273 log.println("where options include:"); 274 for (Option o : Options.recognizedOptions) { 275 String name = o.aliases[0].substring(1); // there must always be at least one name 276 name = name.charAt(0) == '-' ? name.substring(1) : name; 277 if (o.isHidden() || name.equals("h")) { 278 continue; 279 } 280 log.println(o.help); 281 } 282 log.flush(); 283 } 284 285 private void showVersion() { 286 log.println(PROGNAME + " " + JVM_VERSION); 287 } 288 } |