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 }