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.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.runtime.GraalJVMCICompiler;
  63 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
  64 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
  65 import org.graalvm.compiler.hotspot.HotSpotHostBackend;
  66 import org.graalvm.compiler.java.GraphBuilderPhase;
  67 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
  68 import org.graalvm.compiler.phases.BasePhase;
  69 import org.graalvm.compiler.phases.PhaseSuite;
  70 import org.graalvm.compiler.phases.tiers.HighTierContext;
  71 import org.graalvm.compiler.runtime.RuntimeProvider;
  72 
  73 import jdk.vm.ci.meta.MetaAccessProvider;
  74 import jdk.vm.ci.meta.ResolvedJavaMethod;
  75 import jdk.vm.ci.meta.ResolvedJavaType;
  76 import jdk.vm.ci.runtime.JVMCI;
  77 
  78 public class Main implements LogPrinter {
  79     static {
  80         GeneratePIC.setValue(true);
  81         ImmutableCode.setValue(true);
  82     }
  83 
  84     static class BadArgs extends Exception {
  85         private static final long serialVersionUID = 1L;
  86         final String key;
  87         final Object[] args;
  88         boolean showUsage;
  89 
  90         BadArgs(String key, Object... args) {
  91             super(MessageFormat.format(key, args));
  92             this.key = key;
  93             this.args = args;
  94         }
  95 
  96         BadArgs showUsage(boolean b) {
  97             showUsage = b;
  98             return this;
  99         }
 100     }
 101 
 102     abstract static class Option {
 103         final String help;
 104         final boolean hasArg;
 105         final String[] aliases;
 106 
 107         Option(String help, boolean hasArg, String... aliases) {
 108             this.help = help;
 109             this.hasArg = hasArg;
 110             this.aliases = aliases;
 111         }
 112 
 113         boolean isHidden() {
 114             return false;
 115         }
 116 
 117         boolean matches(String opt) {
 118             for (String a : aliases) {
 119                 if (a.equals(opt)) {
 120                     return true;
 121                 } else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) {
 122                     return true;
 123                 }
 124             }
 125             return false;
 126         }
 127 
 128         boolean ignoreRest() {
 129             return false;
 130         }
 131 
 132         abstract void process(Main task, String opt, String arg) throws BadArgs;
 133     }
 134 
 135     static Option[] recognizedOptions = { new Option("  --output <file>            Output file name", true, "--output") {
 136         @Override
 137         void process(Main task, String opt, String arg) {
 138             String name = arg;
 139             if (name.endsWith(".so")) {
 140                 name = name.substring(0, name.length() - ".so".length());
 141             }
 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             TieredAOT.setValue(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("  -J<flag>                   Pass <flag> directly to the runtime system", false, "-J") {
 238         @Override
 239         void process(Main task, String opt, String arg) {
 240         }
 241     }};
 242 
 243     public static class Options {
 244         public List<SearchFor> files = new LinkedList<>();
 245         public String outputName = "unnamed";
 246         public String methodList;
 247         public List<ClassSource> sources = new ArrayList<>();
 248         public SearchPath searchPath = new SearchPath();
 249 
 250         /**
 251          * We don't see scaling beyond 16 threads.
 252          */
 253         private static final int COMPILER_THREADS = 16;
 254 
 255         public int threads = Integer.min(COMPILER_THREADS, Runtime.getRuntime().availableProcessors());
 256 
 257         public boolean ignoreClassLoadingErrors;
 258         public boolean exitOnError;
 259         public boolean info;
 260         public boolean verbose;
 261         public boolean debug;
 262         public boolean help;
 263         public boolean version;
 264         public boolean compileWithAssertions;
 265     }
 266 
 267     /* package */final Options options = new Options();
 268 
 269     /**
 270      * Logfile.
 271      */
 272     private static FileWriter logFile = null;
 273 
 274     private static final int EXIT_OK = 0;        // No errors.
 275     private static final int EXIT_CMDERR = 2;    // Bad command-line arguments and/or switches.
 276     private static final int EXIT_ABNORMAL = 4;  // Terminated abnormally.
 277 
 278     private static final String PROGNAME = "jaotc";
 279 
 280     private static final String JVM_VERSION = System.getProperty("java.runtime.version");
 281 
 282     public static void main(String[] args) throws Exception {
 283         Main t = new Main();
 284         final int exitCode = t.run(args);
 285         System.exit(exitCode);
 286     }
 287 
 288     private int run(String[] args) {
 289         if (log == null) {
 290             log = new PrintWriter(System.out);
 291         }
 292 
 293         try {
 294             handleOptions(args);
 295             if (options.help) {
 296                 showHelp();
 297                 return EXIT_OK;
 298             }
 299             if (options.version) {
 300                 showVersion();
 301                 return EXIT_OK;
 302             }
 303 
 304             printlnInfo("Compiling " + options.outputName + "...");
 305             final long start = System.currentTimeMillis();
 306             if (!run()) {
 307               return EXIT_ABNORMAL;
 308             }
 309             final long end = System.currentTimeMillis();
 310             printlnInfo("Total time: " + (end - start) + " ms");
 311 
 312             return EXIT_OK;
 313         } catch (BadArgs e) {
 314             reportError(e.key, e.args);
 315             if (e.showUsage) {
 316                 showUsage();
 317             }
 318             return EXIT_CMDERR;
 319         } catch (Exception e) {
 320             e.printStackTrace();
 321             return EXIT_ABNORMAL;
 322         } finally {
 323             log.flush();
 324         }
 325     }
 326 
 327     private static String humanReadableByteCount(long bytes) {
 328         int unit = 1024;
 329 
 330         if (bytes < unit) {
 331             return bytes + " B";
 332         }
 333 
 334         int exp = (int) (Math.log(bytes) / Math.log(unit));
 335         char pre = "KMGTPE".charAt(exp - 1);
 336         return String.format("%.1f %cB", bytes / Math.pow(unit, exp), pre);
 337     }
 338 
 339     void printMemoryUsage() {
 340         if (options.verbose) {
 341             MemoryUsage memusage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
 342             float freeratio = 1f - (float) memusage.getUsed() / memusage.getCommitted();
 343             log.format(" [used: %-7s, comm: %-7s, freeRatio ~= %.1f%%]",
 344                             humanReadableByteCount(memusage.getUsed()),
 345                             humanReadableByteCount(memusage.getCommitted()),
 346                             freeratio * 100);
 347         }
 348     }
 349 
 350     @SuppressWarnings("try")
 351     private boolean run() throws Exception {
 352         openLog();
 353 
 354         try {
 355             CompilationSpec compilationRestrictions = collectSpecifiedMethods();
 356 
 357             Set<Class<?>> classesToCompile = new HashSet<>();
 358 
 359             try (Timer t = new Timer(this, "")) {
 360                 FileSupport fileSupport = new FileSupport();
 361                 ClassSearch lookup = new ClassSearch();
 362                 lookup.addProvider(new ModuleSourceProvider());
 363                 lookup.addProvider(new ClassNameSourceProvider(fileSupport));
 364                 lookup.addProvider(new JarSourceProvider());
 365                 lookup.addProvider(new DirectorySourceProvider(fileSupport));
 366 
 367                 List<LoadedClass> found = null;
 368                 try {
 369                     found = lookup.search(options.files, options.searchPath);
 370                 } catch (InternalError e) {
 371                     reportError(e);
 372                     return false;
 373                 }
 374 
 375                 for (LoadedClass loadedClass : found) {
 376                     classesToCompile.add(loadedClass.getLoadedClass());
 377                 }
 378 
 379                 printInfo(classesToCompile.size() + " classes found");
 380             }
 381 
 382             GraalJVMCICompiler graalCompiler = (GraalJVMCICompiler) JVMCI.getRuntime().getCompiler();
 383             HotSpotGraalRuntimeProvider runtime = (HotSpotGraalRuntimeProvider) graalCompiler.getGraalRuntime();
 384             HotSpotHostBackend backend = (HotSpotHostBackend) runtime.getCapability(RuntimeProvider.class).getHostBackend();
 385             MetaAccessProvider metaAccess = backend.getProviders().getMetaAccess();
 386             GraalFilters filters = new GraalFilters(metaAccess);
 387 
 388             List<AOTCompiledClass> classes;
 389 
 390             try (Timer t = new Timer(this, "")) {
 391                 classes = collectMethodsToCompile(classesToCompile, compilationRestrictions, filters, metaAccess);
 392             }
 393 
 394             // Free memory!
 395             try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) {
 396                 printMemoryUsage();
 397                 compilationRestrictions = null;
 398                 classesToCompile = null;
 399                 System.gc();
 400             }
 401 
 402             AOTBackend aotBackend = new AOTBackend(this, backend, filters);
 403             AOTCompiler compiler = new AOTCompiler(this, aotBackend, options.threads);
 404             classes = compiler.compileClasses(classes);
 405 
 406             GraalHotSpotVMConfig graalHotSpotVMConfig = runtime.getVMConfig();
 407             PhaseSuite<HighTierContext> graphBuilderSuite = aotBackend.getGraphBuilderSuite();
 408             ListIterator<BasePhase<? super HighTierContext>> iterator = graphBuilderSuite.findPhase(GraphBuilderPhase.class);
 409             GraphBuilderConfiguration graphBuilderConfig = ((GraphBuilderPhase) iterator.previous()).getGraphBuilderConfig();
 410 
 411             // Free memory!
 412             try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) {
 413                 printMemoryUsage();
 414                 aotBackend = null;
 415                 compiler = null;
 416                 System.gc();
 417             }
 418 
 419             BinaryContainer binaryContainer = new BinaryContainer(graalHotSpotVMConfig, graphBuilderConfig, JVM_VERSION);
 420             DataBuilder dataBuilder = new DataBuilder(this, backend, classes, binaryContainer);
 421             dataBuilder.prepareData();
 422 
 423             // Print information about section sizes
 424             printContainerInfo(binaryContainer.getHeaderContainer().getContainer());
 425             printContainerInfo(binaryContainer.getConfigContainer());
 426             printContainerInfo(binaryContainer.getKlassesOffsetsContainer());
 427             printContainerInfo(binaryContainer.getMethodsOffsetsContainer());
 428             printContainerInfo(binaryContainer.getKlassesDependenciesContainer());
 429             printContainerInfo(binaryContainer.getStubsOffsetsContainer());
 430             printContainerInfo(binaryContainer.getMethodMetadataContainer());
 431             printContainerInfo(binaryContainer.getCodeContainer());
 432             printContainerInfo(binaryContainer.getCodeSegmentsContainer());
 433             printContainerInfo(binaryContainer.getConstantDataContainer());
 434             printContainerInfo(binaryContainer.getMetaspaceGotContainer());
 435             printContainerInfo(binaryContainer.getMetadataGotContainer());
 436             printContainerInfo(binaryContainer.getMethodStateContainer());
 437             printContainerInfo(binaryContainer.getOopGotContainer());
 438             printContainerInfo(binaryContainer.getMetaspaceNamesContainer());
 439 
 440             // Free memory!
 441             try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) {
 442                 printMemoryUsage();
 443                 backend = null;
 444                 for (AOTCompiledClass aotCompClass : classes) {
 445                     aotCompClass.clear();
 446                 }
 447                 classes.clear();
 448                 classes = null;
 449                 dataBuilder = null;
 450                 binaryContainer.freeMemory();
 451                 System.gc();
 452             }
 453 
 454             String objectFileName = options.outputName + ".o";
 455             String libraryFileName = options.outputName + ".so";
 456 
 457             try (Timer t = new Timer(this, "Creating binary: " + objectFileName)) {
 458                 binaryContainer.createBinary(objectFileName, JVM_VERSION);
 459             }
 460 
 461             // Free memory!
 462             try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) {
 463                 printMemoryUsage();
 464                 binaryContainer = null;
 465                 System.gc();
 466             }
 467 
 468             try (Timer t = new Timer(this, "Creating shared library: " + libraryFileName)) {
 469                 Process p = Runtime.getRuntime().exec("ld -shared -z noexecstack -o " + libraryFileName + " " + objectFileName);
 470                 final int exitCode = p.waitFor();
 471                 if (exitCode != 0) {
 472                     InputStream stderr = p.getErrorStream();
 473                     BufferedReader br = new BufferedReader(new InputStreamReader(stderr));
 474                     Stream<String> lines = br.lines();
 475                     StringBuilder sb = new StringBuilder();
 476                     lines.iterator().forEachRemaining(e -> sb.append(e));
 477                     throw new InternalError(sb.toString());
 478                 }
 479                 File objFile = new File(objectFileName);
 480                 if (objFile.exists()) {
 481                     if (!objFile.delete()) {
 482                         throw new InternalError("Failed to delete " + objectFileName + " file");
 483                     }
 484                 }
 485                 // Make non-executable for all.
 486                 File libFile = new File(libraryFileName);
 487                 if (libFile.exists()) {
 488                     if (!libFile.setExecutable(false, false)) {
 489                         throw new InternalError("Failed to change attribute for " + libraryFileName + " file");
 490                     }
 491                 }
 492             }
 493 
 494             printVerbose("Final memory  ");
 495             printMemoryUsage();
 496             printlnVerbose("");
 497 
 498         } finally {
 499             closeLog();
 500         }
 501         return true;
 502     }
 503 
 504     private void addMethods(AOTCompiledClass aotClass, ResolvedJavaMethod[] methods, CompilationSpec compilationRestrictions, GraalFilters filters) {
 505         for (ResolvedJavaMethod m : methods) {
 506             addMethod(aotClass, m, compilationRestrictions, filters);
 507         }
 508     }
 509 
 510     private void addMethod(AOTCompiledClass aotClass, ResolvedJavaMethod method, CompilationSpec compilationRestrictions, GraalFilters filters) {
 511         // Don't compile native or abstract methods.
 512         if (!method.hasBytecodes()) {
 513             return;
 514         }
 515         if (!compilationRestrictions.shouldCompileMethod(method)) {
 516             return;
 517         }
 518         if (!filters.shouldCompileMethod(method)) {
 519             return;
 520         }
 521 
 522         aotClass.addMethod(method);
 523         printlnVerbose("  added " + method.getName() + method.getSignature().toMethodDescriptor());
 524     }
 525 
 526     private void printContainerInfo(ByteContainer container) {
 527         printlnVerbose(container.getContainerName() + ": " + container.getByteStreamSize() + " bytes");
 528     }
 529 
 530     PrintWriter log;
 531 
 532     private void handleOptions(String[] args) throws BadArgs {
 533         if (args.length == 0) {
 534             options.help = true;
 535             return;
 536         }
 537 
 538         // Make checkstyle happy.
 539         int i = 0;
 540         for (; i < args.length; i++) {
 541             String arg = args[i];
 542 
 543             if (arg.charAt(0) == '-') {
 544                 Option option = getOption(arg);
 545                 String param = null;
 546 
 547                 if (option.hasArg) {
 548                     if (arg.startsWith("--") && arg.indexOf('=') > 0) {
 549                         param = arg.substring(arg.indexOf('=') + 1, arg.length());
 550                     } else if (i + 1 < args.length) {
 551                         param = args[++i];
 552                     }
 553 
 554                     if (param == null || param.isEmpty() || param.charAt(0) == '-') {
 555                         throw new BadArgs("missing argument for option: {0}", arg).showUsage(true);
 556                     }
 557                 }
 558 
 559                 option.process(this, arg, param);
 560 
 561                 if (option.ignoreRest()) {
 562                     break;
 563                 }
 564             } else {
 565                 options.files.add(new SearchFor(arg));
 566             }
 567         }
 568     }
 569 
 570     private static Option getOption(String name) throws BadArgs {
 571         for (Option o : recognizedOptions) {
 572             if (o.matches(name)) {
 573                 return o;
 574             }
 575         }
 576         throw new BadArgs("unknown option: {0}", name).showUsage(true);
 577     }
 578 
 579     public void printInfo(String message) {
 580         if (options.info) {
 581             log.print(message);
 582             log.flush();
 583         }
 584     }
 585 
 586     public void printlnInfo(String message) {
 587         if (options.info) {
 588             log.println(message);
 589             log.flush();
 590         }
 591     }
 592 
 593     public void printVerbose(String message) {
 594         if (options.verbose) {
 595             log.print(message);
 596             log.flush();
 597         }
 598     }
 599 
 600     public void printlnVerbose(String message) {
 601         if (options.verbose) {
 602             log.println(message);
 603             log.flush();
 604         }
 605     }
 606 
 607     public void printDebug(String message) {
 608         if (options.debug) {
 609             log.print(message);
 610             log.flush();
 611         }
 612     }
 613 
 614     public void printlnDebug(String message) {
 615         if (options.debug) {
 616             log.println(message);
 617             log.flush();
 618         }
 619     }
 620 
 621     public void printError(String message) {
 622         log.println("Error: " + message);
 623         log.flush();
 624     }
 625 
 626     private void reportError(Throwable e) {
 627         log.println("Error: " + e.getMessage());
 628         if (options.info) {
 629             e.printStackTrace(log);
 630         }
 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> list");
 650         log.println();
 651         log.println("  list       A : separated list of class names, modules, jar files");
 652         log.println("             or directories which contain class files.");
 653         log.println();
 654         log.println("where 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 }