1 /*
   2  * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  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.LinkedList;
  47 import java.util.List;
  48 import java.util.Set;
  49 import java.util.stream.Stream;
  50 
  51 import jdk.tools.jaotc.binformat.BinaryContainer;
  52 import jdk.tools.jaotc.binformat.ByteContainer;
  53 import jdk.tools.jaotc.collect.ClassCollector;
  54 import jdk.tools.jaotc.utils.Timer;
  55 
  56 import org.graalvm.compiler.api.runtime.GraalJVMCICompiler;
  57 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
  58 import org.graalvm.compiler.hotspot.HotSpotHostBackend;
  59 import org.graalvm.compiler.runtime.RuntimeProvider;
  60 
  61 import jdk.vm.ci.meta.MetaAccessProvider;
  62 import jdk.vm.ci.meta.ResolvedJavaMethod;
  63 import jdk.vm.ci.meta.ResolvedJavaType;
  64 import jdk.vm.ci.runtime.JVMCI;
  65 
  66 public class Main implements LogPrinter {
  67     static {
  68         GeneratePIC.setValue(true);
  69         ImmutableCode.setValue(true);
  70     }
  71 
  72     static class BadArgs extends Exception {
  73         private static final long serialVersionUID = 1L;
  74         final String key;
  75         final Object[] args;
  76         boolean showUsage;
  77 
  78         BadArgs(String key, Object... args) {
  79             super(MessageFormat.format(key, args));
  80             this.key = key;
  81             this.args = args;
  82         }
  83 
  84         BadArgs showUsage(boolean b) {
  85             showUsage = b;
  86             return this;
  87         }
  88     }
  89 
  90     abstract static class Option {
  91         final String help;
  92         final boolean hasArg;
  93         final String[] aliases;
  94 
  95         Option(String help, boolean hasArg, String... aliases) {
  96             this.help = help;
  97             this.hasArg = hasArg;
  98             this.aliases = aliases;
  99         }
 100 
 101         boolean isHidden() {
 102             return false;
 103         }
 104 
 105         boolean matches(String opt) {
 106             for (String a : aliases) {
 107                 if (a.equals(opt)) {
 108                     return true;
 109                 } else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) {
 110                     return true;
 111                 }
 112             }
 113             return false;
 114         }
 115 
 116         boolean ignoreRest() {
 117             return false;
 118         }
 119 
 120         abstract void process(Main task, String opt, String arg) throws BadArgs;
 121     }
 122 
 123     static Option[] recognizedOptions = {new Option("  --module <name>            Module to compile", true, "--module") {
 124         @Override
 125         void process(Main task, String opt, String arg) {
 126             task.options.module = arg;
 127         }
 128     }, new Option("  --module-path <path>       Specify where to find module to compile", true, "--module-path") {
 129         @Override
 130         void process(Main task, String opt, String arg) {
 131             task.options.modulepath = arg;
 132         }
 133     }, new Option("  --output <file>            Output file name", true, "--output") {
 134         @Override
 135         void process(Main task, String opt, String arg) {
 136             String name = arg;
 137             task.options.outputName = name;
 138         }
 139     }, new Option("  --compile-commands <file>  Name of file with compile commands", true, "--compile-commands") {
 140         @Override
 141         void process(Main task, String opt, String arg) {
 142             task.options.methodList = arg;
 143         }
 144     }, new Option("  --compile-for-tiered       Generated profiling code for tiered compilation", false, "--compile-for-tiered") {
 145         @Override
 146         void process(Main task, String opt, String arg) {
 147             TieredAOT.setValue(true);
 148         }
 149     }, new Option("  --classpath <path>         Specify where to find user class files", true, "--classpath", "--class-path") {
 150         @Override
 151         void process(Main task, String opt, String arg) {
 152             task.options.classpath = arg;
 153         }
 154     }, new Option("  --threads <number>         Number of compilation threads to be used", true, "--threads") {
 155         @Override
 156         void process(Main task, String opt, String arg) {
 157             int threads = Integer.parseInt(arg);
 158             final int available = Runtime.getRuntime().availableProcessors();
 159             if (threads <= 0) {
 160                 task.warning("invalid number of threads specified: {0}, using: {1}", threads, available);
 161                 threads = available;
 162             }
 163             if (threads > available) {
 164                 task.warning("too many threads specified: {0}, limiting to: {1}", threads, available);
 165             }
 166             task.options.threads = Integer.min(threads, available);
 167         }
 168     }, new Option("  --ignore-errors            Ignores all exceptions thrown during class loading", false, "--ignore-errors") {
 169         @Override
 170         void process(Main task, String opt, String arg) {
 171             task.options.ignoreClassLoadingErrors = true;
 172         }
 173     }, new Option("  --exit-on-error            Exit on compilation errors", false, "--exit-on-error") {
 174         @Override
 175         void process(Main task, String opt, String arg) {
 176             task.options.exitOnError = true;
 177         }
 178     }, new Option("  --info                     Print information during compilation", false, "--info") {
 179         @Override
 180         void process(Main task, String opt, String arg) throws BadArgs {
 181             task.options.info = true;
 182         }
 183     }, new Option("  --verbose                  Print verbose information", false, "--verbose") {
 184         @Override
 185         void process(Main task, String opt, String arg) throws BadArgs {
 186             task.options.info = true;
 187             task.options.verbose = true;
 188         }
 189     }, new Option("  --debug                    Print debug information", false, "--debug") {
 190         @Override
 191         void process(Main task, String opt, String arg) throws BadArgs {
 192             task.options.info = true;
 193             task.options.verbose = true;
 194             task.options.debug = true;
 195         }
 196     }, new Option("  --help                     Print this usage message", false, "--help") {
 197         @Override
 198         void process(Main task, String opt, String arg) {
 199             task.options.help = true;
 200         }
 201     }, new Option("  --version                  Version information", false, "--version") {
 202         @Override
 203         void process(Main task, String opt, String arg) {
 204             task.options.version = true;
 205         }
 206     }, new Option("  -J<flag>                   Pass <flag> directly to the runtime system", false, "-J") {
 207         @Override
 208         void process(Main task, String opt, String arg) {
 209         }
 210     }};
 211 
 212     public static class Options {
 213         public List<String> files = new LinkedList<>();
 214         public String module = null;
 215         public String modulepath = "modules";
 216         public String outputName = "unnamed.so";
 217         public String methodList;
 218         public String classpath = ".";
 219 
 220         /**
 221          * We don't see scaling beyond 16 threads.
 222          */
 223         private static final int COMPILER_THREADS = 16;
 224 
 225         int threads = Integer.min(COMPILER_THREADS, Runtime.getRuntime().availableProcessors());
 226 
 227         public boolean ignoreClassLoadingErrors;
 228         public boolean exitOnError;
 229         boolean info;
 230         boolean verbose;
 231         boolean debug;
 232         boolean help;
 233         boolean version;
 234     }
 235 
 236     /* package */final Options options = new Options();
 237 
 238     /**
 239      * Logfile.
 240      */
 241     private static FileWriter logFile = null;
 242 
 243     private static final int EXIT_OK = 0;        // No errors.
 244     private static final int EXIT_CMDERR = 2;    // Bad command-line arguments and/or switches.
 245     private static final int EXIT_ABNORMAL = 4;  // Terminated abnormally.
 246 
 247     private static final String PROGNAME = "jaotc";
 248 
 249     private static final String JVM_VERSION = System.getProperty("java.runtime.version");
 250 
 251     public static void main(String[] args) throws Exception {
 252         Main t = new Main();
 253         final int exitCode = t.run(args);
 254         System.exit(exitCode);
 255     }
 256 
 257     private int run(String[] args) {
 258         if (log == null) {
 259             log = new PrintWriter(System.out);
 260         }
 261 
 262         try {
 263             handleOptions(args);
 264             if (options.help) {
 265                 showHelp();
 266                 return EXIT_OK;
 267             }
 268             if (options.version) {
 269                 showVersion();
 270                 return EXIT_OK;
 271             }
 272 
 273             printlnInfo("Compiling " + options.outputName + "...");
 274             final long start = System.currentTimeMillis();
 275             run();
 276             final long end = System.currentTimeMillis();
 277             printlnInfo("Total time: " + (end - start) + " ms");
 278 
 279             return EXIT_OK;
 280         } catch (BadArgs e) {
 281             reportError(e.key, e.args);
 282             if (e.showUsage) {
 283                 showUsage();
 284             }
 285             return EXIT_CMDERR;
 286         } catch (Exception e) {
 287             e.printStackTrace();
 288             return EXIT_ABNORMAL;
 289         } finally {
 290             log.flush();
 291         }
 292     }
 293 
 294     private static String humanReadableByteCount(long bytes) {
 295         int unit = 1024;
 296 
 297         if (bytes < unit) {
 298             return bytes + " B";
 299         }
 300 
 301         int exp = (int) (Math.log(bytes) / Math.log(unit));
 302         char pre = "KMGTPE".charAt(exp - 1);
 303         return String.format("%.1f %cB", bytes / Math.pow(unit, exp), pre);
 304     }
 305 
 306     void printMemoryUsage() {
 307         if (options.verbose) {
 308             MemoryUsage memusage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
 309             float freeratio = 1f - (float) memusage.getUsed() / memusage.getCommitted();
 310             log.format(" [used: %-7s, comm: %-7s, freeRatio ~= %.1f%%]",
 311                             humanReadableByteCount(memusage.getUsed()),
 312                             humanReadableByteCount(memusage.getCommitted()),
 313                             freeratio * 100);
 314         }
 315     }
 316 
 317     /**
 318      * Search for Visual Studio link.exe
 319      * Search Order is:  VS2013, VS2015, VS2012
 320      */
 321     private String getWindowsLinkPath() {
 322         String vs2013 = "C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\bin\\amd64\\link.exe";
 323         String vs2015 = "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\amd64\\link.exe";
 324         String vs2012 = "C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\VC\\bin\\amd64\\link.exe";
 325 
 326         if (new File(vs2015).exists()) return vs2015;
 327         if (new File(vs2013).exists()) return vs2013;
 328         if (new File(vs2012).exists()) return vs2012;
 329         return null;
 330     }
 331 
 332     @SuppressWarnings("try")
 333     private void run() throws Exception {
 334         openLog();
 335 
 336         try {
 337             CompilationSpec compilationRestrictions = collectSpecifiedMethods();
 338 
 339             Set<Class<?>> classesToCompile;
 340 
 341             try (Timer t = new Timer(this, "")) {
 342                 ClassCollector collector = new ClassCollector(this.options, this);
 343                 classesToCompile = collector.collectClassesToCompile();
 344                 printInfo(classesToCompile.size() + " classes found");
 345             }
 346 
 347             GraalJVMCICompiler graalCompiler = (GraalJVMCICompiler) JVMCI.getRuntime().getCompiler();
 348             HotSpotGraalRuntimeProvider runtime = (HotSpotGraalRuntimeProvider) graalCompiler.getGraalRuntime();
 349             HotSpotHostBackend backend = (HotSpotHostBackend) runtime.getCapability(RuntimeProvider.class).getHostBackend();
 350             MetaAccessProvider metaAccess = backend.getProviders().getMetaAccess();
 351             GraalFilters filters = new GraalFilters(metaAccess);
 352 
 353             List<AOTCompiledClass> classes;
 354 
 355             try (Timer t = new Timer(this, "")) {
 356                 classes = collectMethodsToCompile(classesToCompile, compilationRestrictions, filters, metaAccess);
 357             }
 358 
 359             // Free memory!
 360             try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) {
 361                 printMemoryUsage();
 362                 compilationRestrictions = null;
 363                 classesToCompile = null;
 364                 System.gc();
 365             }
 366 
 367             AOTBackend aotBackend = new AOTBackend(this, backend, filters);
 368             AOTCompiler compiler = new AOTCompiler(this, aotBackend, options.threads);
 369             classes = compiler.compileClasses(classes);
 370 
 371             // Free memory!
 372             try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) {
 373                 printMemoryUsage();
 374                 aotBackend = null;
 375                 compiler = null;
 376                 System.gc();
 377             }
 378 
 379             BinaryContainer binaryContainer = new BinaryContainer(runtime.getVMConfig(), JVM_VERSION);
 380             DataBuilder dataBuilder = new DataBuilder(this, backend, classes, binaryContainer);
 381             dataBuilder.prepareData();
 382 
 383             // Print information about section sizes
 384             printContainerInfo(binaryContainer.getHeaderContainer().getContainer());
 385             printContainerInfo(binaryContainer.getConfigContainer());
 386             printContainerInfo(binaryContainer.getKlassesOffsetsContainer());
 387             printContainerInfo(binaryContainer.getMethodsOffsetsContainer());
 388             printContainerInfo(binaryContainer.getKlassesDependenciesContainer());
 389             printContainerInfo(binaryContainer.getStubsOffsetsContainer());
 390             printContainerInfo(binaryContainer.getMethodMetadataContainer());
 391             printContainerInfo(binaryContainer.getCodeContainer());
 392             printContainerInfo(binaryContainer.getCodeSegmentsContainer());
 393             printContainerInfo(binaryContainer.getConstantDataContainer());
 394             printContainerInfo(binaryContainer.getMetaspaceGotContainer());
 395             printContainerInfo(binaryContainer.getMetadataGotContainer());
 396             printContainerInfo(binaryContainer.getMethodStateContainer());
 397             printContainerInfo(binaryContainer.getOopGotContainer());
 398             printContainerInfo(binaryContainer.getMetaspaceNamesContainer());
 399 
 400             // Free memory!
 401             try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) {
 402                 printMemoryUsage();
 403                 backend = null;
 404                 for (AOTCompiledClass aotCompClass : classes) {
 405                     aotCompClass.clear();
 406                 }
 407                 classes.clear();
 408                 classes = null;
 409                 dataBuilder = null;
 410                 binaryContainer.freeMemory();
 411                 System.gc();
 412             }
 413 
 414             String name = options.outputName;
 415             String objectFileName = name;
 416 
 417             // [TODO] The jtregs tests expect .so extension so don't
 418             // override with platform specific file extension until the
 419             // tests are fixed.
 420             String libraryFileName = name;
 421 
 422             String ldCmd;
 423             String osName = System.getProperty("os.name");
 424 
 425             if (name.endsWith(".so")) {
 426                 objectFileName = name.substring(0, name.length() - ".so".length());
 427             }
 428             else if (name.endsWith(".dylib")) {
 429                 objectFileName = name.substring(0, name.length() - ".dylib".length());
 430             }
 431             else if (name.endsWith(".dll")) {
 432                 objectFileName = name.substring(0, name.length() - ".dll".length());
 433             }
 434 
 435             switch (osName) {
 436                 case "Linux":
 437                     // libraryFileName = options.outputName + ".so";
 438                     objectFileName = objectFileName + ".o";
 439                     ldCmd = "ld -shared -z noexecstack -o " + libraryFileName + " " + objectFileName;
 440                     break;
 441                 case "SunOS":
 442                     // libraryFileName = options.outputName + ".so";
 443                     objectFileName = objectFileName + ".o";
 444                     ldCmd = "ld -shared -o " + libraryFileName + " " + objectFileName;
 445                     break;
 446                 case "Mac OS X":
 447                     // libraryFileName = options.outputName + ".dylib";
 448                     objectFileName = objectFileName + ".o";
 449                     ldCmd = "ld -dylib -o " + libraryFileName + " " + objectFileName;
 450                     break;
 451                 default:
 452                     if (osName.startsWith("Windows")) {
 453                         // libraryFileName = options.outputName + ".dll";
 454                         objectFileName = objectFileName + ".obj";
 455                         String linkpath = getWindowsLinkPath();
 456                         if (linkpath == null) {
 457                             throw new InternalError("Can't locate Microsoft Visual Studio amd64 link.exe");
 458                         }
 459                         ldCmd = linkpath + " /DLL /OPT:NOREF /NOLOGO /NOENTRY" + " /OUT:" + libraryFileName + " " + objectFileName;
 460                         break;
 461                     }
 462                     else
 463                         throw new InternalError("Unsupported platform: " + osName);
 464             }
 465 
 466             try (Timer t = new Timer(this, "Creating binary: " + objectFileName)) {
 467                 binaryContainer.createBinary(objectFileName, JVM_VERSION);
 468             }
 469 
 470             // Free memory!
 471             try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) {
 472                 printMemoryUsage();
 473                 binaryContainer = null;
 474                 System.gc();
 475             }
 476 
 477             try (Timer t = new Timer(this, "Creating shared library: " + libraryFileName)) {
 478                 Process p = Runtime.getRuntime().exec(ldCmd);
 479                 final int exitCode = p.waitFor();
 480                 if (exitCode != 0) {
 481                     InputStream stderr = p.getErrorStream();
 482                     BufferedReader br = new BufferedReader(new InputStreamReader(stderr));
 483                     Stream<String> lines = br.lines();
 484                     StringBuilder sb = new StringBuilder();
 485                     lines.iterator().forEachRemaining(e -> sb.append(e));
 486                     throw new InternalError(sb.toString());
 487                 }
 488                 File objFile = new File(objectFileName);
 489                 if (objFile.exists()) {
 490                     if (!objFile.delete()) {
 491                         throw new InternalError("Failed to delete " + objectFileName + " file");
 492                     }
 493                 }
 494                 // Make non-executable for all.
 495                 File libFile = new File(libraryFileName);
 496                 if (libFile.exists() && !osName.startsWith("Windows")) {
 497                     if (!libFile.setExecutable(false, false)) {
 498                         throw new InternalError("Failed to change attribute for " + libraryFileName + " file");
 499                     }
 500                 }
 501             }
 502 
 503             printVerbose("Final memory  ");
 504             printMemoryUsage();
 505             printlnVerbose("");
 506 
 507         } finally {
 508             closeLog();
 509         }
 510     }
 511 
 512     private void addMethods(AOTCompiledClass aotClass, ResolvedJavaMethod[] methods, CompilationSpec compilationRestrictions, GraalFilters filters) {
 513         for (ResolvedJavaMethod m : methods) {
 514             addMethod(aotClass, m, compilationRestrictions, filters);
 515         }
 516     }
 517 
 518     private void addMethod(AOTCompiledClass aotClass, ResolvedJavaMethod method, CompilationSpec compilationRestrictions, GraalFilters filters) {
 519         // Don't compile native or abstract methods.
 520         if (!method.hasBytecodes()) {
 521             return;
 522         }
 523         if (!compilationRestrictions.shouldCompileMethod(method)) {
 524             return;
 525         }
 526         if (!filters.shouldCompileMethod(method)) {
 527             return;
 528         }
 529 
 530         aotClass.addMethod(method);
 531         printlnVerbose("  added " + method.getName() + method.getSignature().toMethodDescriptor());
 532     }
 533 
 534     private void printContainerInfo(ByteContainer container) {
 535         printlnVerbose(container.getContainerName() + ": " + container.getByteStreamSize() + " bytes");
 536     }
 537 
 538     PrintWriter log;
 539 
 540     private void handleOptions(String[] args) throws BadArgs {
 541         if (args.length == 0) {
 542             options.help = true;
 543             return;
 544         }
 545 
 546         // Make checkstyle happy.
 547         int i = 0;
 548         for (; i < args.length; i++) {
 549             String arg = args[i];
 550 
 551             if (arg.charAt(0) == '-') {
 552                 Option option = getOption(arg);
 553                 String param = null;
 554 
 555                 if (option.hasArg) {
 556                     if (arg.startsWith("--") && arg.indexOf('=') > 0) {
 557                         param = arg.substring(arg.indexOf('=') + 1, arg.length());
 558                     } else if (i + 1 < args.length) {
 559                         param = args[++i];
 560                     }
 561 
 562                     if (param == null || param.isEmpty() || param.charAt(0) == '-') {
 563                         throw new BadArgs("missing argument for option: {0}", arg).showUsage(true);
 564                     }
 565                 }
 566 
 567                 option.process(this, arg, param);
 568 
 569                 if (option.ignoreRest()) {
 570                     break;
 571                 }
 572             } else {
 573                 options.files.add(arg);
 574             }
 575         }
 576     }
 577 
 578     private static Option getOption(String name) throws BadArgs {
 579         for (Option o : recognizedOptions) {
 580             if (o.matches(name)) {
 581                 return o;
 582             }
 583         }
 584         throw new BadArgs("unknown option: {0}", name).showUsage(true);
 585     }
 586 
 587     public void printInfo(String message) {
 588         if (options.info) {
 589             log.print(message);
 590             log.flush();
 591         }
 592     }
 593 
 594     public void printlnInfo(String message) {
 595         if (options.info) {
 596             log.println(message);
 597             log.flush();
 598         }
 599     }
 600 
 601     public void printVerbose(String message) {
 602         if (options.verbose) {
 603             log.print(message);
 604             log.flush();
 605         }
 606     }
 607 
 608     public void printlnVerbose(String message) {
 609         if (options.verbose) {
 610             log.println(message);
 611             log.flush();
 612         }
 613     }
 614 
 615     public void printDebug(String message) {
 616         if (options.debug) {
 617             log.print(message);
 618             log.flush();
 619         }
 620     }
 621 
 622     public void printlnDebug(String message) {
 623         if (options.debug) {
 624             log.println(message);
 625             log.flush();
 626         }
 627     }
 628 
 629     public void printError(String message) {
 630         log.println("Error: " + message);
 631         log.flush();
 632     }
 633 
 634     private void reportError(String key, Object... args) {
 635         printError(MessageFormat.format(key, args));
 636     }
 637 
 638     private void warning(String key, Object... args) {
 639         log.println("Warning: " + MessageFormat.format(key, args));
 640         log.flush();
 641     }
 642 
 643     private void showUsage() {
 644         log.println("Usage: " + PROGNAME + " <options> list...");
 645         log.println("use --help for a list of possible options");
 646     }
 647 
 648     private void showHelp() {
 649         log.println("Usage: " + PROGNAME + " <options> <--module name> | <list...>");
 650         log.println();
 651         log.println("  list       A list of class files, jar files or directories which");
 652         log.println("             contains class files.");
 653         log.println();
 654         log.println("where possible options include:");
 655         for (Option o : recognizedOptions) {
 656             String name = o.aliases[0].substring(1); // there must always be at least one name
 657             name = name.charAt(0) == '-' ? name.substring(1) : name;
 658             if (o.isHidden() || name.equals("h")) {
 659                 continue;
 660             }
 661             log.println(o.help);
 662         }
 663     }
 664 
 665     private void showVersion() {
 666         log.println(PROGNAME + " " + JVM_VERSION);
 667     }
 668 
 669     /**
 670      * Collect all method we should compile.
 671      *
 672      * @return array list of AOT classes which have compiled methods.
 673      */
 674     private List<AOTCompiledClass> collectMethodsToCompile(Set<Class<?>> classesToCompile, CompilationSpec compilationRestrictions, GraalFilters filters, MetaAccessProvider metaAccess) {
 675         int total = 0;
 676         int count = 0;
 677         List<AOTCompiledClass> classes = new ArrayList<>();
 678 
 679         for (Class<?> c : classesToCompile) {
 680             ResolvedJavaType resolvedJavaType = metaAccess.lookupJavaType(c);
 681             if (filters.shouldCompileAnyMethodInClass(resolvedJavaType)) {
 682                 AOTCompiledClass aotClass = new AOTCompiledClass(resolvedJavaType);
 683                 printlnVerbose(" Scanning " + c.getName());
 684 
 685                 // Constructors
 686                 try {
 687                     ResolvedJavaMethod[] ctors = resolvedJavaType.getDeclaredConstructors();
 688                     addMethods(aotClass, ctors, compilationRestrictions, filters);
 689                     total += ctors.length;
 690                 } catch (Throwable e) {
 691                     // If we are running in JCK mode we ignore all exceptions.
 692                     if (options.ignoreClassLoadingErrors) {
 693                         printError(c.getName() + ": " + e);
 694                     } else {
 695                         throw new InternalError(e);
 696                     }
 697                 }
 698 
 699                 // Methods
 700                 try {
 701                     ResolvedJavaMethod[] methods = resolvedJavaType.getDeclaredMethods();
 702                     addMethods(aotClass, methods, compilationRestrictions, filters);
 703                     total += methods.length;
 704                 } catch (Throwable e) {
 705                     // If we are running in JCK mode we ignore all exceptions.
 706                     if (options.ignoreClassLoadingErrors) {
 707                         printError(c.getName() + ": " + e);
 708                     } else {
 709                         throw new InternalError(e);
 710                     }
 711                 }
 712 
 713                 // Class initializer
 714                 try {
 715                     ResolvedJavaMethod clinit = resolvedJavaType.getClassInitializer();
 716                     if (clinit != null) {
 717                         addMethod(aotClass, clinit, compilationRestrictions, filters);
 718                         total++;
 719                     }
 720                 } catch (Throwable e) {
 721                     // If we are running in JCK mode we ignore all exceptions.
 722                     if (options.ignoreClassLoadingErrors) {
 723                         printError(c.getName() + ": " + e);
 724                     } else {
 725                         throw new InternalError(e);
 726                     }
 727                 }
 728 
 729                 // Found any methods to compile? Add the class.
 730                 if (aotClass.hasMethods()) {
 731                     classes.add(aotClass);
 732                     count += aotClass.getMethodCount();
 733                 }
 734             }
 735         }
 736         printInfo(total + " methods total, " + count + " methods to compile");
 737         return classes;
 738     }
 739 
 740     /**
 741      * If a file with compilation limitations is specified using the java property
 742      * jdk.tools.jaotc.compile.method.list, read the file's contents and collect the restrictions.
 743      */
 744     private CompilationSpec collectSpecifiedMethods() {
 745         CompilationSpec compilationRestrictions = new CompilationSpec();
 746         String methodListFileName = options.methodList;
 747 
 748         if (methodListFileName != null && !methodListFileName.equals("")) {
 749             try {
 750                 FileReader methListFile = new FileReader(methodListFileName);
 751                 BufferedReader readBuf = new BufferedReader(methListFile);
 752                 String line = null;
 753                 while ((line = readBuf.readLine()) != null) {
 754                     String trimmedLine = line.trim();
 755                     if (!trimmedLine.startsWith("#")) {
 756                         String[] components = trimmedLine.split(" ");
 757                         if (components.length == 2) {
 758                             String directive = components[0];
 759                             String pattern = components[1];
 760                             switch (directive) {
 761                                 case "compileOnly":
 762                                     compilationRestrictions.addCompileOnlyPattern(pattern);
 763                                     break;
 764                                 case "exclude":
 765                                     compilationRestrictions.addExcludePattern(pattern);
 766                                     break;
 767                                 default:
 768                                     System.out.println("Unrecognized command " + directive + ". Ignoring\n\t" + line + "\n encountered in " + methodListFileName);
 769                             }
 770                         } else {
 771                             if (!trimmedLine.equals("")) {
 772                                 System.out.println("Ignoring malformed line:\n\t " + line + "\n");
 773                             }
 774                         }
 775                     }
 776                 }
 777                 readBuf.close();
 778             } catch (FileNotFoundException e) {
 779                 throw new InternalError("Unable to open method list file: " + methodListFileName, e);
 780             } catch (IOException e) {
 781                 throw new InternalError("Unable to read method list file: " + methodListFileName, e);
 782             }
 783         }
 784 
 785         return compilationRestrictions;
 786     }
 787 
 788     private static void openLog() {
 789         int v = Integer.getInteger("jdk.tools.jaotc.logCompilation", 0);
 790         if (v == 0) {
 791             logFile = null;
 792             return;
 793         }
 794         // Create log file in current directory
 795         String fileName = "aot_compilation" + new Date().getTime() + ".log";
 796         Path logFilePath = Paths.get("./", fileName);
 797         String logFileName = logFilePath.toString();
 798         try {
 799             // Create file to which we do not append
 800             logFile = new FileWriter(logFileName, false);
 801         } catch (IOException e) {
 802             System.out.println("Unable to open logfile :" + logFileName + "\nNo logs will be created");
 803             logFile = null;
 804         }
 805     }
 806 
 807     public static void writeLog(String str) {
 808         if (logFile != null) {
 809             try {
 810                 logFile.write(str + "\n");
 811                 logFile.flush();
 812             } catch (IOException e) {
 813                 // Print to console
 814                 System.out.println(str + "\n");
 815             }
 816         }
 817     }
 818 
 819     public static void closeLog() {
 820         if (logFile != null) {
 821             try {
 822                 logFile.close();
 823             } catch (IOException e) {
 824                 // Do nothing
 825             }
 826         }
 827     }
 828 }