001/* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013package org.jikesrvm.compilers.common; 014 015import static org.jikesrvm.runtime.UnboxedSizeConstants.BYTES_IN_ADDRESS; 016 017import java.util.Comparator; 018import java.util.Set; 019import java.util.TreeMap; 020 021import org.jikesrvm.VM; 022import org.jikesrvm.classloader.RVMArray; 023import org.jikesrvm.classloader.RVMMethod; 024import org.jikesrvm.classloader.RVMType; 025import org.jikesrvm.compilers.baseline.BaselineCompiledMethod; 026import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod; 027import org.jikesrvm.jni.JNICompiledMethod; 028import org.jikesrvm.runtime.Magic; 029import org.jikesrvm.runtime.Memory; 030import org.jikesrvm.util.Services; 031import org.vmmagic.pragma.Uninterruptible; 032import org.vmmagic.unboxed.Address; 033 034/** 035 * Manage pool of compiled methods. <p> 036 * Original extracted from RVMClassLoader. 037 */ 038public class CompiledMethods { 039 /** 040 * 2^LOG_ROW_SIZE is the number of elements per row 041 */ 042 private static final int LOG_ROW_SIZE = 10; 043 /** 044 * Mask to ascertain row from id number 045 */ 046 private static final int ROW_MASK = (1 << LOG_ROW_SIZE) - 1; 047 /** 048 * Java methods that have been compiled into machine code. 049 * Note that there may be more than one compiled versions of the same method 050 * (i.e. at different levels of optimization). 051 */ 052 private static CompiledMethod[][] compiledMethods = new CompiledMethod[16][1 << LOG_ROW_SIZE]; 053 054 /** 055 * Index of most recently allocated slot in compiledMethods[]. 056 */ 057 private static int currentCompiledMethodId = 0; 058 059 /** 060 * Used to communicate between {@link #setCompiledMethodObsolete} 061 * and {@link #snipObsoleteCompiledMethods} 062 */ 063 private static boolean scanForObsoleteMethods = false; 064 065 /** 066 * Ensure space in backing array for id. 067 * 068 * @param id the id we need to ensure capacity for 069 */ 070 private static void ensureCapacity(int id) { 071 int column = id >> LOG_ROW_SIZE; 072 if (column >= compiledMethods.length) { 073 CompiledMethod[][] tmp = new CompiledMethod[column + 1][]; 074 for (int i = 0; i < column; i++) { 075 tmp[i] = compiledMethods[i]; 076 } 077 tmp[column] = new CompiledMethod[1 << LOG_ROW_SIZE]; 078 compiledMethods = tmp; 079 Magic.sync(); 080 } 081 } 082 083 /** 084 * @param cmid id of the method 085 * @return a previously compiled method without checking 086 */ 087 @Uninterruptible 088 public static CompiledMethod getCompiledMethodUnchecked(int cmid) { 089 int column = cmid >> LOG_ROW_SIZE; 090 return compiledMethods[column][cmid & ROW_MASK]; 091 } 092 093 @Uninterruptible 094 private static void setCompiledMethod(int cmid, CompiledMethod cm) { 095 int column = cmid >> LOG_ROW_SIZE; 096 CompiledMethod[] col = compiledMethods[column]; 097 Services.setArrayUninterruptible(col, cmid & ROW_MASK, cm); 098 } 099 100 /** 101 * @param compiledMethodId the id of the compiled method 102 * @return a previously compiled method 103 */ 104 @Uninterruptible 105 public static CompiledMethod getCompiledMethod(int compiledMethodId) { 106 Magic.isync(); // see potential update from other procs 107 108 if (VM.VerifyAssertions) { 109 if (!(0 < compiledMethodId && compiledMethodId <= currentCompiledMethodId)) { 110 VM.sysWriteln("WARNING: attempt to get compiled method #", compiledMethodId); 111 VM.sysFail("attempt to get an invalid compiled method ID"); 112 return null; 113 } 114 } 115 116 return getCompiledMethodUnchecked(compiledMethodId); 117 } 118 119 public static synchronized CompiledMethod createCompiledMethod(RVMMethod m, int compilerType) { 120 int id = currentCompiledMethodId + 1; 121 ensureCapacity(id); 122 currentCompiledMethodId++; 123 CompiledMethod cm = null; 124 if (compilerType == CompiledMethod.BASELINE) { 125 cm = new BaselineCompiledMethod(id, m); 126 } else if (VM.BuildForOptCompiler && compilerType == CompiledMethod.OPT) { 127 cm = new OptCompiledMethod(id, m); 128 } else if (compilerType == CompiledMethod.JNI) { 129 cm = new JNICompiledMethod(id, m); 130 } else { 131 if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED, "Unexpected compiler type!"); 132 } 133 setCompiledMethod(id, cm); 134 return cm; 135 } 136 137 /** 138 * @return a CompiledMethod for the synthetic hardware trap frame 139 */ 140 public static synchronized CompiledMethod createHardwareTrapCompiledMethod() { 141 int id = currentCompiledMethodId + 1; 142 ensureCapacity(id); 143 currentCompiledMethodId++; 144 CompiledMethod cm = new HardwareTrapCompiledMethod(id, null); 145 setCompiledMethod(id, cm); 146 return cm; 147 } 148 149 /** 150 * @return number of methods compiled so far. 151 */ 152 @Uninterruptible 153 public static int numCompiledMethods() { 154 return currentCompiledMethodId + 1; 155 } 156 157 /** 158 * Find the method whose machine code contains the specified instruction.<p> 159 * 160 * Assumption: caller has disabled gc (otherwise collector could move 161 * objects without fixing up the raw <code>ip</code> pointer)<p> 162 * 163 * Note: this method is highly inefficient. Normally you should use the 164 * following instead: 165 * 166 * <code> 167 * RVMClassLoader.getCompiledMethod(Magic.getCompiledMethodID(fp)) 168 * </code> 169 * 170 * @param ip instruction address. Usage note: <code>ip</code> must point 171 * to the instruction *following* the 172 * actual instruction whose method is sought. This allows us to properly 173 * handle the case where the only address we have to work with is a return 174 * address (i.e. from a stackframe) or an exception address (i.e. from a null 175 * pointer dereference, array bounds check, or divide by zero) on a machine 176 * architecture with variable length instructions. In such situations we'd 177 * have no idea how far to back up the instruction pointer to point to the 178 * "call site" or "exception site". 179 * 180 * @return method (<code>null</code> --> not found) 181 */ 182 @Uninterruptible 183 public static CompiledMethod findMethodForInstruction(Address ip) { 184 for (int i = 0, n = numCompiledMethods(); i < n; ++i) { 185 CompiledMethod compiledMethod = getCompiledMethodUnchecked(i); 186 if (compiledMethod == null || !compiledMethod.isCompiled()) { 187 continue; // empty slot 188 } 189 190 if (compiledMethod.containsReturnAddress(ip)) { 191 return compiledMethod; 192 } 193 } 194 195 return null; 196 } 197 198 // We keep track of compiled methods that become obsolete because they have 199 // been replaced by another version. These are candidates for GC. But, they 200 // can only be collected once we are certain that they are no longer being 201 // executed. Here, we keep track of them until we know they are no longer 202 // in use. 203 public static void setCompiledMethodObsolete(CompiledMethod compiledMethod) { 204 compiledMethod.setObsolete(); 205 Magic.sync(); 206 scanForObsoleteMethods = true; 207 } 208 209 /** 210 * Snip reference to CompiledMethod so that we can reclaim code space. If 211 * the code is currently being executed, stack scanning is responsible for 212 * marking it NOT obsolete. Keep such reference until a future GC. 213 * <p> 214 * NOTE: It's expected that this is processed during GC, after scanning 215 * stacks to determine which methods are currently executing. 216 */ 217 @Uninterruptible 218 public static void snipObsoleteCompiledMethods() { 219 Magic.isync(); 220 if (!scanForObsoleteMethods) return; 221 scanForObsoleteMethods = false; 222 Magic.sync(); 223 224 int max = numCompiledMethods(); 225 for (int i = 0; i < max; i++) { 226 CompiledMethod cm = getCompiledMethodUnchecked(i); 227 if (cm != null) { 228 if (cm.isActiveOnStack()) { 229 if (cm.isObsolete()) { 230 // can't get it this time; force us to look again next GC 231 scanForObsoleteMethods = true; 232 Magic.sync(); 233 } 234 cm.clearActiveOnStack(); 235 } else { 236 if (cm.isObsolete()) { 237 // obsolete and not active on a thread stack: it's garbage! 238 setCompiledMethod(i, null); 239 } 240 } 241 } 242 } 243 } 244 245 /** 246 * Report on the space used by compiled code and associated mapping information 247 */ 248 public static void spaceReport() { 249 int[] codeCount = new int[CompiledMethod.NUM_COMPILER_TYPES + 1]; 250 int[] codeBytes = new int[CompiledMethod.NUM_COMPILER_TYPES + 1]; 251 int[] mapBytes = new int[CompiledMethod.NUM_COMPILER_TYPES + 1]; 252 253 RVMArray codeArray = RVMType.CodeArrayType.asArray(); 254 for (int i = 0; i < numCompiledMethods(); i++) { 255 CompiledMethod cm = getCompiledMethodUnchecked(i); 256 if (cm == null || !cm.isCompiled()) continue; 257 int ct = cm.getCompilerType(); 258 codeCount[ct]++; 259 int size = codeArray.getInstanceSize(cm.numberOfInstructions()); 260 codeBytes[ct] += Memory.alignUp(size, BYTES_IN_ADDRESS); 261 mapBytes[ct] += cm.size(); 262 } 263 VM.sysWriteln("Compiled code space report\n"); 264 265 VM.sysWriteln(" Baseline Compiler"); 266 VM.sysWriteln(" Number of compiled methods = " + codeCount[CompiledMethod.BASELINE]); 267 VM.sysWriteln(" Total size of code (bytes) = " + codeBytes[CompiledMethod.BASELINE]); 268 VM.sysWriteln(" Total size of mapping data (bytes) = " + mapBytes[CompiledMethod.BASELINE]); 269 270 if (codeCount[CompiledMethod.OPT] > 0) { 271 VM.sysWriteln(" Optimizing Compiler"); 272 VM.sysWriteln(" Number of compiled methods = " + codeCount[CompiledMethod.OPT]); 273 VM.sysWriteln(" Total size of code (bytes) = " + codeBytes[CompiledMethod.OPT]); 274 VM.sysWriteln(" Total size of mapping data (bytes) = " + mapBytes[CompiledMethod.OPT]); 275 } 276 277 if (codeCount[CompiledMethod.JNI] > 0) { 278 VM.sysWriteln(" JNI Stub Compiler (Java->C stubs for native methods)"); 279 VM.sysWriteln(" Number of compiled methods = " + codeCount[CompiledMethod.JNI]); 280 VM.sysWriteln(" Total size of code (bytes) = " + codeBytes[CompiledMethod.JNI]); 281 VM.sysWriteln(" Total size of mapping data (bytes) = " + mapBytes[CompiledMethod.JNI]); 282 } 283 if (!VM.runningVM) { 284 TreeMap<String, Integer> packageData = new TreeMap<String, Integer>( 285 new Comparator<String>() { 286 @Override 287 public int compare(String a, String b) { 288 return a.compareTo(b); 289 } 290 }); 291 for (int i = 0; i < numCompiledMethods(); ++i) { 292 CompiledMethod compiledMethod = getCompiledMethodUnchecked(i); 293 if (compiledMethod != null) { 294 RVMMethod m = compiledMethod.getMethod(); 295 if (m != null && compiledMethod.isCompiled()) { 296 String packageName = m.getDeclaringClass().getPackageName(); 297 int numInstructions = compiledMethod.numberOfInstructions(); 298 Integer val = packageData.get(packageName); 299 if (val == null) { 300 val = numInstructions; 301 } else { 302 val = val + numInstructions; 303 } 304 packageData.put(packageName, val); 305 } 306 } 307 } 308 VM.sysWriteln("------------------------------------------------------------------------------------------"); 309 VM.sysWriteln(" Break down of code space usage by package (bytes):"); 310 VM.sysWriteln("------------------------------------------------------------------------------------------"); 311 Set<String> keys = packageData.keySet(); 312 int maxPackageNameSize = 0; 313 for (String packageName : keys) { 314 maxPackageNameSize = Math.max(maxPackageNameSize, packageName.length()); 315 } 316 maxPackageNameSize++; 317 for (String packageName : keys) { 318 VM.sysWriteField(maxPackageNameSize, packageName); 319 VM.sysWriteField(10, packageData.get(packageName)); 320 VM.sysWriteln(); 321 } 322 } 323 } 324}