1 /* 2 * Copyright (c) 2013, 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 com.oracle.graal.hotspot; 24 25 import java.io.File; 26 import java.lang.reflect.Constructor; 27 import java.lang.reflect.Method; 28 import java.lang.reflect.Modifier; 29 import java.net.*; 30 import java.util.Enumeration; 31 import java.util.jar.*; 32 33 import com.oracle.graal.api.meta.*; 34 import com.oracle.graal.bytecode.Bytecodes; 35 import com.oracle.graal.debug.*; 36 import com.oracle.graal.hotspot.bridge.*; 37 import com.oracle.graal.hotspot.meta.*; 38 import com.oracle.graal.nodes.*; 39 import com.oracle.graal.phases.*; 40 import com.oracle.graal.replacements.*; 41 42 /** 43 * This class implements compile-the-world functionality in Graal. 44 */ 45 public final class CompileTheWorld { 46 47 /** 48 * This is our magic token to trigger reading files from the boot class path. 49 */ 50 public final static String SUN_BOOT_CLASS_PATH = "sun.boot.class.path"; 51 52 // Some runtime instances we need. 53 private final HotSpotGraalRuntime graalRuntime = HotSpotGraalRuntime.getInstance(); 54 private final VMToCompilerImpl vmToCompiler = (VMToCompilerImpl) graalRuntime.getVMToCompiler(); 55 56 /** List of Zip/Jar files to compile (see {@link GraalOptions.CompileTheWorld} */ 57 private final String files; 58 59 /** Class index to start compilation at (see {@link GraalOptions.CompileTheWorldStartAt} */ 60 private final int startAt; 61 62 /** Class index to stop compilation at (see {@link GraalOptions.CompileTheWorldStopAt} */ 63 private final int stopAt; 64 65 // Counters 66 private int classFileCounter = 0; 67 private int compiledMethodsCounter = 0; 68 private long compileTime = 0; 69 70 /** 71 * Create a compile-the-world instance with default values from 72 * {@link GraalOptions.CompileTheWorld}, {@link GraalOptions.CompileTheWorldStartAt} and 73 * {@link GraalOptions.CompileTheWorldStopAt}. 74 */ 75 public CompileTheWorld() { 76 this(GraalOptions.CompileTheWorld, GraalOptions.CompileTheWorldStartAt, GraalOptions.CompileTheWorldStopAt); 77 } 78 79 /** 80 * Create a compile-the-world instance. 81 * 82 * @param files {@link File.pathSeparator} separated list of Zip/Jar files 83 * @param startAt index of the class file to start compilation at 84 * @param stopAt index of the class file to stop compilation at 85 */ 86 public CompileTheWorld(String files, int startAt, int stopAt) { 87 this.files = files; 88 this.startAt = startAt; 89 this.stopAt = stopAt; 90 91 // We don't want the VM to exit when a method fails to compile. 92 GraalOptions.ExitVMOnException = false; 93 } 94 95 /** 96 * Compile all methods in all classes in the Zip/Jar files in 97 * {@link GraalOptions.CompileTheWorld}. If the GraalOptions.CompileTheWorld contains the magic 98 * token {@link SUN_BOOT_CLASS_PATH} passed up from HotSpot we take the files from the boot 99 * class path. 100 * 101 * @throws Throwable 102 */ 103 public void compile() throws Throwable { 104 if (SUN_BOOT_CLASS_PATH.equals(files)) { 105 final String[] entries = System.getProperty(SUN_BOOT_CLASS_PATH).split(File.pathSeparator); 106 String files = ""; 107 for (int i = 0; i < entries.length; i++) { 108 final String entry = entries[i]; 109 110 // We stop at rt.jar, unless it is the first boot class path entry. 111 if (entry.endsWith("rt.jar") && (i > 0)) { 112 break; 113 } 114 if (i > 0) { 115 files += File.pathSeparator; 116 } 117 files += entry; 118 } 119 compile(files); 120 } else { 121 compile(files); 122 } 123 } 124 125 /** 126 * Compile all methods in all classes in the Zip/Jar files passed in files. 127 * 128 * @param files {@link File.pathSeparator} separated list of Zip/Jar files 129 * @throws Throwable 130 */ 131 private void compile(String files) throws Throwable { 132 final String[] entries = files.split(File.pathSeparator); 133 134 for (int i = 0; i < entries.length; i++) { 135 final String entry = entries[i]; 136 137 // For now we only compile all methods in all classes in zip/jar files. 138 if (!entry.endsWith(".zip") && !entry.endsWith(".jar")) { 139 TTY.println("CompileTheWorld : Skipped classes in " + entry); 140 TTY.println(); 141 continue; 142 } 143 144 TTY.println("CompileTheWorld : Compiling all classes in " + entry); 145 TTY.println(); 146 147 URL url = new URL("jar", "", "file:" + entry + "!/"); 148 ClassLoader loader = new URLClassLoader(new URL[]{url}); 149 150 JarFile jarFile = new JarFile(entry); 151 Enumeration<JarEntry> e = jarFile.entries(); 152 153 while (e.hasMoreElements()) { 154 JarEntry je = e.nextElement(); 155 if (je.isDirectory() || !je.getName().endsWith(".class")) { 156 continue; 157 } 158 159 // Are we done? 160 if (classFileCounter >= stopAt) { 161 break; 162 } 163 164 String className = je.getName().substring(0, je.getName().length() - ".class".length()); 165 className = className.replace('/', '.'); 166 classFileCounter++; 167 168 try { 169 // Load and initialize class 170 Class<?> javaClass = Class.forName(className, true, loader); 171 172 // Pre-load all classes in the constant pool. 173 try { 174 HotSpotResolvedObjectType objectType = (HotSpotResolvedObjectType) HotSpotResolvedObjectType.fromClass(javaClass); 175 ConstantPool constantPool = objectType.constantPool(); 176 for (int cpi = 1; cpi < constantPool.length(); cpi++) { 177 constantPool.loadReferencedType(cpi, Bytecodes.LDC); 178 } 179 } catch (Throwable t) { 180 // If something went wrong during pre-loading we just ignore it. 181 TTY.println("CompileTheWorld (%d) : Preloading failed for %s", classFileCounter, className); 182 } 183 184 // Are we compiling this class? 185 if (classFileCounter >= startAt) { 186 TTY.println("CompileTheWorld (%d) : %s", classFileCounter, className); 187 188 // Enqueue each constructor/method in the class for compilation. 189 for (Constructor<?> constructor : javaClass.getDeclaredConstructors()) { 190 HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) graalRuntime.getRuntime().lookupJavaConstructor(constructor); 191 if (canBeCompiled(javaMethod, constructor.getModifiers())) { 192 compileMethod(javaMethod); 193 } 194 } 195 for (Method method : javaClass.getDeclaredMethods()) { 196 HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) graalRuntime.getRuntime().lookupJavaMethod(method); 197 if (canBeCompiled(javaMethod, method.getModifiers())) { 198 compileMethod(javaMethod); 199 } 200 } 201 } 202 } catch (Throwable t) { 203 TTY.println("CompileTheWorld (%d) : Skipping %s", classFileCounter, className); 204 } 205 } 206 jarFile.close(); 207 } 208 209 TTY.println(); 210 TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms)", classFileCounter, compiledMethodsCounter, compileTime); 211 } 212 213 /** 214 * Helper method to schedule a method for compilation and gather some statistics. 215 */ 216 private void compileMethod(HotSpotResolvedJavaMethod method) { 217 try { 218 long start = System.currentTimeMillis(); 219 vmToCompiler.compileMethod(method, StructuredGraph.INVOCATION_ENTRY_BCI, true, 10); 220 compileTime += (System.currentTimeMillis() - start); 221 compiledMethodsCounter++; 222 method.reprofile(); // makes the method also not-entrant 223 } catch (Throwable t) { 224 // Catch everything and print a message 225 TTY.println("CompileTheWorld (%d) : Error compiling method: %s", classFileCounter, MetaUtil.format("%H.%n(%p):%r", method)); 226 t.printStackTrace(TTY.cachedOut); 227 } 228 } 229 230 /** 231 * Helper method for CompileTheWorld to determine if a method should be compiled (Cf. 232 * CompilationPolicy::can_be_compiled). 233 * 234 * @return true if it can be compiled, false otherwise 235 */ 236 private static boolean canBeCompiled(HotSpotResolvedJavaMethod javaMethod, int modifiers) { 237 if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) { 238 return false; 239 } 240 // This number is from HotSpot: 241 final int HugeMethodLimit = 8000; 242 if (javaMethod.getCodeSize() > HugeMethodLimit) { 243 return false; 244 } 245 // Skip @Snippets for now 246 if (javaMethod.getAnnotation(Snippet.class) != null) { 247 return false; 248 } 249 return true; 250 } 251 252 }