001/* 002 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package jdk.internal.jvmci.meta; 024 025import java.io.*; 026import java.lang.reflect.*; 027import java.util.*; 028 029/** 030 * Miscellaneous collection of utility methods used by {@code jdk.internal.jvmci.meta} and its 031 * clients. 032 */ 033public class MetaUtil { 034 035 private static class ClassInfo { 036 public long totalSize; 037 public long instanceCount; 038 039 @Override 040 public String toString() { 041 return "totalSize=" + totalSize + ", instanceCount=" + instanceCount; 042 } 043 } 044 045 /** 046 * Returns the number of bytes occupied by this constant value or constant object and 047 * recursively all values reachable from this value. 048 * 049 * @param constant the constant whose bytes should be measured 050 * @param printTopN print total size and instance count of the top n classes is desired 051 * @return the number of bytes occupied by this constant 052 */ 053 public static long getMemorySizeRecursive(MetaAccessProvider access, ConstantReflectionProvider constantReflection, JavaConstant constant, PrintStream out, int printTopN) { 054 Set<JavaConstant> marked = new HashSet<>(); 055 Deque<JavaConstant> stack = new ArrayDeque<>(); 056 if (constant.getKind() == Kind.Object && constant.isNonNull()) { 057 marked.add(constant); 058 } 059 final HashMap<ResolvedJavaType, ClassInfo> histogram = new HashMap<>(); 060 stack.push(constant); 061 long sum = 0; 062 while (!stack.isEmpty()) { 063 JavaConstant c = stack.pop(); 064 long memorySize = access.getMemorySize(constant); 065 sum += memorySize; 066 if (c.getKind() == Kind.Object && c.isNonNull()) { 067 ResolvedJavaType clazz = access.lookupJavaType(c); 068 if (!histogram.containsKey(clazz)) { 069 histogram.put(clazz, new ClassInfo()); 070 } 071 ClassInfo info = histogram.get(clazz); 072 info.instanceCount++; 073 info.totalSize += memorySize; 074 ResolvedJavaType type = access.lookupJavaType(c); 075 if (type.isArray()) { 076 if (!type.getComponentType().isPrimitive()) { 077 int length = constantReflection.readArrayLength(c); 078 for (int i = 0; i < length; i++) { 079 JavaConstant value = constantReflection.readArrayElement(c, i); 080 pushConstant(marked, stack, value); 081 } 082 } 083 } else { 084 ResolvedJavaField[] instanceFields = type.getInstanceFields(true); 085 for (ResolvedJavaField f : instanceFields) { 086 if (f.getKind() == Kind.Object) { 087 JavaConstant value = constantReflection.readFieldValue(f, c); 088 pushConstant(marked, stack, value); 089 } 090 } 091 } 092 } 093 } 094 ArrayList<ResolvedJavaType> clazzes = new ArrayList<>(); 095 clazzes.addAll(histogram.keySet()); 096 Collections.sort(clazzes, new Comparator<ResolvedJavaType>() { 097 098 @Override 099 public int compare(ResolvedJavaType o1, ResolvedJavaType o2) { 100 long l1 = histogram.get(o1).totalSize; 101 long l2 = histogram.get(o2).totalSize; 102 if (l1 > l2) { 103 return -1; 104 } else if (l1 == l2) { 105 return 0; 106 } else { 107 return 1; 108 } 109 } 110 }); 111 112 int z = 0; 113 for (ResolvedJavaType c : clazzes) { 114 if (z > printTopN) { 115 break; 116 } 117 out.println("Class " + c + ", " + histogram.get(c)); 118 ++z; 119 } 120 121 return sum; 122 } 123 124 private static void pushConstant(Set<JavaConstant> marked, Deque<JavaConstant> stack, JavaConstant value) { 125 if (value.isNonNull()) { 126 if (!marked.contains(value)) { 127 marked.add(value); 128 stack.push(value); 129 } 130 } 131 } 132 133 /** 134 * Calls {@link JavaType#resolve(ResolvedJavaType)} on an array of types. 135 */ 136 public static ResolvedJavaType[] resolveJavaTypes(JavaType[] types, ResolvedJavaType accessingClass) { 137 ResolvedJavaType[] result = new ResolvedJavaType[types.length]; 138 for (int i = 0; i < result.length; i++) { 139 result[i] = types[i].resolve(accessingClass); 140 } 141 return result; 142 } 143 144 /** 145 * Extends the functionality of {@link Class#getSimpleName()} to include a non-empty string for 146 * anonymous and local classes. 147 * 148 * @param clazz the class for which the simple name is being requested 149 * @param withEnclosingClass specifies if the returned name should be qualified with the name(s) 150 * of the enclosing class/classes of {@code clazz} (if any). This option is ignored 151 * if {@code clazz} denotes an anonymous or local class. 152 * @return the simple name 153 */ 154 public static String getSimpleName(Class<?> clazz, boolean withEnclosingClass) { 155 final String simpleName = clazz.getSimpleName(); 156 if (simpleName.length() != 0) { 157 if (withEnclosingClass) { 158 String prefix = ""; 159 Class<?> enclosingClass = clazz; 160 while ((enclosingClass = enclosingClass.getEnclosingClass()) != null) { 161 prefix = enclosingClass.getSimpleName() + "." + prefix; 162 } 163 return prefix + simpleName; 164 } 165 return simpleName; 166 } 167 // Must be an anonymous or local class 168 final String name = clazz.getName(); 169 int index = name.indexOf('$'); 170 if (index == -1) { 171 return name; 172 } 173 index = name.lastIndexOf('.', index); 174 if (index == -1) { 175 return name; 176 } 177 return name.substring(index + 1); 178 } 179 180 static String internalNameToJava(String name, boolean qualified, boolean classForNameCompatible) { 181 switch (name.charAt(0)) { 182 case 'L': { 183 String result = name.substring(1, name.length() - 1).replace('/', '.'); 184 if (!qualified) { 185 final int lastDot = result.lastIndexOf('.'); 186 if (lastDot != -1) { 187 result = result.substring(lastDot + 1); 188 } 189 } 190 return result; 191 } 192 case '[': 193 return classForNameCompatible ? name.replace('/', '.') : internalNameToJava(name.substring(1), qualified, classForNameCompatible) + "[]"; 194 default: 195 if (name.length() != 1) { 196 throw new IllegalArgumentException("Illegal internal name: " + name); 197 } 198 return Kind.fromPrimitiveOrVoidTypeChar(name.charAt(0)).getJavaName(); 199 } 200 } 201 202 /** 203 * Turns an class name in internal format into a resolved Java type. 204 */ 205 public static ResolvedJavaType classForName(String internal, MetaAccessProvider metaAccess, ClassLoader cl) { 206 Kind k = Kind.fromTypeString(internal); 207 try { 208 String n = internalNameToJava(internal, true, true); 209 return metaAccess.lookupJavaType(k.isPrimitive() ? k.toJavaClass() : Class.forName(n, true, cl)); 210 } catch (ClassNotFoundException cnfe) { 211 throw new IllegalArgumentException("could not instantiate class described by " + internal, cnfe); 212 } 213 } 214 215 /** 216 * Convenient shortcut for calling 217 * {@link #appendLocation(StringBuilder, ResolvedJavaMethod, int)} without having to supply a 218 * {@link StringBuilder} instance and convert the result to a string. 219 */ 220 public static String toLocation(ResolvedJavaMethod method, int bci) { 221 return appendLocation(new StringBuilder(), method, bci).toString(); 222 } 223 224 /** 225 * Appends a string representation of a location specified by a given method and bci to a given 226 * {@link StringBuilder}. If a stack trace element with a non-null file name and non-negative 227 * line number is {@linkplain ResolvedJavaMethod#asStackTraceElement(int) available} for the 228 * given method, then the string returned is the {@link StackTraceElement#toString()} value of 229 * the stack trace element, suffixed by the bci location. For example: 230 * 231 * <pre> 232 * java.lang.String.valueOf(String.java:2930) [bci: 12] 233 * </pre> 234 * 235 * Otherwise, the string returned is the value of applying {@link JavaMethod#format(String)} 236 * with the format string {@code "%H.%n(%p)"}, suffixed by the bci location. For example: 237 * 238 * <pre> 239 * java.lang.String.valueOf(int) [bci: 12] 240 * </pre> 241 * 242 * @param sb 243 * @param method 244 * @param bci 245 */ 246 public static StringBuilder appendLocation(StringBuilder sb, ResolvedJavaMethod method, int bci) { 247 if (method != null) { 248 StackTraceElement ste = method.asStackTraceElement(bci); 249 if (ste.getFileName() != null && ste.getLineNumber() > 0) { 250 sb.append(ste); 251 } else { 252 sb.append(method.format("%H.%n(%p)")); 253 } 254 } else { 255 sb.append("Null method"); 256 } 257 return sb.append(" [bci: ").append(bci).append(']'); 258 } 259 260 static void appendProfile(StringBuilder buf, AbstractJavaProfile<?, ?> profile, int bci, String type, String sep) { 261 if (profile != null) { 262 AbstractProfiledItem<?>[] pitems = profile.getItems(); 263 if (pitems != null) { 264 buf.append(String.format("%s@%d:", type, bci)); 265 for (int j = 0; j < pitems.length; j++) { 266 AbstractProfiledItem<?> pitem = pitems[j]; 267 buf.append(String.format(" %.6f (%s)%s", pitem.getProbability(), pitem.getItem(), sep)); 268 } 269 if (profile.getNotRecordedProbability() != 0) { 270 buf.append(String.format(" %.6f <other %s>%s", profile.getNotRecordedProbability(), type, sep)); 271 } else { 272 buf.append(String.format(" <no other %s>%s", type, sep)); 273 } 274 } 275 } 276 } 277 278 /** 279 * Converts a Java source-language class name into the internal form. 280 * 281 * @param className the class name 282 * @return the internal name form of the class name 283 */ 284 public static String toInternalName(String className) { 285 if (className.startsWith("[")) { 286 /* Already in the correct array style. */ 287 return className.replace('.', '/'); 288 } 289 290 StringBuilder result = new StringBuilder(); 291 String base = className; 292 while (base.endsWith("[]")) { 293 result.append("["); 294 base = base.substring(0, base.length() - 2); 295 } 296 297 switch (base) { 298 case "boolean": 299 result.append("Z"); 300 break; 301 case "byte": 302 result.append("B"); 303 break; 304 case "short": 305 result.append("S"); 306 break; 307 case "char": 308 result.append("C"); 309 break; 310 case "int": 311 result.append("I"); 312 break; 313 case "float": 314 result.append("F"); 315 break; 316 case "long": 317 result.append("J"); 318 break; 319 case "double": 320 result.append("D"); 321 break; 322 case "void": 323 result.append("V"); 324 break; 325 default: 326 result.append("L").append(base.replace('.', '/')).append(";"); 327 break; 328 } 329 return result.toString(); 330 } 331 332 /** 333 * Prepends the String {@code indentation} to every line in String {@code lines}, including a 334 * possibly non-empty line following the final newline. 335 */ 336 public static String indent(String lines, String indentation) { 337 if (lines.length() == 0) { 338 return lines; 339 } 340 final String newLine = "\n"; 341 if (lines.endsWith(newLine)) { 342 return indentation + (lines.substring(0, lines.length() - 1)).replace(newLine, newLine + indentation) + newLine; 343 } 344 return indentation + lines.replace(newLine, newLine + indentation); 345 } 346 347 /** 348 * Gets a string representation of an object based soley on its class and its 349 * {@linkplain System#identityHashCode(Object) identity hash code}. This avoids and calls to 350 * virtual methods on the object such as {@link Object#hashCode()}. 351 */ 352 public static String identityHashCodeString(Object obj) { 353 if (obj == null) { 354 return "null"; 355 } 356 return obj.getClass().getName() + "@" + System.identityHashCode(obj); 357 } 358 359 /** 360 * Used to lookup constants from {@link Modifier} that are not public (VARARGS, SYNTHETIC etc.). 361 */ 362 static int getNonPublicModifierStaticField(String name) { 363 try { 364 Field field = Modifier.class.getDeclaredField(name); 365 field.setAccessible(true); 366 return field.getInt(null); 367 } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { 368 throw new InternalError(e); 369 } 370 } 371}