1 /*
   2  * Copyright (c) 2013, 2015, 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 package org.graalvm.compiler.hotspot.test;
  24 
  25 import static java.util.Collections.singletonList;
  26 import static org.graalvm.compiler.core.CompilationWrapper.ExceptionAction.Print;
  27 import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationBailoutAction;
  28 import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationFailureAction;
  29 import static org.graalvm.compiler.core.test.ReflectionOptionDescriptors.extractEntries;
  30 import static org.graalvm.compiler.debug.MemUseTrackerKey.getCurrentThreadAllocatedBytes;
  31 import static org.graalvm.compiler.hotspot.test.CompileTheWorld.Options.DESCRIPTORS;
  32 import static org.graalvm.compiler.serviceprovider.JDK9Method.Java8OrEarlier;
  33 
  34 import java.io.Closeable;
  35 import java.io.File;
  36 import java.io.IOException;
  37 import java.lang.annotation.Annotation;
  38 import java.lang.reflect.Constructor;
  39 import java.lang.reflect.Method;
  40 import java.lang.reflect.Modifier;
  41 import java.net.URI;
  42 import java.net.URL;
  43 import java.net.URLClassLoader;
  44 import java.nio.file.FileSystem;
  45 import java.nio.file.FileSystems;
  46 import java.nio.file.FileVisitResult;
  47 import java.nio.file.Files;
  48 import java.nio.file.Path;
  49 import java.nio.file.SimpleFileVisitor;
  50 import java.nio.file.attribute.BasicFileAttributes;
  51 import java.util.ArrayList;
  52 import java.util.Arrays;
  53 import java.util.Collections;
  54 import java.util.Enumeration;
  55 import java.util.HashSet;
  56 import java.util.List;
  57 import java.util.ServiceLoader;
  58 import java.util.Set;
  59 import java.util.concurrent.ExecutionException;
  60 import java.util.concurrent.Future;
  61 import java.util.concurrent.LinkedBlockingQueue;
  62 import java.util.concurrent.ThreadPoolExecutor;
  63 import java.util.concurrent.TimeUnit;
  64 import java.util.concurrent.atomic.AtomicLong;
  65 import java.util.jar.JarEntry;
  66 import java.util.jar.JarFile;
  67 import java.util.stream.Collectors;
  68 
  69 import org.graalvm.compiler.api.replacements.Snippet;
  70 import org.graalvm.compiler.bytecode.Bytecodes;
  71 import org.graalvm.compiler.core.CompilerThreadFactory;
  72 import org.graalvm.compiler.core.test.ReflectionOptionDescriptors;
  73 import org.graalvm.compiler.debug.DebugOptions;
  74 import org.graalvm.compiler.debug.GraalError;
  75 import org.graalvm.compiler.debug.MethodFilter;
  76 import org.graalvm.compiler.debug.TTY;
  77 import org.graalvm.compiler.hotspot.CompilationTask;
  78 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
  79 import org.graalvm.compiler.hotspot.HotSpotGraalCompiler;
  80 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
  81 import org.graalvm.compiler.options.OptionDescriptors;
  82 import org.graalvm.compiler.options.OptionKey;
  83 import org.graalvm.compiler.options.OptionValues;
  84 import org.graalvm.compiler.options.OptionsParser;
  85 import org.graalvm.compiler.serviceprovider.JDK9Method;
  86 import org.graalvm.util.EconomicMap;
  87 import org.graalvm.util.UnmodifiableEconomicMap;
  88 
  89 import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider;
  90 import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
  91 import jdk.vm.ci.hotspot.HotSpotInstalledCode;
  92 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
  93 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider;
  94 import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
  95 import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
  96 import jdk.vm.ci.meta.ConstantPool;
  97 import jdk.vm.ci.meta.MetaAccessProvider;
  98 import jdk.vm.ci.runtime.JVMCI;
  99 import jdk.vm.ci.runtime.JVMCICompiler;
 100 
 101 /**
 102  * This class implements compile-the-world functionality with JVMCI.
 103  */
 104 public final class CompileTheWorld {
 105 
 106     /**
 107      * Magic token to denote that JDK classes are to be compiled. If
 108      * {@link JDK9Method#Java8OrEarlier}, then the classes in {@code rt.jar} are compiled. Otherwise
 109      * the classes in the Java runtime image are compiled.
 110      */
 111     public static final String SUN_BOOT_CLASS_PATH = "sun.boot.class.path";
 112 
 113     /**
 114      * Magic token to denote the classes in the Java runtime image (i.e. in the {@code jrt:/} file
 115      * system).
 116      */
 117     public static final String JRT_CLASS_PATH_ENTRY = "<jrt>";
 118 
 119     /**
 120      * @param options a space separated set of option value settings with each option setting in a
 121      *            {@code -Dgraal.<name>=<value>} format but without the leading {@code -Dgraal.}.
 122      *            Ignored if null.
 123      */
 124     public static EconomicMap<OptionKey<?>, Object> parseOptions(String options) {
 125         if (options != null) {
 126             EconomicMap<String, String> optionSettings = EconomicMap.create();
 127             for (String optionSetting : options.split("\\s+|#")) {
 128                 OptionsParser.parseOptionSettingTo(optionSetting, optionSettings);
 129             }
 130             EconomicMap<OptionKey<?>, Object> values = OptionValues.newOptionMap();
 131             ServiceLoader<OptionDescriptors> loader = ServiceLoader.load(OptionDescriptors.class, OptionDescriptors.class.getClassLoader());
 132             OptionsParser.parseOptions(optionSettings, values, loader);
 133             return values;
 134         }
 135         return EconomicMap.create();
 136     }
 137 
 138     private final HotSpotJVMCIRuntimeProvider jvmciRuntime;
 139 
 140     private final HotSpotGraalCompiler compiler;
 141 
 142     /**
 143      * Class path denoting classes to compile.
 144      *
 145      * @see Options#Classpath
 146      */
 147     private final String inputClassPath;
 148 
 149     /**
 150      * Class index to start compilation at.
 151      *
 152      * @see Options#StartAt
 153      */
 154     private final int startAt;
 155 
 156     /**
 157      * Class index to stop compilation at.
 158      *
 159      * @see Options#StopAt
 160      */
 161     private final int stopAt;
 162 
 163     /** Only compile methods matching one of the filters in this array if the array is non-null. */
 164     private final MethodFilter[] methodFilters;
 165 
 166     /** Exclude methods matching one of the filters in this array if the array is non-null. */
 167     private final MethodFilter[] excludeMethodFilters;
 168 
 169     // Counters
 170     private int classFileCounter = 0;
 171     private AtomicLong compiledMethodsCounter = new AtomicLong();
 172     private AtomicLong compileTime = new AtomicLong();
 173     private AtomicLong memoryUsed = new AtomicLong();
 174 
 175     private boolean verbose;
 176 
 177     /**
 178      * Signal that the threads should start compiling in multithreaded mode.
 179      */
 180     private boolean running;
 181 
 182     private ThreadPoolExecutor threadPool;
 183 
 184     private OptionValues currentOptions;
 185     private final UnmodifiableEconomicMap<OptionKey<?>, Object> compilationOptions;
 186 
 187     /**
 188      * Creates a compile-the-world instance.
 189      *
 190      * @param files {@link File#pathSeparator} separated list of Zip/Jar files to compile
 191      * @param startAt index of the class file to start compilation at
 192      * @param stopAt index of the class file to stop compilation at
 193      * @param methodFilters
 194      * @param excludeMethodFilters
 195      */
 196     public CompileTheWorld(HotSpotJVMCIRuntimeProvider jvmciRuntime, HotSpotGraalCompiler compiler, String files, int startAt, int stopAt, String methodFilters, String excludeMethodFilters,
 197                     boolean verbose, OptionValues initialOptions, EconomicMap<OptionKey<?>, Object> compilationOptions) {
 198         this.jvmciRuntime = jvmciRuntime;
 199         this.compiler = compiler;
 200         this.inputClassPath = files;
 201         this.startAt = startAt;
 202         this.stopAt = stopAt;
 203         this.methodFilters = methodFilters == null || methodFilters.isEmpty() ? null : MethodFilter.parse(methodFilters);
 204         this.excludeMethodFilters = excludeMethodFilters == null || excludeMethodFilters.isEmpty() ? null : MethodFilter.parse(excludeMethodFilters);
 205         this.verbose = verbose;
 206         this.currentOptions = initialOptions;
 207 
 208         // Copy the initial options and add in any extra options
 209         EconomicMap<OptionKey<?>, Object> compilationOptionsCopy = EconomicMap.create(initialOptions.getMap());
 210         compilationOptionsCopy.putAll(compilationOptions);
 211 
 212         // We want to see stack traces when a method fails to compile
 213         CompilationBailoutAction.putIfAbsent(compilationOptionsCopy, Print);
 214         CompilationFailureAction.putIfAbsent(compilationOptionsCopy, Print);
 215 
 216         // By default only report statistics for the CTW threads themselves
 217         DebugOptions.MetricsThreadFilter.putIfAbsent(compilationOptionsCopy, "^CompileTheWorld");
 218         this.compilationOptions = compilationOptionsCopy;
 219     }
 220 
 221     public CompileTheWorld(HotSpotJVMCIRuntimeProvider jvmciRuntime, HotSpotGraalCompiler compiler, OptionValues options) {
 222         this(jvmciRuntime, compiler, Options.Classpath.getValue(options),
 223                         Options.StartAt.getValue(options),
 224                         Options.StopAt.getValue(options),
 225                         Options.MethodFilter.getValue(options),
 226                         Options.ExcludeMethodFilter.getValue(options),
 227                         Options.Verbose.getValue(options),
 228                         options,
 229                         parseOptions(Options.Config.getValue(options)));
 230     }
 231 
 232     /**
 233      * Compiles all methods in all classes in {@link #inputClassPath}. If {@link #inputClassPath}
 234      * equals {@link #SUN_BOOT_CLASS_PATH} the boot classes are used.
 235      */
 236     public void compile() throws Throwable {
 237         if (SUN_BOOT_CLASS_PATH.equals(inputClassPath)) {
 238             String bcpEntry = null;
 239             if (Java8OrEarlier) {
 240                 final String[] entries = System.getProperty(SUN_BOOT_CLASS_PATH).split(File.pathSeparator);
 241                 for (int i = 0; i < entries.length && bcpEntry == null; i++) {
 242                     String entry = entries[i];
 243                     File entryFile = new File(entry);
 244                     if (entryFile.getName().endsWith("rt.jar") && entryFile.isFile()) {
 245                         bcpEntry = entry;
 246                     }
 247                 }
 248                 if (bcpEntry == null) {
 249                     throw new GraalError("Could not find rt.jar on boot class path %s", System.getProperty(SUN_BOOT_CLASS_PATH));
 250                 }
 251             } else {
 252                 bcpEntry = JRT_CLASS_PATH_ENTRY;
 253             }
 254             compile(bcpEntry);
 255         } else {
 256             compile(inputClassPath);
 257         }
 258     }
 259 
 260     public void println() {
 261         println("");
 262     }
 263 
 264     public void println(String format, Object... args) {
 265         println(String.format(format, args));
 266     }
 267 
 268     public void println(String s) {
 269         println(verbose, s);
 270     }
 271 
 272     public static void println(boolean cond, String s) {
 273         if (cond) {
 274             TTY.println(s);
 275         }
 276     }
 277 
 278     public void printStackTrace(Throwable t) {
 279         if (verbose) {
 280             t.printStackTrace(TTY.out);
 281         }
 282     }
 283 
 284     @SuppressWarnings("unused")
 285     private static void dummy() {
 286     }
 287 
 288     /**
 289      * Abstraction over different types of class path entries.
 290      */
 291     abstract static class ClassPathEntry implements Closeable {
 292         final String name;
 293 
 294         ClassPathEntry(String name) {
 295             this.name = name;
 296         }
 297 
 298         /**
 299          * Creates a {@link ClassLoader} for loading classes from this entry.
 300          */
 301         public abstract ClassLoader createClassLoader() throws IOException;
 302 
 303         /**
 304          * Gets the list of classes available under this entry.
 305          */
 306         public abstract List<String> getClassNames() throws IOException;
 307 
 308         @Override
 309         public String toString() {
 310             return name;
 311         }
 312 
 313         @Override
 314         public void close() throws IOException {
 315         }
 316     }
 317 
 318     /**
 319      * A class path entry that is a normal file system directory.
 320      */
 321     static class DirClassPathEntry extends ClassPathEntry {
 322 
 323         private final File dir;
 324 
 325         DirClassPathEntry(String name) {
 326             super(name);
 327             dir = new File(name);
 328             assert dir.isDirectory();
 329         }
 330 
 331         @Override
 332         public ClassLoader createClassLoader() throws IOException {
 333             URL url = dir.toURI().toURL();
 334             return new URLClassLoader(new URL[]{url});
 335         }
 336 
 337         @Override
 338         public List<String> getClassNames() throws IOException {
 339             List<String> classNames = new ArrayList<>();
 340             String root = dir.getPath();
 341             SimpleFileVisitor<Path> visitor = new SimpleFileVisitor<Path>() {
 342                 @Override
 343                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
 344                     if (attrs.isRegularFile()) {
 345                         File path = file.toFile();
 346                         if (path.getName().endsWith(".class")) {
 347                             String pathString = path.getPath();
 348                             assert pathString.startsWith(root);
 349                             String classFile = pathString.substring(root.length() + 1);
 350                             String className = classFile.replace(File.separatorChar, '.');
 351                             classNames.add(className.replace('/', '.').substring(0, className.length() - ".class".length()));
 352                         }
 353                     }
 354                     return super.visitFile(file, attrs);
 355                 }
 356             };
 357             Files.walkFileTree(dir.toPath(), visitor);
 358             return classNames;
 359         }
 360     }
 361 
 362     /**
 363      * A class path entry that is a jar or zip file.
 364      */
 365     static class JarClassPathEntry extends ClassPathEntry {
 366 
 367         private final JarFile jarFile;
 368 
 369         JarClassPathEntry(String name) throws IOException {
 370             super(name);
 371             jarFile = new JarFile(name);
 372         }
 373 
 374         @Override
 375         public ClassLoader createClassLoader() throws IOException {
 376             URL url = new URL("jar", "", "file:" + name + "!/");
 377             return new URLClassLoader(new URL[]{url});
 378         }
 379 
 380         @Override
 381         public List<String> getClassNames() throws IOException {
 382             Enumeration<JarEntry> e = jarFile.entries();
 383             List<String> classNames = new ArrayList<>(jarFile.size());
 384             while (e.hasMoreElements()) {
 385                 JarEntry je = e.nextElement();
 386                 if (je.isDirectory() || !je.getName().endsWith(".class")) {
 387                     continue;
 388                 }
 389                 String className = je.getName().substring(0, je.getName().length() - ".class".length());
 390                 classNames.add(className.replace('/', '.'));
 391             }
 392             return classNames;
 393         }
 394 
 395         @Override
 396         public void close() throws IOException {
 397             jarFile.close();
 398         }
 399     }
 400 
 401     /**
 402      * A class path entry representing the {@code jrt:/} file system.
 403      */
 404     static class JRTClassPathEntry extends ClassPathEntry {
 405 
 406         private final String limitModules;
 407 
 408         JRTClassPathEntry(String name, String limitModules) {
 409             super(name);
 410             this.limitModules = limitModules;
 411         }
 412 
 413         @Override
 414         public ClassLoader createClassLoader() throws IOException {
 415             URL url = URI.create("jrt:/").toURL();
 416             return new URLClassLoader(new URL[]{url});
 417         }
 418 
 419         @Override
 420         public List<String> getClassNames() throws IOException {
 421             Set<String> negative = new HashSet<>();
 422             Set<String> positive = new HashSet<>();
 423             if (limitModules != null && !limitModules.isEmpty()) {
 424                 for (String s : limitModules.split(",")) {
 425                     if (s.startsWith("~")) {
 426                         negative.add(s.substring(1));
 427                     } else {
 428                         positive.add(s);
 429                     }
 430                 }
 431             }
 432             List<String> classNames = new ArrayList<>();
 433             FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Collections.emptyMap());
 434             Path top = fs.getPath("/modules/");
 435             Files.find(top, Integer.MAX_VALUE,
 436                             (path, attrs) -> attrs.isRegularFile()).forEach(p -> {
 437                                 int nameCount = p.getNameCount();
 438                                 if (nameCount > 2) {
 439                                     String base = p.getName(nameCount - 1).toString();
 440                                     if (base.endsWith(".class") && !base.equals("module-info.class")) {
 441                                         String module = p.getName(1).toString();
 442                                         if (positive.isEmpty() || positive.contains(module)) {
 443                                             if (negative.isEmpty() || !negative.contains(module)) {
 444                                                 // Strip module prefix and convert to dotted form
 445                                                 String className = p.subpath(2, nameCount).toString().replace('/', '.');
 446                                                 // Strip ".class" suffix
 447                                                 className = className.replace('/', '.').substring(0, className.length() - ".class".length());
 448                                                 classNames.add(className);
 449                                             }
 450                                         }
 451                                     }
 452                                 }
 453                             });
 454             return classNames;
 455         }
 456     }
 457 
 458     private boolean isClassIncluded(String className) {
 459         if (methodFilters != null && !MethodFilter.matchesClassName(methodFilters, className)) {
 460             return false;
 461         }
 462         if (excludeMethodFilters != null && MethodFilter.matchesClassName(excludeMethodFilters, className)) {
 463             return false;
 464         }
 465         return true;
 466     }
 467 
 468     /**
 469      * Compiles all methods in all classes in a given class path.
 470      *
 471      * @param classPath class path denoting classes to compile
 472      * @throws IOException
 473      */
 474     @SuppressWarnings("try")
 475     private void compile(String classPath) throws IOException {
 476         final String[] entries = classPath.split(File.pathSeparator);
 477         long start = System.currentTimeMillis();
 478 
 479         try {
 480             // compile dummy method to get compiler initialized outside of the
 481             // config debug override.
 482             HotSpotResolvedJavaMethod dummyMethod = (HotSpotResolvedJavaMethod) JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess().lookupJavaMethod(
 483                             CompileTheWorld.class.getDeclaredMethod("dummy"));
 484             int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI;
 485             boolean useProfilingInfo = false;
 486             boolean installAsDefault = false;
 487             CompilationTask task = new CompilationTask(jvmciRuntime, compiler, new HotSpotCompilationRequest(dummyMethod, entryBCI, 0L), useProfilingInfo, installAsDefault, currentOptions);
 488             task.runCompilation();
 489         } catch (NoSuchMethodException | SecurityException e1) {
 490             printStackTrace(e1);
 491         }
 492 
 493         /*
 494          * Always use a thread pool, even for single threaded mode since it simplifies the use of
 495          * DebugValueThreadFilter to filter on the thread names.
 496          */
 497         int threadCount = 1;
 498         if (Options.MultiThreaded.getValue(currentOptions)) {
 499             threadCount = Options.Threads.getValue(currentOptions);
 500             if (threadCount == 0) {
 501                 threadCount = Runtime.getRuntime().availableProcessors();
 502             }
 503         } else {
 504             running = true;
 505         }
 506 
 507         OptionValues savedOptions = currentOptions;
 508         currentOptions = new OptionValues(compilationOptions);
 509         threadPool = new ThreadPoolExecutor(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new CompilerThreadFactory("CompileTheWorld"));
 510 
 511         try {
 512             for (int i = 0; i < entries.length; i++) {
 513                 final String entry = entries[i];
 514 
 515                 ClassPathEntry cpe;
 516                 if (entry.endsWith(".zip") || entry.endsWith(".jar")) {
 517                     cpe = new JarClassPathEntry(entry);
 518                 } else if (entry.equals(JRT_CLASS_PATH_ENTRY)) {
 519                     cpe = new JRTClassPathEntry(entry, Options.LimitModules.getValue(currentOptions));
 520                 } else {
 521                     if (!new File(entry).isDirectory()) {
 522                         println("CompileTheWorld : Skipped classes in " + entry);
 523                         println();
 524                         continue;
 525                     }
 526                     cpe = new DirClassPathEntry(entry);
 527                 }
 528 
 529                 if (methodFilters == null || methodFilters.length == 0) {
 530                     println("CompileTheWorld : Compiling all classes in " + entry);
 531                 } else {
 532                     String include = Arrays.asList(methodFilters).stream().map(MethodFilter::toString).collect(Collectors.joining(", "));
 533                     println("CompileTheWorld : Compiling all methods in " + entry + " matching one of the following filters: " + include);
 534                 }
 535                 if (excludeMethodFilters != null && excludeMethodFilters.length > 0) {
 536                     String exclude = Arrays.asList(excludeMethodFilters).stream().map(MethodFilter::toString).collect(Collectors.joining(", "));
 537                     println("CompileTheWorld : Excluding all methods matching one of the following filters: " + exclude);
 538                 }
 539                 println();
 540 
 541                 ClassLoader loader = cpe.createClassLoader();
 542 
 543                 for (String className : cpe.getClassNames()) {
 544 
 545                     // Are we done?
 546                     if (classFileCounter >= stopAt) {
 547                         break;
 548                     }
 549 
 550                     classFileCounter++;
 551 
 552                     if (className.startsWith("jdk.management.") || className.startsWith("jdk.internal.cmm.*")) {
 553                         continue;
 554                     }
 555 
 556                     try {
 557                         // Load and initialize class
 558                         Class<?> javaClass = Class.forName(className, true, loader);
 559 
 560                         // Pre-load all classes in the constant pool.
 561                         try {
 562                             HotSpotResolvedObjectType objectType = HotSpotResolvedObjectType.fromObjectClass(javaClass);
 563                             ConstantPool constantPool = objectType.getConstantPool();
 564                             for (int cpi = 1; cpi < constantPool.length(); cpi++) {
 565                                 constantPool.loadReferencedType(cpi, Bytecodes.LDC);
 566                             }
 567                         } catch (Throwable t) {
 568                             // If something went wrong during pre-loading we just ignore it.
 569                             if (isClassIncluded(className)) {
 570                                 println("Preloading failed for (%d) %s: %s", classFileCounter, className, t);
 571                             }
 572                             continue;
 573                         }
 574 
 575                         /*
 576                          * Only check filters after class loading and resolution to mitigate impact
 577                          * on reproducibility.
 578                          */
 579                         if (!isClassIncluded(className)) {
 580                             continue;
 581                         }
 582 
 583                         // Are we compiling this class?
 584                         MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess();
 585                         if (classFileCounter >= startAt) {
 586                             println("CompileTheWorld (%d) : %s", classFileCounter, className);
 587 
 588                             // Compile each constructor/method in the class.
 589                             for (Constructor<?> constructor : javaClass.getDeclaredConstructors()) {
 590                                 HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(constructor);
 591                                 if (canBeCompiled(javaMethod, constructor.getModifiers())) {
 592                                     compileMethod(javaMethod);
 593                                 }
 594                             }
 595                             for (Method method : javaClass.getDeclaredMethods()) {
 596                                 HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(method);
 597                                 if (canBeCompiled(javaMethod, method.getModifiers())) {
 598                                     compileMethod(javaMethod);
 599                                 }
 600                             }
 601 
 602                             // Also compile the class initializer if it exists
 603                             HotSpotResolvedJavaMethod clinit = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaType(javaClass).getClassInitializer();
 604                             if (clinit != null && canBeCompiled(clinit, clinit.getModifiers())) {
 605                                 compileMethod(clinit);
 606                             }
 607                         }
 608                     } catch (Throwable t) {
 609                         if (isClassIncluded(className)) {
 610                             println("CompileTheWorld (%d) : Skipping %s %s", classFileCounter, className, t.toString());
 611                             printStackTrace(t);
 612                         }
 613                     }
 614                 }
 615                 cpe.close();
 616             }
 617         } finally {
 618             currentOptions = savedOptions;
 619         }
 620 
 621         if (!running) {
 622             startThreads();
 623         }
 624         int wakeups = 0;
 625         while (threadPool.getCompletedTaskCount() != threadPool.getTaskCount()) {
 626             if (wakeups % 15 == 0) {
 627                 TTY.println("CompileTheWorld : Waiting for " + (threadPool.getTaskCount() - threadPool.getCompletedTaskCount()) + " compiles");
 628             }
 629             try {
 630                 threadPool.awaitTermination(1, TimeUnit.SECONDS);
 631                 wakeups++;
 632             } catch (InterruptedException e) {
 633             }
 634         }
 635         threadPool = null;
 636 
 637         long elapsedTime = System.currentTimeMillis() - start;
 638 
 639         println();
 640         if (Options.MultiThreaded.getValue(currentOptions)) {
 641             TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms elapsed, %d ms compile time, %d bytes of memory used)", classFileCounter, compiledMethodsCounter.get(), elapsedTime,
 642                             compileTime.get(), memoryUsed.get());
 643         } else {
 644             TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms, %d bytes of memory used)", classFileCounter, compiledMethodsCounter.get(), compileTime.get(), memoryUsed.get());
 645         }
 646     }
 647 
 648     private synchronized void startThreads() {
 649         running = true;
 650         // Wake up any waiting threads
 651         notifyAll();
 652     }
 653 
 654     private synchronized void waitToRun() {
 655         while (!running) {
 656             try {
 657                 wait();
 658             } catch (InterruptedException e) {
 659             }
 660         }
 661     }
 662 
 663     @SuppressWarnings("try")
 664     private void compileMethod(HotSpotResolvedJavaMethod method) throws InterruptedException, ExecutionException {
 665         if (methodFilters != null && !MethodFilter.matches(methodFilters, method)) {
 666             return;
 667         }
 668         if (excludeMethodFilters != null && MethodFilter.matches(excludeMethodFilters, method)) {
 669             return;
 670         }
 671         Future<?> task = threadPool.submit(new Runnable() {
 672             @Override
 673             public void run() {
 674                 waitToRun();
 675                 OptionValues savedOptions = currentOptions;
 676                 currentOptions = new OptionValues(compilationOptions);
 677                 try {
 678                     compileMethod(method, classFileCounter);
 679                 } finally {
 680                     currentOptions = savedOptions;
 681                 }
 682             }
 683         });
 684         if (threadPool.getCorePoolSize() == 1) {
 685             task.get();
 686         }
 687     }
 688 
 689     /**
 690      * Compiles a method and gathers some statistics.
 691      */
 692     private void compileMethod(HotSpotResolvedJavaMethod method, int counter) {
 693         try {
 694             long start = System.currentTimeMillis();
 695             long allocatedAtStart = getCurrentThreadAllocatedBytes();
 696             int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI;
 697             HotSpotCompilationRequest request = new HotSpotCompilationRequest(method, entryBCI, 0L);
 698             // For more stable CTW execution, disable use of profiling information
 699             boolean useProfilingInfo = false;
 700             boolean installAsDefault = false;
 701             CompilationTask task = new CompilationTask(jvmciRuntime, compiler, request, useProfilingInfo, installAsDefault, currentOptions);
 702             task.runCompilation();
 703 
 704             // Invalidate the generated code so the code cache doesn't fill up
 705             HotSpotInstalledCode installedCode = task.getInstalledCode();
 706             if (installedCode != null) {
 707                 installedCode.invalidate();
 708             }
 709 
 710             memoryUsed.getAndAdd(getCurrentThreadAllocatedBytes() - allocatedAtStart);
 711             compileTime.getAndAdd(System.currentTimeMillis() - start);
 712             compiledMethodsCounter.incrementAndGet();
 713         } catch (Throwable t) {
 714             // Catch everything and print a message
 715             println("CompileTheWorld (%d) : Error compiling method: %s", counter, method.format("%H.%n(%p):%r"));
 716             printStackTrace(t);
 717         }
 718     }
 719 
 720     /**
 721      * Determines if a method should be compiled (Cf. CompilationPolicy::can_be_compiled).
 722      *
 723      * @return true if it can be compiled, false otherwise
 724      */
 725     private boolean canBeCompiled(HotSpotResolvedJavaMethod javaMethod, int modifiers) {
 726         if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) {
 727             return false;
 728         }
 729         GraalHotSpotVMConfig c = compiler.getGraalRuntime().getVMConfig();
 730         if (c.dontCompileHugeMethods && javaMethod.getCodeSize() > c.hugeMethodLimit) {
 731             println(verbose || methodFilters != null,
 732                             String.format("CompileTheWorld (%d) : Skipping huge method %s (use -XX:-DontCompileHugeMethods or -XX:HugeMethodLimit=%d to include it)", classFileCounter,
 733                                             javaMethod.format("%H.%n(%p):%r"),
 734                                             javaMethod.getCodeSize()));
 735             return false;
 736         }
 737         // Allow use of -XX:CompileCommand=dontinline to exclude problematic methods
 738         if (!javaMethod.canBeInlined()) {
 739             return false;
 740         }
 741         // Skip @Snippets for now
 742         for (Annotation annotation : javaMethod.getAnnotations()) {
 743             if (annotation.annotationType().equals(Snippet.class)) {
 744                 return false;
 745             }
 746         }
 747         return true;
 748     }
 749 
 750     static class Options {
 751         // @formatter:off
 752         public static final OptionKey<Boolean> Help = new OptionKey<>(false);
 753         public static final OptionKey<String> Classpath = new OptionKey<>(CompileTheWorld.SUN_BOOT_CLASS_PATH);
 754         public static final OptionKey<Boolean> Verbose = new OptionKey<>(true);
 755         /**
 756          * Ignore Graal classes by default to avoid problems associated with compiling
 757          * snippets and method substitutions.
 758          */
 759         public static final OptionKey<String> LimitModules = new OptionKey<>("~jdk.internal.vm.compiler");
 760         public static final OptionKey<Integer> Iterations = new OptionKey<>(1);
 761         public static final OptionKey<String> MethodFilter = new OptionKey<>(null);
 762         public static final OptionKey<String> ExcludeMethodFilter = new OptionKey<>(null);
 763         public static final OptionKey<Integer> StartAt = new OptionKey<>(1);
 764         public static final OptionKey<Integer> StopAt = new OptionKey<>(Integer.MAX_VALUE);
 765         public static final OptionKey<String> Config = new OptionKey<>(null);
 766         public static final OptionKey<Boolean> MultiThreaded = new OptionKey<>(false);
 767         public static final OptionKey<Integer> Threads = new OptionKey<>(0);
 768 
 769         static final ReflectionOptionDescriptors DESCRIPTORS = new ReflectionOptionDescriptors(Options.class,
 770                            "Help", "List options and their help messages and then exit.",
 771                       "Classpath", "Class path denoting methods to compile. Default is to compile boot classes.",
 772                         "Verbose", "Verbose operation.",
 773                    "LimitModules", "Comma separated list of module names to which compilation should be limited. " +
 774                                    "Module names can be prefixed with \"~\" to exclude the named module.",
 775                      "Iterations", "The number of iterations to perform.",
 776                    "MethodFilter", "Only compile methods matching this filter.",
 777             "ExcludeMethodFilter", "Exclude methods matching this filter from compilation.",
 778                         "StartAt", "First class to consider for compilation.",
 779                          "StopAt", "Last class to consider for compilation.",
 780                          "Config", "Option value overrides to use during compile the world. For example, " +
 781                                    "to disable inlining and partial escape analysis specify 'PartialEscapeAnalysis=false Inline=false'. " +
 782                                    "The format for each option is the same as on the command line just without the '-Dgraal.' prefix.",
 783                   "MultiThreaded", "Run using multiple threads for compilation.",
 784                         "Threads", "Number of threads to use for multithreaded execution. Defaults to Runtime.getRuntime().availableProcessors().");
 785         // @formatter:on
 786     }
 787 
 788     public static OptionValues loadOptions(OptionValues initialValues) {
 789         EconomicMap<OptionKey<?>, Object> values = OptionValues.newOptionMap();
 790         List<OptionDescriptors> loader = singletonList(DESCRIPTORS);
 791         OptionsParser.parseOptions(extractEntries(System.getProperties(), "CompileTheWorld.", true), values, loader);
 792         OptionValues options = new OptionValues(initialValues, values);
 793         if (Options.Help.getValue(options)) {
 794             options.printHelp(loader, System.out, "CompileTheWorld.");
 795             System.exit(0);
 796         }
 797         return options;
 798     }
 799 
 800     public static void main(String[] args) throws Throwable {
 801         HotSpotJVMCIRuntime jvmciRuntime = HotSpotJVMCIRuntime.runtime();
 802         HotSpotGraalCompiler compiler = (HotSpotGraalCompiler) jvmciRuntime.getCompiler();
 803         HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime();
 804         HotSpotCodeCacheProvider codeCache = graalRuntime.getHostProviders().getCodeCache();
 805         OptionValues options = loadOptions(graalRuntime.getOptions());
 806 
 807         int iterations = Options.Iterations.getValue(options);
 808         for (int i = 0; i < iterations; i++) {
 809             codeCache.resetCompilationStatistics();
 810             TTY.println("CompileTheWorld : iteration " + i);
 811 
 812             CompileTheWorld ctw = new CompileTheWorld(jvmciRuntime, compiler, options);
 813             ctw.compile();
 814         }
 815         // This is required as non-daemon threads can be started by class initializers
 816         System.exit(0);
 817     }
 818 }