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