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 }