CompileTheWorld.java revision 13304:5e9c41536bd2
1266692Sgshapiro/* 2132943Sgshapiro * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. 3132943Sgshapiro * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4132943Sgshapiro * 5132943Sgshapiro * This code is free software; you can redistribute it and/or modify it 6132943Sgshapiro * under the terms of the GNU General Public License version 2 only, as 7132943Sgshapiro * published by the Free Software Foundation. 8132943Sgshapiro * 9132943Sgshapiro * This code is distributed in the hope that it will be useful, but WITHOUT 10132943Sgshapiro * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11132943Sgshapiro * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12173340Sgshapiro * version 2 for more details (a copy is included in the LICENSE file that 13285303Sgshapiro * accompanied this code). 14285303Sgshapiro * 15285303Sgshapiro * You should have received a copy of the GNU General Public License version 16285303Sgshapiro * 2 along with this work; if not, write to the Free Software Foundation, 17285303Sgshapiro * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18285303Sgshapiro * 19285303Sgshapiro * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20285303Sgshapiro * or visit www.oracle.com if you need additional information or have any 21285303Sgshapiro * questions. 22285303Sgshapiro */ 23285303Sgshapiropackage org.graalvm.compiler.hotspot.test; 24285303Sgshapiro 25285303Sgshapiroimport static java.util.Collections.singletonList; 26285303Sgshapiroimport static org.graalvm.compiler.core.CompilationWrapper.ExceptionAction.Print; 27285303Sgshapiroimport static org.graalvm.compiler.core.GraalCompilerOptions.CompilationBailoutAction; 28285303Sgshapiroimport static org.graalvm.compiler.core.GraalCompilerOptions.CompilationFailureAction; 29285303Sgshapiroimport static org.graalvm.compiler.core.test.ReflectionOptionDescriptors.extractEntries; 30285303Sgshapiroimport static org.graalvm.compiler.debug.MemUseTrackerKey.getCurrentThreadAllocatedBytes; 31285303Sgshapiroimport static org.graalvm.compiler.hotspot.test.CompileTheWorld.Options.DESCRIPTORS; 32285303Sgshapiroimport static org.graalvm.compiler.serviceprovider.JDK9Method.Java8OrEarlier; 33285303Sgshapiro 34285303Sgshapiroimport java.io.Closeable; 35285303Sgshapiroimport java.io.File; 36285303Sgshapiroimport java.io.IOException; 37285303Sgshapiroimport java.lang.annotation.Annotation; 38285303Sgshapiroimport java.lang.reflect.Constructor; 39285303Sgshapiroimport java.lang.reflect.Method; 40285303Sgshapiroimport java.lang.reflect.Modifier; 41285303Sgshapiroimport java.net.URI; 42285303Sgshapiroimport java.net.URL; 43285303Sgshapiroimport java.net.URLClassLoader; 44285303Sgshapiroimport java.nio.file.FileSystem; 45285303Sgshapiroimport java.nio.file.FileSystems; 46285303Sgshapiroimport java.nio.file.FileVisitResult; 47285303Sgshapiroimport java.nio.file.Files; 48285303Sgshapiroimport java.nio.file.Path; 49285303Sgshapiroimport java.nio.file.SimpleFileVisitor; 50285303Sgshapiroimport java.nio.file.attribute.BasicFileAttributes; 51285303Sgshapiroimport java.util.ArrayList; 52285303Sgshapiroimport java.util.Arrays; 53285303Sgshapiroimport java.util.Collections; 54285303Sgshapiroimport java.util.Enumeration; 55285303Sgshapiroimport java.util.HashSet; 56285303Sgshapiroimport java.util.List; 57285303Sgshapiroimport java.util.ServiceLoader; 58285303Sgshapiroimport java.util.Set; 59285303Sgshapiroimport java.util.concurrent.ExecutionException; 60285303Sgshapiroimport java.util.concurrent.Future; 61285303Sgshapiroimport java.util.concurrent.LinkedBlockingQueue; 62285303Sgshapiroimport java.util.concurrent.ThreadPoolExecutor; 63285303Sgshapiroimport java.util.concurrent.TimeUnit; 64285303Sgshapiroimport java.util.concurrent.atomic.AtomicLong; 65285303Sgshapiroimport java.util.jar.JarEntry; 66285303Sgshapiroimport java.util.jar.JarFile; 67285303Sgshapiroimport java.util.stream.Collectors; 68285303Sgshapiro 69285303Sgshapiroimport org.graalvm.compiler.api.replacements.Snippet; 70285303Sgshapiroimport org.graalvm.compiler.bytecode.Bytecodes; 71285303Sgshapiroimport org.graalvm.compiler.core.CompilerThreadFactory; 72285303Sgshapiroimport org.graalvm.compiler.core.test.ReflectionOptionDescriptors; 73285303Sgshapiroimport org.graalvm.compiler.debug.DebugOptions; 74285303Sgshapiroimport org.graalvm.compiler.debug.GraalError; 75285303Sgshapiroimport org.graalvm.compiler.debug.MethodFilter; 76285303Sgshapiroimport org.graalvm.compiler.debug.TTY; 77285303Sgshapiroimport org.graalvm.compiler.hotspot.CompilationTask; 78285303Sgshapiroimport org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; 79285303Sgshapiroimport org.graalvm.compiler.hotspot.HotSpotGraalCompiler; 80285303Sgshapiroimport org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider; 81285303Sgshapiroimport org.graalvm.compiler.options.OptionDescriptors; 82285303Sgshapiroimport org.graalvm.compiler.options.OptionKey; 83285303Sgshapiroimport org.graalvm.compiler.options.OptionValues; 84285303Sgshapiroimport org.graalvm.compiler.options.OptionsParser; 85285303Sgshapiroimport org.graalvm.compiler.serviceprovider.JDK9Method; 86285303Sgshapiroimport org.graalvm.util.EconomicMap; 87285303Sgshapiroimport org.graalvm.util.UnmodifiableEconomicMap; 88285303Sgshapiro 89285303Sgshapiroimport jdk.vm.ci.hotspot.HotSpotCodeCacheProvider; 90285303Sgshapiroimport jdk.vm.ci.hotspot.HotSpotCompilationRequest; 91285303Sgshapiroimport jdk.vm.ci.hotspot.HotSpotInstalledCode; 92285303Sgshapiroimport jdk.vm.ci.hotspot.HotSpotJVMCIRuntime; 93285303Sgshapiroimport jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider; 94285303Sgshapiroimport jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; 95285303Sgshapiroimport jdk.vm.ci.hotspot.HotSpotResolvedObjectType; 96285303Sgshapiroimport jdk.vm.ci.meta.ConstantPool; 97285303Sgshapiroimport jdk.vm.ci.meta.MetaAccessProvider; 98285303Sgshapiroimport jdk.vm.ci.runtime.JVMCI; 99285303Sgshapiroimport jdk.vm.ci.runtime.JVMCICompiler; 100285303Sgshapiro 101285303Sgshapiro/** 102285303Sgshapiro * This class implements compile-the-world functionality with JVMCI. 103285303Sgshapiro */ 104285303Sgshapiropublic final class CompileTheWorld { 105285303Sgshapiro 106285303Sgshapiro /** 107285303Sgshapiro * Magic token to denote that JDK classes are to be compiled. If 108285303Sgshapiro * {@link JDK9Method#Java8OrEarlier}, then the classes in {@code rt.jar} are compiled. Otherwise 109249729Sgshapiro * the classes in the Java runtime image are compiled. 110249729Sgshapiro */ 111249729Sgshapiro public static final String SUN_BOOT_CLASS_PATH = "sun.boot.class.path"; 112132943Sgshapiro 113249729Sgshapiro /** 114249729Sgshapiro * Magic token to denote the classes in the Java runtime image (i.e. in the {@code jrt:/} file 115249729Sgshapiro * system). 116132943Sgshapiro */ 117132943Sgshapiro public static final String JRT_CLASS_PATH_ENTRY = "<jrt>"; 118249729Sgshapiro 119249729Sgshapiro /** 120249729Sgshapiro * @param options a space separated set of option value settings with each option setting in a 121249729Sgshapiro * {@code -Dgraal.<name>=<value>} format but without the leading {@code -Dgraal.}. 122249729Sgshapiro * Ignored if null. 123249729Sgshapiro */ 124249729Sgshapiro public static EconomicMap<OptionKey<?>, Object> parseOptions(String options) { 125249729Sgshapiro if (options != null) { 126249729Sgshapiro EconomicMap<String, String> optionSettings = EconomicMap.create(); 127249729Sgshapiro for (String optionSetting : options.split("\\s+|#")) { 128249729Sgshapiro OptionsParser.parseOptionSettingTo(optionSetting, optionSettings); 129249729Sgshapiro } 130249729Sgshapiro EconomicMap<OptionKey<?>, Object> values = OptionValues.newOptionMap(); 131249729Sgshapiro ServiceLoader<OptionDescriptors> loader = ServiceLoader.load(OptionDescriptors.class, OptionDescriptors.class.getClassLoader()); 132249729Sgshapiro OptionsParser.parseOptions(optionSettings, values, loader); 133249729Sgshapiro return values; 134249729Sgshapiro } 135249729Sgshapiro return EconomicMap.create(); 136249729Sgshapiro } 137249729Sgshapiro 138132943Sgshapiro private final HotSpotJVMCIRuntimeProvider jvmciRuntime; 139132943Sgshapiro 140132943Sgshapiro private final HotSpotGraalCompiler compiler; 141249729Sgshapiro 142132943Sgshapiro /** 143249729Sgshapiro * Class path denoting classes to compile. 144249729Sgshapiro * 145249729Sgshapiro * @see Options#Classpath 146132943Sgshapiro */ 147132943Sgshapiro private final String inputClassPath; 148249729Sgshapiro 149132943Sgshapiro /** 150249729Sgshapiro * Class index to start compilation at. 151132943Sgshapiro * 152249729Sgshapiro * @see Options#StartAt 153249729Sgshapiro */ 154249729Sgshapiro private final int startAt; 155249729Sgshapiro 156249729Sgshapiro /** 157249729Sgshapiro * Class index to stop compilation at. 158249729Sgshapiro * 159249729Sgshapiro * @see Options#StopAt 160249729Sgshapiro */ 161249729Sgshapiro private final int stopAt; 162249729Sgshapiro 163249729Sgshapiro /** Only compile methods matching one of the filters in this array if the array is non-null. */ 164249729Sgshapiro private final MethodFilter[] methodFilters; 165249729Sgshapiro 166249729Sgshapiro /** Exclude methods matching one of the filters in this array if the array is non-null. */ 167249729Sgshapiro private final MethodFilter[] excludeMethodFilters; 168249729Sgshapiro 169132943Sgshapiro // Counters 170249729Sgshapiro private int classFileCounter = 0; 171132943Sgshapiro private AtomicLong compiledMethodsCounter = new AtomicLong(); 172132943Sgshapiro private AtomicLong compileTime = new AtomicLong(); 173249729Sgshapiro private AtomicLong memoryUsed = new AtomicLong(); 174249729Sgshapiro 175173340Sgshapiro private boolean verbose; 176173340Sgshapiro 177249729Sgshapiro /** 178249729Sgshapiro * Signal that the threads should start compiling in multithreaded mode. 179249729Sgshapiro */ 180249729Sgshapiro private boolean running; 181249729Sgshapiro 182249729Sgshapiro private ThreadPoolExecutor threadPool; 183249729Sgshapiro 184249729Sgshapiro private OptionValues currentOptions; 185249729Sgshapiro private final UnmodifiableEconomicMap<OptionKey<?>, Object> compilationOptions; 186249729Sgshapiro 187173340Sgshapiro /** 188249729Sgshapiro * Creates a compile-the-world instance. 189249729Sgshapiro * 190249729Sgshapiro * @param files {@link File#pathSeparator} separated list of Zip/Jar files to compile 191249729Sgshapiro * @param startAt index of the class file to start compilation at 192249729Sgshapiro * @param stopAt index of the class file to stop compilation at 193249729Sgshapiro * @param methodFilters 194249729Sgshapiro * @param excludeMethodFilters 195249729Sgshapiro */ 196249729Sgshapiro public CompileTheWorld(HotSpotJVMCIRuntimeProvider jvmciRuntime, HotSpotGraalCompiler compiler, String files, int startAt, int stopAt, String methodFilters, String excludeMethodFilters, 197249729Sgshapiro boolean verbose, OptionValues initialOptions, EconomicMap<OptionKey<?>, Object> compilationOptions) { 198132943Sgshapiro this.jvmciRuntime = jvmciRuntime; 199 this.compiler = compiler; 200 this.inputClassPath = files; 201 this.startAt = startAt; 202 this.stopAt = stopAt; 203 this.methodFilters = methodFilters == null || methodFilters.isEmpty() ? null : MethodFilter.parse(methodFilters); 204 this.excludeMethodFilters = excludeMethodFilters == null || excludeMethodFilters.isEmpty() ? null : MethodFilter.parse(excludeMethodFilters); 205 this.verbose = verbose; 206 this.currentOptions = initialOptions; 207 208 // Copy the initial options and add in any extra options 209 EconomicMap<OptionKey<?>, Object> compilationOptionsCopy = EconomicMap.create(initialOptions.getMap()); 210 compilationOptionsCopy.putAll(compilationOptions); 211 212 // We want to see stack traces when a method fails to compile 213 CompilationBailoutAction.putIfAbsent(compilationOptionsCopy, Print); 214 CompilationFailureAction.putIfAbsent(compilationOptionsCopy, Print); 215 216 // By default only report statistics for the CTW threads themselves 217 DebugOptions.MetricsThreadFilter.putIfAbsent(compilationOptionsCopy, "^CompileTheWorld"); 218 this.compilationOptions = compilationOptionsCopy; 219 } 220 221 public CompileTheWorld(HotSpotJVMCIRuntimeProvider jvmciRuntime, HotSpotGraalCompiler compiler, OptionValues options) { 222 this(jvmciRuntime, compiler, Options.Classpath.getValue(options), 223 Options.StartAt.getValue(options), 224 Options.StopAt.getValue(options), 225 Options.MethodFilter.getValue(options), 226 Options.ExcludeMethodFilter.getValue(options), 227 Options.Verbose.getValue(options), 228 options, 229 parseOptions(Options.Config.getValue(options))); 230 } 231 232 /** 233 * Compiles all methods in all classes in {@link #inputClassPath}. If {@link #inputClassPath} 234 * equals {@link #SUN_BOOT_CLASS_PATH} the boot classes are used. 235 */ 236 public void compile() throws Throwable { 237 if (SUN_BOOT_CLASS_PATH.equals(inputClassPath)) { 238 String bcpEntry = null; 239 if (Java8OrEarlier) { 240 final String[] entries = System.getProperty(SUN_BOOT_CLASS_PATH).split(File.pathSeparator); 241 for (int i = 0; i < entries.length && bcpEntry == null; i++) { 242 String entry = entries[i]; 243 File entryFile = new File(entry); 244 if (entryFile.getName().endsWith("rt.jar") && entryFile.isFile()) { 245 bcpEntry = entry; 246 } 247 } 248 if (bcpEntry == null) { 249 throw new GraalError("Could not find rt.jar on boot class path %s", System.getProperty(SUN_BOOT_CLASS_PATH)); 250 } 251 } else { 252 bcpEntry = JRT_CLASS_PATH_ENTRY; 253 } 254 compile(bcpEntry); 255 } else { 256 compile(inputClassPath); 257 } 258 } 259 260 public void println() { 261 println(""); 262 } 263 264 public void println(String format, Object... args) { 265 println(String.format(format, args)); 266 } 267 268 public void println(String s) { 269 println(verbose, s); 270 } 271 272 public static void println(boolean cond, String s) { 273 if (cond) { 274 TTY.println(s); 275 } 276 } 277 278 public void printStackTrace(Throwable t) { 279 if (verbose) { 280 t.printStackTrace(TTY.out); 281 } 282 } 283 284 @SuppressWarnings("unused") 285 private static void dummy() { 286 } 287 288 /** 289 * Abstraction over different types of class path entries. 290 */ 291 abstract static class ClassPathEntry implements Closeable { 292 final String name; 293 294 ClassPathEntry(String name) { 295 this.name = name; 296 } 297 298 /** 299 * Creates a {@link ClassLoader} for loading classes from this entry. 300 */ 301 public abstract ClassLoader createClassLoader() throws IOException; 302 303 /** 304 * Gets the list of classes available under this entry. 305 */ 306 public abstract List<String> getClassNames() throws IOException; 307 308 @Override 309 public String toString() { 310 return name; 311 } 312 313 @Override 314 public void close() throws IOException { 315 } 316 } 317 318 /** 319 * A class path entry that is a normal file system directory. 320 */ 321 static class DirClassPathEntry extends ClassPathEntry { 322 323 private final File dir; 324 325 DirClassPathEntry(String name) { 326 super(name); 327 dir = new File(name); 328 assert dir.isDirectory(); 329 } 330 331 @Override 332 public ClassLoader createClassLoader() throws IOException { 333 URL url = dir.toURI().toURL(); 334 return new URLClassLoader(new URL[]{url}); 335 } 336 337 @Override 338 public List<String> getClassNames() throws IOException { 339 List<String> classNames = new ArrayList<>(); 340 String root = dir.getPath(); 341 SimpleFileVisitor<Path> visitor = new SimpleFileVisitor<Path>() { 342 @Override 343 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 344 if (attrs.isRegularFile()) { 345 File path = file.toFile(); 346 if (path.getName().endsWith(".class")) { 347 String pathString = path.getPath(); 348 assert pathString.startsWith(root); 349 String classFile = pathString.substring(root.length() + 1); 350 String className = classFile.replace(File.separatorChar, '.'); 351 classNames.add(className.replace('/', '.').substring(0, className.length() - ".class".length())); 352 } 353 } 354 return super.visitFile(file, attrs); 355 } 356 }; 357 Files.walkFileTree(dir.toPath(), visitor); 358 return classNames; 359 } 360 } 361 362 /** 363 * A class path entry that is a jar or zip file. 364 */ 365 static class JarClassPathEntry extends ClassPathEntry { 366 367 private final JarFile jarFile; 368 369 JarClassPathEntry(String name) throws IOException { 370 super(name); 371 jarFile = new JarFile(name); 372 } 373 374 @Override 375 public ClassLoader createClassLoader() throws IOException { 376 URL url = new URL("jar", "", "file:" + name + "!/"); 377 return new URLClassLoader(new URL[]{url}); 378 } 379 380 @Override 381 public List<String> getClassNames() throws IOException { 382 Enumeration<JarEntry> e = jarFile.entries(); 383 List<String> classNames = new ArrayList<>(jarFile.size()); 384 while (e.hasMoreElements()) { 385 JarEntry je = e.nextElement(); 386 if (je.isDirectory() || !je.getName().endsWith(".class")) { 387 continue; 388 } 389 String className = je.getName().substring(0, je.getName().length() - ".class".length()); 390 classNames.add(className.replace('/', '.')); 391 } 392 return classNames; 393 } 394 395 @Override 396 public void close() throws IOException { 397 jarFile.close(); 398 } 399 } 400 401 /** 402 * A class path entry representing the {@code jrt:/} file system. 403 */ 404 static class JRTClassPathEntry extends ClassPathEntry { 405 406 private final String limitModules; 407 408 JRTClassPathEntry(String name, String limitModules) { 409 super(name); 410 this.limitModules = limitModules; 411 } 412 413 @Override 414 public ClassLoader createClassLoader() throws IOException { 415 URL url = URI.create("jrt:/").toURL(); 416 return new URLClassLoader(new URL[]{url}); 417 } 418 419 @Override 420 public List<String> getClassNames() throws IOException { 421 Set<String> negative = new HashSet<>(); 422 Set<String> positive = new HashSet<>(); 423 if (limitModules != null && !limitModules.isEmpty()) { 424 for (String s : limitModules.split(",")) { 425 if (s.startsWith("~")) { 426 negative.add(s.substring(1)); 427 } else { 428 positive.add(s); 429 } 430 } 431 } 432 List<String> classNames = new ArrayList<>(); 433 FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Collections.emptyMap()); 434 Path top = fs.getPath("/modules/"); 435 Files.find(top, Integer.MAX_VALUE, 436 (path, attrs) -> attrs.isRegularFile()).forEach(p -> { 437 int nameCount = p.getNameCount(); 438 if (nameCount > 2) { 439 String base = p.getName(nameCount - 1).toString(); 440 if (base.endsWith(".class") && !base.equals("module-info.class")) { 441 String module = p.getName(1).toString(); 442 if (positive.isEmpty() || positive.contains(module)) { 443 if (negative.isEmpty() || !negative.contains(module)) { 444 // Strip module prefix and convert to dotted form 445 String className = p.subpath(2, nameCount).toString().replace('/', '.'); 446 // Strip ".class" suffix 447 className = className.replace('/', '.').substring(0, className.length() - ".class".length()); 448 classNames.add(className); 449 } 450 } 451 } 452 } 453 }); 454 return classNames; 455 } 456 } 457 458 private boolean isClassIncluded(String className) { 459 if (methodFilters != null && !MethodFilter.matchesClassName(methodFilters, className)) { 460 return false; 461 } 462 if (excludeMethodFilters != null && MethodFilter.matchesClassName(excludeMethodFilters, className)) { 463 return false; 464 } 465 return true; 466 } 467 468 /** 469 * Compiles all methods in all classes in a given class path. 470 * 471 * @param classPath class path denoting classes to compile 472 * @throws IOException 473 */ 474 @SuppressWarnings("try") 475 private void compile(String classPath) throws IOException { 476 final String[] entries = classPath.split(File.pathSeparator); 477 long start = System.currentTimeMillis(); 478 479 try { 480 // compile dummy method to get compiler initialized outside of the 481 // config debug override. 482 HotSpotResolvedJavaMethod dummyMethod = (HotSpotResolvedJavaMethod) JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess().lookupJavaMethod( 483 CompileTheWorld.class.getDeclaredMethod("dummy")); 484 int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI; 485 boolean useProfilingInfo = false; 486 boolean installAsDefault = false; 487 CompilationTask task = new CompilationTask(jvmciRuntime, compiler, new HotSpotCompilationRequest(dummyMethod, entryBCI, 0L), useProfilingInfo, installAsDefault, currentOptions); 488 task.runCompilation(); 489 } catch (NoSuchMethodException | SecurityException e1) { 490 printStackTrace(e1); 491 } 492 493 /* 494 * Always use a thread pool, even for single threaded mode since it simplifies the use of 495 * DebugValueThreadFilter to filter on the thread names. 496 */ 497 int threadCount = 1; 498 if (Options.MultiThreaded.getValue(currentOptions)) { 499 threadCount = Options.Threads.getValue(currentOptions); 500 if (threadCount == 0) { 501 threadCount = Runtime.getRuntime().availableProcessors(); 502 } 503 } else { 504 running = true; 505 } 506 507 OptionValues savedOptions = currentOptions; 508 currentOptions = new OptionValues(compilationOptions); 509 threadPool = new ThreadPoolExecutor(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new CompilerThreadFactory("CompileTheWorld")); 510 511 try { 512 for (int i = 0; i < entries.length; i++) { 513 final String entry = entries[i]; 514 515 ClassPathEntry cpe; 516 if (entry.endsWith(".zip") || entry.endsWith(".jar")) { 517 cpe = new JarClassPathEntry(entry); 518 } else if (entry.equals(JRT_CLASS_PATH_ENTRY)) { 519 cpe = new JRTClassPathEntry(entry, Options.LimitModules.getValue(currentOptions)); 520 } else { 521 if (!new File(entry).isDirectory()) { 522 println("CompileTheWorld : Skipped classes in " + entry); 523 println(); 524 continue; 525 } 526 cpe = new DirClassPathEntry(entry); 527 } 528 529 if (methodFilters == null || methodFilters.length == 0) { 530 println("CompileTheWorld : Compiling all classes in " + entry); 531 } else { 532 String include = Arrays.asList(methodFilters).stream().map(MethodFilter::toString).collect(Collectors.joining(", ")); 533 println("CompileTheWorld : Compiling all methods in " + entry + " matching one of the following filters: " + include); 534 } 535 if (excludeMethodFilters != null && excludeMethodFilters.length > 0) { 536 String exclude = Arrays.asList(excludeMethodFilters).stream().map(MethodFilter::toString).collect(Collectors.joining(", ")); 537 println("CompileTheWorld : Excluding all methods matching one of the following filters: " + exclude); 538 } 539 println(); 540 541 ClassLoader loader = cpe.createClassLoader(); 542 543 for (String className : cpe.getClassNames()) { 544 545 // Are we done? 546 if (classFileCounter >= stopAt) { 547 break; 548 } 549 550 classFileCounter++; 551 552 if (className.startsWith("jdk.management.") || className.startsWith("jdk.internal.cmm.*")) { 553 continue; 554 } 555 556 try { 557 // Load and initialize class 558 Class<?> javaClass = Class.forName(className, true, loader); 559 560 // Pre-load all classes in the constant pool. 561 try { 562 HotSpotResolvedObjectType objectType = HotSpotResolvedObjectType.fromObjectClass(javaClass); 563 ConstantPool constantPool = objectType.getConstantPool(); 564 for (int cpi = 1; cpi < constantPool.length(); cpi++) { 565 constantPool.loadReferencedType(cpi, Bytecodes.LDC); 566 } 567 } catch (Throwable t) { 568 // If something went wrong during pre-loading we just ignore it. 569 if (isClassIncluded(className)) { 570 println("Preloading failed for (%d) %s: %s", classFileCounter, className, t); 571 } 572 continue; 573 } 574 575 /* 576 * Only check filters after class loading and resolution to mitigate impact 577 * on reproducibility. 578 */ 579 if (!isClassIncluded(className)) { 580 continue; 581 } 582 583 // Are we compiling this class? 584 MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess(); 585 if (classFileCounter >= startAt) { 586 println("CompileTheWorld (%d) : %s", classFileCounter, className); 587 588 // Compile each constructor/method in the class. 589 for (Constructor<?> constructor : javaClass.getDeclaredConstructors()) { 590 HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(constructor); 591 if (canBeCompiled(javaMethod, constructor.getModifiers())) { 592 compileMethod(javaMethod); 593 } 594 } 595 for (Method method : javaClass.getDeclaredMethods()) { 596 HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(method); 597 if (canBeCompiled(javaMethod, method.getModifiers())) { 598 compileMethod(javaMethod); 599 } 600 } 601 602 // Also compile the class initializer if it exists 603 HotSpotResolvedJavaMethod clinit = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaType(javaClass).getClassInitializer(); 604 if (clinit != null && canBeCompiled(clinit, clinit.getModifiers())) { 605 compileMethod(clinit); 606 } 607 } 608 } catch (Throwable t) { 609 if (isClassIncluded(className)) { 610 println("CompileTheWorld (%d) : Skipping %s %s", classFileCounter, className, t.toString()); 611 printStackTrace(t); 612 } 613 } 614 } 615 cpe.close(); 616 } 617 } finally { 618 currentOptions = savedOptions; 619 } 620 621 if (!running) { 622 startThreads(); 623 } 624 int wakeups = 0; 625 while (threadPool.getCompletedTaskCount() != threadPool.getTaskCount()) { 626 if (wakeups % 15 == 0) { 627 TTY.println("CompileTheWorld : Waiting for " + (threadPool.getTaskCount() - threadPool.getCompletedTaskCount()) + " compiles"); 628 } 629 try { 630 threadPool.awaitTermination(1, TimeUnit.SECONDS); 631 wakeups++; 632 } catch (InterruptedException e) { 633 } 634 } 635 threadPool = null; 636 637 long elapsedTime = System.currentTimeMillis() - start; 638 639 println(); 640 if (Options.MultiThreaded.getValue(currentOptions)) { 641 TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms elapsed, %d ms compile time, %d bytes of memory used)", classFileCounter, compiledMethodsCounter.get(), elapsedTime, 642 compileTime.get(), memoryUsed.get()); 643 } else { 644 TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms, %d bytes of memory used)", classFileCounter, compiledMethodsCounter.get(), compileTime.get(), memoryUsed.get()); 645 } 646 } 647 648 private synchronized void startThreads() { 649 running = true; 650 // Wake up any waiting threads 651 notifyAll(); 652 } 653 654 private synchronized void waitToRun() { 655 while (!running) { 656 try { 657 wait(); 658 } catch (InterruptedException e) { 659 } 660 } 661 } 662 663 @SuppressWarnings("try") 664 private void compileMethod(HotSpotResolvedJavaMethod method) throws InterruptedException, ExecutionException { 665 if (methodFilters != null && !MethodFilter.matches(methodFilters, method)) { 666 return; 667 } 668 if (excludeMethodFilters != null && MethodFilter.matches(excludeMethodFilters, method)) { 669 return; 670 } 671 Future<?> task = threadPool.submit(new Runnable() { 672 @Override 673 public void run() { 674 waitToRun(); 675 OptionValues savedOptions = currentOptions; 676 currentOptions = new OptionValues(compilationOptions); 677 try { 678 compileMethod(method, classFileCounter); 679 } finally { 680 currentOptions = savedOptions; 681 } 682 } 683 }); 684 if (threadPool.getCorePoolSize() == 1) { 685 task.get(); 686 } 687 } 688 689 /** 690 * Compiles a method and gathers some statistics. 691 */ 692 private void compileMethod(HotSpotResolvedJavaMethod method, int counter) { 693 try { 694 long start = System.currentTimeMillis(); 695 long allocatedAtStart = getCurrentThreadAllocatedBytes(); 696 int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI; 697 HotSpotCompilationRequest request = new HotSpotCompilationRequest(method, entryBCI, 0L); 698 // For more stable CTW execution, disable use of profiling information 699 boolean useProfilingInfo = false; 700 boolean installAsDefault = false; 701 CompilationTask task = new CompilationTask(jvmciRuntime, compiler, request, useProfilingInfo, installAsDefault, currentOptions); 702 task.runCompilation(); 703 704 // Invalidate the generated code so the code cache doesn't fill up 705 HotSpotInstalledCode installedCode = task.getInstalledCode(); 706 if (installedCode != null) { 707 installedCode.invalidate(); 708 } 709 710 memoryUsed.getAndAdd(getCurrentThreadAllocatedBytes() - allocatedAtStart); 711 compileTime.getAndAdd(System.currentTimeMillis() - start); 712 compiledMethodsCounter.incrementAndGet(); 713 } catch (Throwable t) { 714 // Catch everything and print a message 715 println("CompileTheWorld (%d) : Error compiling method: %s", counter, method.format("%H.%n(%p):%r")); 716 printStackTrace(t); 717 } 718 } 719 720 /** 721 * Determines if a method should be compiled (Cf. CompilationPolicy::can_be_compiled). 722 * 723 * @return true if it can be compiled, false otherwise 724 */ 725 private boolean canBeCompiled(HotSpotResolvedJavaMethod javaMethod, int modifiers) { 726 if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) { 727 return false; 728 } 729 GraalHotSpotVMConfig c = compiler.getGraalRuntime().getVMConfig(); 730 if (c.dontCompileHugeMethods && javaMethod.getCodeSize() > c.hugeMethodLimit) { 731 println(verbose || methodFilters != null, 732 String.format("CompileTheWorld (%d) : Skipping huge method %s (use -XX:-DontCompileHugeMethods or -XX:HugeMethodLimit=%d to include it)", classFileCounter, 733 javaMethod.format("%H.%n(%p):%r"), 734 javaMethod.getCodeSize())); 735 return false; 736 } 737 // Allow use of -XX:CompileCommand=dontinline to exclude problematic methods 738 if (!javaMethod.canBeInlined()) { 739 return false; 740 } 741 // Skip @Snippets for now 742 for (Annotation annotation : javaMethod.getAnnotations()) { 743 if (annotation.annotationType().equals(Snippet.class)) { 744 return false; 745 } 746 } 747 return true; 748 } 749 750 static class Options { 751 // @formatter:off 752 public static final OptionKey<Boolean> Help = new OptionKey<>(false); 753 public static final OptionKey<String> Classpath = new OptionKey<>(CompileTheWorld.SUN_BOOT_CLASS_PATH); 754 public static final OptionKey<Boolean> Verbose = new OptionKey<>(true); 755 /** 756 * Ignore Graal classes by default to avoid problems associated with compiling 757 * snippets and method substitutions. 758 */ 759 public static final OptionKey<String> LimitModules = new OptionKey<>("~jdk.internal.vm.compiler"); 760 public static final OptionKey<Integer> Iterations = new OptionKey<>(1); 761 public static final OptionKey<String> MethodFilter = new OptionKey<>(null); 762 public static final OptionKey<String> ExcludeMethodFilter = new OptionKey<>(null); 763 public static final OptionKey<Integer> StartAt = new OptionKey<>(1); 764 public static final OptionKey<Integer> StopAt = new OptionKey<>(Integer.MAX_VALUE); 765 public static final OptionKey<String> Config = new OptionKey<>(null); 766 public static final OptionKey<Boolean> MultiThreaded = new OptionKey<>(false); 767 public static final OptionKey<Integer> Threads = new OptionKey<>(0); 768 769 static final ReflectionOptionDescriptors DESCRIPTORS = new ReflectionOptionDescriptors(Options.class, 770 "Help", "List options and their help messages and then exit.", 771 "Classpath", "Class path denoting methods to compile. Default is to compile boot classes.", 772 "Verbose", "Verbose operation.", 773 "LimitModules", "Comma separated list of module names to which compilation should be limited. " + 774 "Module names can be prefixed with \"~\" to exclude the named module.", 775 "Iterations", "The number of iterations to perform.", 776 "MethodFilter", "Only compile methods matching this filter.", 777 "ExcludeMethodFilter", "Exclude methods matching this filter from compilation.", 778 "StartAt", "First class to consider for compilation.", 779 "StopAt", "Last class to consider for compilation.", 780 "Config", "Option value overrides to use during compile the world. For example, " + 781 "to disable inlining and partial escape analysis specify 'PartialEscapeAnalysis=false Inline=false'. " + 782 "The format for each option is the same as on the command line just without the '-Dgraal.' prefix.", 783 "MultiThreaded", "Run using multiple threads for compilation.", 784 "Threads", "Number of threads to use for multithreaded execution. Defaults to Runtime.getRuntime().availableProcessors()."); 785 // @formatter:on 786 } 787 788 public static OptionValues loadOptions(OptionValues initialValues) { 789 EconomicMap<OptionKey<?>, Object> values = OptionValues.newOptionMap(); 790 List<OptionDescriptors> loader = singletonList(DESCRIPTORS); 791 OptionsParser.parseOptions(extractEntries(System.getProperties(), "CompileTheWorld.", true), values, loader); 792 OptionValues options = new OptionValues(initialValues, values); 793 if (Options.Help.getValue(options)) { 794 options.printHelp(loader, System.out, "CompileTheWorld."); 795 System.exit(0); 796 } 797 return options; 798 } 799 800 public static void main(String[] args) throws Throwable { 801 HotSpotJVMCIRuntime jvmciRuntime = HotSpotJVMCIRuntime.runtime(); 802 HotSpotGraalCompiler compiler = (HotSpotGraalCompiler) jvmciRuntime.getCompiler(); 803 HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime(); 804 HotSpotCodeCacheProvider codeCache = graalRuntime.getHostProviders().getCodeCache(); 805 OptionValues options = loadOptions(graalRuntime.getOptions()); 806 807 int iterations = Options.Iterations.getValue(options); 808 for (int i = 0; i < iterations; i++) { 809 codeCache.resetCompilationStatistics(); 810 TTY.println("CompileTheWorld : iteration " + i); 811 812 CompileTheWorld ctw = new CompileTheWorld(jvmciRuntime, compiler, options); 813 ctw.compile(); 814 } 815 // This is required as non-daemon threads can be started by class initializers 816 System.exit(0); 817 } 818} 819