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