1 /* MemoryAnalyze.java -- Analyzes a libgcj heap dump. 2 Copyright (C) 2007 Free Software Foundation 3 4 This file is part of libgcj. 5 6 This software is copyrighted work licensed under the terms of the 7 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for 8 details. */ 9 10 package gnu.gcj.tools.gc_analyze; 11 12 import gnu.classpath.tools.getopt.FileArgumentCallback; 13 import gnu.classpath.tools.getopt.Option; 14 import gnu.classpath.tools.getopt.OptionException; 15 import gnu.classpath.tools.getopt.Parser; 16 17 import java.io.BufferedReader; 18 import java.io.FileInputStream; 19 import java.io.IOException; 20 import java.io.InputStreamReader; 21 import java.text.NumberFormat; 22 import java.util.ArrayList; 23 import java.util.Collections; 24 import java.util.Comparator; 25 import java.util.HashMap; 26 import java.util.Iterator; 27 import java.util.Map; 28 29 class MemoryAnalyze 30 { MemoryAnalyze()31 public MemoryAnalyze() 32 { 33 } 34 35 private static NumberFormat numberFormat; 36 private static boolean verbose; format(long number, int digits)37 static String format(long number, int digits) 38 { 39 if (numberFormat == null) 40 { 41 numberFormat = NumberFormat.getNumberInstance(); 42 numberFormat.setGroupingUsed(true); 43 } 44 String temp = numberFormat.format(number); 45 int spaces = digits - temp.length(); 46 if (spaces < 0) 47 spaces = 0; 48 return " ".substring(0,spaces) + temp; 49 } 50 sorted_report(String description, int total_space, ArrayList<String> list, Comparator<String> comparator)51 static void sorted_report(String description, 52 int total_space, 53 ArrayList<String> list, 54 Comparator<String> comparator) 55 { 56 System.out.println("*** " + description + " ***"); 57 System.out.println(); 58 System.out.println(" Total Size Count Size Description"); 59 System.out.println("-------------- ----- -------- -----------------------------------"); 60 Collections.sort(list, comparator); 61 for (Iterator it = list.iterator(); it.hasNext(); ) 62 { 63 String v = (String)it.next(); 64 System.out.println(stripend(v)); 65 } 66 System.out.println("-------------- ----- -------- -----------------------------------"); 67 System.out.println(format(total_space, 14)); 68 System.out.println(); 69 System.out.println(); 70 } 71 stripend(String s)72 private static String stripend(String s) 73 { 74 int n = s.lastIndexOf(" /"); 75 if (n > 0) 76 return s.substring(0,n); 77 return s; 78 } 79 80 static class SubstringComparator implements Comparator<String> 81 { 82 private int begin, end; 83 private boolean reverse; 84 SubstringComparator(int begin, int end, boolean reverse)85 SubstringComparator(int begin, int end, boolean reverse) 86 { 87 this.begin = begin; 88 this.end = end; 89 this.reverse = reverse; 90 } 91 compare(String s1, String s2)92 public int compare(String s1, String s2) 93 { 94 if (end == 0) 95 s1 = s1.substring(begin); 96 else 97 s1 = s1.substring(begin, end); 98 99 if (end == 0) 100 s2 = s2.substring(begin); 101 else 102 s2 = s2.substring(begin, end); 103 int i = s1.compareTo(s2); 104 if (reverse) 105 return -i; 106 return i; 107 } 108 } 109 110 static class OptionParser extends Parser 111 { 112 int filesFound; 113 OptionParser()114 OptionParser() 115 { 116 super("gc-analyze", 117 "gc-analyze (" + System.getProperty("java.vm.version") + ")"); 118 119 add(new Option('d', 120 "Directory containing runtime objects", 121 "directory") 122 { 123 public void parsed(String argument) throws OptionException 124 { 125 ToolPrefix.pathPrefix = argument; 126 } 127 }); 128 129 add(new Option('p', 130 "Binary tool prefix, prepended to nm and readelf to " 131 + "obtain target specific versions of these commands", 132 "prefix") 133 { 134 public void parsed(String argument) throws OptionException 135 { 136 ToolPrefix.toolPrefix = argument; 137 } 138 }); 139 140 add(new Option("verbose", 'v', 141 "Verbose output; requires filename.bytes") 142 { 143 public void parsed(String argument) throws OptionException 144 { 145 verbose = true; 146 } 147 }); 148 149 setHeader("usage: gc-analyze [-v] [-p tool-prefix] [-d <directory>] " 150 + "filename"); 151 } 152 validate()153 protected void validate() throws OptionException 154 { 155 if (filesFound != 1) 156 throw new OptionException("Must specify exactly one filename"); 157 } 158 parse(String[] inArgs)159 public String[] parse(String[] inArgs) 160 { 161 final ArrayList<String> fileResult = new ArrayList<String>(); 162 parse(inArgs, new FileArgumentCallback() 163 { 164 public void notifyFile(String fileArgument) 165 { 166 filesFound++; 167 fileResult.add(fileArgument); 168 } 169 }); 170 return fileResult.toArray(new String[1]); 171 } 172 } 173 main(String[] args)174 public static void main(String[] args) 175 { 176 class Info 177 { 178 int size; 179 int count; 180 } 181 int total_space = 0; 182 183 Parser optionParser = new OptionParser(); 184 185 String rest[] = optionParser.parse(args); 186 187 String filename = rest[0]; 188 189 try 190 { 191 BufferedReader reader = 192 new BufferedReader(new InputStreamReader(new FileInputStream(filename))); 193 SymbolLookup lookup = new SymbolLookup(reader, filename + ".bytes"); 194 ObjectMap objectMap = new ObjectMap(reader); 195 BlockMap blockMap = new BlockMap(reader); 196 reader.close(); 197 198 // add info to item(s) 199 // add item.klass 200 for (Map.Entry<Long, ObjectMap.ObjectItem> me : objectMap) 201 { 202 ObjectMap.ObjectItem item = me.getValue(); 203 204 // try to get a klass (happens with intern'ed strings...) 205 if (item.klass==0) 206 { 207 BytePtr p = lookup.getBytePtr(item.ptr, item.size); 208 if (p!=null) 209 { 210 long vtable = p.getWord(0); 211 String sym = 212 lookup.getSymbolViaVtable(vtable - 2 * lookup.memoryMap.wordSize); 213 if (sym != null) 214 { 215 item.typeName = SymbolTable.demangleVTName(sym); 216 } 217 else if (vtable != 0) 218 { 219 // get klass from vtable 220 p = lookup.getBytePtr(vtable, 221 lookup.memoryMap.wordSize); 222 if (p != null) 223 { 224 long klass = p.getWord(0); 225 item.klass = klass; 226 } 227 } 228 } 229 } 230 231 // figure out strings 232 String class_name; 233 if (null == item.typeName) 234 { 235 class_name = 236 MemoryAnalyze.getSymbolPretty(lookup, item, false); 237 item.typeName = class_name; 238 } 239 else 240 { 241 class_name = item.typeName; 242 } 243 System.out.print("class_name=[" + class_name + "]"); 244 245 if (class_name.compareTo("_ZTVN4java4lang6StringE")==0 246 || class_name.compareTo("java.lang.String")==0) 247 { 248 BytePtr p = lookup.getBytePtr(item.ptr, item.size); 249 long data = p.getWord(1); 250 int boffset = p.getInt(2 * p.intsPerWord()); 251 int count = p.getInt(1 + 2 * p.intsPerWord()); 252 int hash = p.getInt(2 + 2 * p.intsPerWord()); 253 BytePtr chars = lookup.getBytePtr(data+boffset, count * 2); 254 StringBuffer sb = new StringBuffer(count); 255 for (int qq = 0; qq<count; qq++) 256 sb.append((char)chars.getShort(qq)); 257 int newhash = sb.toString().hashCode(); 258 if (newhash!=hash) 259 { 260 p.setInt(4, newhash); 261 } 262 263 item.string = sb.toString(); 264 System.out.println(" value = \"" + item.string + "\""); 265 if (data != item.ptr) 266 { 267 ObjectMap.ObjectItem next = objectMap.get(data); 268 if (next != null) 269 next.stringData = true; 270 else 271 System.out.println("String [" + item.string + "] at " 272 + Long.toHexString(item.ptr) 273 + " can't find array at " 274 + Long.toHexString(data)); 275 } 276 } 277 else if (null != item.string) 278 System.out.println(" value = \"" + item.string + "\""); 279 else 280 System.out.println(); 281 } 282 283 284 HashMap<String, Info> map = new HashMap<String, Info>(); 285 for (Map.Entry<Long, ObjectMap.ObjectItem> me : objectMap) 286 { 287 ObjectMap.ObjectItem item = me.getValue(); 288 String name = getSymbolPretty(lookup, item, true); 289 Info info = map.get(name); 290 if (info == null) 291 { 292 info = new Info(); 293 info.count = 0; 294 info.size = item.size; 295 map.put(name, info); 296 } 297 info.count++; 298 total_space += item.size; 299 } 300 301 ArrayList<String> list = new ArrayList<String>(); 302 for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) 303 { 304 Map.Entry me = (Map.Entry)it.next(); 305 String name = (String)me.getKey(); 306 Info info = (Info)me.getValue(); 307 308 StringBuffer sb = new StringBuffer(); 309 sb.append(format(info.count * info.size * 100 / total_space, 310 3)); 311 sb.append("%"); 312 sb.append(format(info.count * info.size, 10)); 313 sb.append(" = "); 314 sb.append(format(info.count, 7)); 315 sb.append(" * "); 316 sb.append(format(info.size, 9)); 317 sb.append(" - "); 318 sb.append(name); 319 list.add(sb.toString()); 320 } 321 322 sorted_report("Memory Usage Sorted by Total Size", 323 total_space, list, new SubstringComparator(5,14,true)); 324 sorted_report("Memory Usage Sorted by Description", 325 total_space, list, new SubstringComparator(39,0,false)); 326 sorted_report("Memory Usage Sorted by Count", 327 total_space, list, new SubstringComparator(17,25,true)); 328 sorted_report("Memory Usage Sorted by Size", 329 total_space, list, new SubstringComparator(28,37,true)); 330 331 blockMap.dump(); 332 333 // dump raw memory 334 if (verbose) 335 { 336 // analyze references 337 for (Map.Entry<Long, ObjectMap.ObjectItem> me : objectMap) 338 { 339 long ptr = me.getKey(); 340 ObjectMap.ObjectItem item = me.getValue(); 341 BytePtr p = lookup.getBytePtr(ptr, item.size); 342 if (p == null) 343 System.out.println("can't find ptr 0x" 344 + Long.toHexString(ptr)); 345 else if (item.kind != 0) // not GC_PTRFREE 346 for (int i = 1; 347 i < item.size / lookup.memoryMap.wordSize; i++) 348 { 349 long maybe_ptr = p.getWord(i); 350 ObjectMap.ObjectItem item2 = objectMap.get(maybe_ptr); 351 if (item2 != null) 352 { 353 item2.pointed_by.add(item); 354 item.points_to.add(item2); 355 } 356 } 357 } 358 System.out.println(); 359 System.out.println("*** All Objects ***"); 360 System.out.println(); 361 362 for (Map.Entry<Long, ObjectMap.ObjectItem> me : objectMap) 363 { 364 long ptr = me.getKey(); 365 ObjectMap.ObjectItem item = me.getValue(); 366 String name = getSymbolPretty(lookup, item, false); 367 System.out.print("0x" + Long.toHexString(ptr) + " - " + name 368 + " (" + item.size + ")"); 369 if (item.string != null) 370 System.out.println(" \"" + item.string + "\""); 371 else 372 System.out.println(); 373 374 BytePtr p = lookup.getBytePtr(ptr, item.size); 375 376 if (p == null) 377 System.out.println( 378 "can't find memory; recently allocated from free list?"); 379 else 380 p.dump(); 381 382 item.points_to.dump(" points to:", lookup); 383 item.pointed_by.dump(" pointed to by:", lookup); 384 System.out.println(); 385 } 386 } 387 } 388 catch (IOException e) 389 { 390 e.printStackTrace(); 391 } 392 } 393 kindToName(int kind)394 public static String kindToName(int kind) 395 { 396 String name; 397 switch (kind) 398 { 399 case 0: 400 name = "GC_PTRFREE"; 401 break; 402 case 1: 403 name = "GC_NORMAL"; 404 break; 405 case 2: 406 name = "GC_UNCOLLECTABLE"; 407 break; 408 case 3: 409 name = "GC_AUUNCOLLCTABLE"; 410 break; 411 case 4: 412 name = "(Java)"; 413 break; 414 case 5: 415 name = "(Java Debug)"; 416 break; 417 case 6: 418 name = "(Java Array)"; 419 break; 420 default: 421 name = "(Kind " + kind + ")"; 422 break; 423 } 424 return name; 425 } 426 getSymbolPretty(SymbolLookup lookup, ObjectMap.ObjectItem item, boolean bsize)427 public static String getSymbolPretty(SymbolLookup lookup, 428 ObjectMap.ObjectItem item, 429 boolean bsize) 430 throws IOException 431 { 432 433 String name = item.typeName; 434 435 if (name == null) 436 name = lookup.getSymbol(item.klass); 437 438 if (name == null) 439 { 440 String v = lookup.decodeUTF8(item.ptr, item.size); 441 if (null != v) 442 { 443 name = "UTF8Const"; 444 item.string = v; 445 } 446 } 447 448 if (name == null) 449 { 450 name = kindToName(item.kind); 451 } 452 if (item.kind==6) 453 name += "[" + format(item.data, 0) + "]"; 454 if (bsize) 455 name = name + " / " + format(item.size, 7); 456 return name; 457 } 458 } 459