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