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