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