1 /* 2 * Copyright (c) 1997, 2017, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 27 /* 28 * The Original Code is HAT. The Initial Developer of the 29 * Original Code is Bill Foote, with contributions from others 30 * at JavaSoft/Sun. 31 */ 32 33 package jdk.test.lib.hprof.model; 34 35 import java.lang.ref.SoftReference; 36 import java.util.*; 37 38 import jdk.test.lib.hprof.parser.ReadBuffer; 39 import jdk.test.lib.hprof.util.Misc; 40 41 /** 42 * 43 * @author Bill Foote 44 */ 45 46 /** 47 * Represents a snapshot of the Java objects in the VM at one instant. 48 * This is the top-level "model" object read out of a single .hprof or .bod 49 * file. 50 */ 51 52 public class Snapshot implements AutoCloseable { 53 54 public static final long SMALL_ID_MASK = 0x0FFFFFFFFL; 55 public static final JavaThing[] EMPTY_JAVATHING_ARRAY = new JavaThing[0]; 56 57 private static final JavaField[] EMPTY_FIELD_ARRAY = new JavaField[0]; 58 private static final JavaStatic[] EMPTY_STATIC_ARRAY = new JavaStatic[0]; 59 60 // all heap objects 61 private Hashtable<Number, JavaHeapObject> heapObjects = 62 new Hashtable<Number, JavaHeapObject>(); 63 64 private Hashtable<Number, JavaClass> fakeClasses = 65 new Hashtable<Number, JavaClass>(); 66 67 // all Roots in this Snapshot 68 private Vector<Root> roots = new Vector<Root>(); 69 70 // name-to-class map 71 private Map<String, JavaClass> classes = 72 new TreeMap<String, JavaClass>(); 73 74 // new objects relative to a baseline - lazily initialized 75 private volatile Map<JavaHeapObject, Boolean> newObjects; 76 77 // allocation site traces for all objects - lazily initialized 78 private volatile Map<JavaHeapObject, StackTrace> siteTraces; 79 80 // object-to-Root map for all objects 81 private Map<JavaHeapObject, Root> rootsMap = 82 new HashMap<JavaHeapObject, Root>(); 83 84 // soft cache of finalizeable objects - lazily initialized 85 private SoftReference<Vector<?>> finalizablesCache; 86 87 // represents null reference 88 private JavaThing nullThing; 89 90 // java.lang.ref.Reference class 91 private JavaClass weakReferenceClass; 92 // index of 'referent' field in java.lang.ref.Reference class 93 private int referentFieldIndex; 94 95 // java.lang.Class class 96 private JavaClass javaLangClass; 97 // java.lang.String class 98 private JavaClass javaLangString; 99 // java.lang.ClassLoader class 100 private JavaClass javaLangClassLoader; 101 102 // unknown "other" array class 103 private volatile JavaClass otherArrayType; 104 // Stuff to exclude from reachable query 105 private ReachableExcludes reachableExcludes; 106 // the underlying heap dump buffer 107 private ReadBuffer readBuf; 108 109 // True iff some heap objects have isNew set 110 private boolean hasNewSet; 111 private boolean unresolvedObjectsOK; 112 113 // whether object array instances have new style class or 114 // old style (element) class. 115 private boolean newStyleArrayClass; 116 117 // object id size in the heap dump 118 private int identifierSize = 4; 119 120 // minimum object size - accounts for object header in 121 // most Java virtual machines - we assume 2 identifierSize 122 // (which is true for Sun's hotspot JVM). 123 private int minimumObjectSize; 124 125 public Snapshot(ReadBuffer buf) { 126 nullThing = new HackJavaValue("<null>", 0); 127 readBuf = buf; 128 } 129 130 public void setSiteTrace(JavaHeapObject obj, StackTrace trace) { 131 if (trace != null && trace.getFrames().length != 0) { 132 initSiteTraces(); 133 siteTraces.put(obj, trace); 134 } 135 } 136 137 public StackTrace getSiteTrace(JavaHeapObject obj) { 138 if (siteTraces != null) { 139 return siteTraces.get(obj); 140 } else { 141 return null; 142 } 143 } 144 145 public void setNewStyleArrayClass(boolean value) { 146 newStyleArrayClass = value; 147 } 148 149 public boolean isNewStyleArrayClass() { 150 return newStyleArrayClass; 151 } 152 153 public void setIdentifierSize(int size) { 154 identifierSize = size; 155 minimumObjectSize = 2 * size; 156 } 157 158 public int getIdentifierSize() { 159 return identifierSize; 160 } 161 162 public int getMinimumObjectSize() { 163 return minimumObjectSize; 164 } 165 166 public void addHeapObject(long id, JavaHeapObject ho) { 167 heapObjects.put(makeId(id), ho); 168 } 169 170 public void addRoot(Root r) { 171 r.setIndex(roots.size()); 172 roots.addElement(r); 173 } 174 175 public void addClass(long id, JavaClass c) { 176 addHeapObject(id, c); 177 putInClassesMap(c); 178 } 179 180 JavaClass addFakeInstanceClass(long classID, int instSize) { 181 // Create a fake class name based on ID. 182 String name = "unknown-class<@" + Misc.toHex(classID) + ">"; 183 184 // Create fake fields convering the given instance size. 185 // Create as many as int type fields and for the left over 186 // size create byte type fields. 187 int numInts = instSize / 4; 188 int numBytes = instSize % 4; 189 JavaField[] fields = new JavaField[numInts + numBytes]; 190 int i; 191 for (i = 0; i < numInts; i++) { 192 fields[i] = new JavaField("unknown-field-" + i, "I"); 193 } 194 for (i = 0; i < numBytes; i++) { 195 fields[i + numInts] = new JavaField("unknown-field-" + 196 i + numInts, "B"); 197 } 198 199 // Create fake instance class 200 JavaClass c = new JavaClass(name, 0, 0, 0, 0, fields, 201 EMPTY_STATIC_ARRAY, instSize); 202 // Add the class 203 addFakeClass(makeId(classID), c); 204 return c; 205 } 206 207 208 /** 209 * @return true iff it's possible that some JavaThing instances might 210 * isNew set 211 * 212 * @see JavaThing.isNew() 213 */ 214 public boolean getHasNewSet() { 215 return hasNewSet; 216 } 217 218 // 219 // Used in the body of resolve() 220 // 221 private static class MyVisitor extends AbstractJavaHeapObjectVisitor { 222 JavaHeapObject t; 223 public void visit(JavaHeapObject other) { 224 other.addReferenceFrom(t); 225 } 226 } 227 228 // To show heap parsing progress, we print a '.' after this limit 229 private static final int DOT_LIMIT = 5000; 230 231 /** 232 * Called after reading complete, to initialize the structure 233 */ 234 public void resolve(boolean calculateRefs) { 235 System.out.println("Resolving " + heapObjects.size() + " objects..."); 236 237 // First, resolve the classes. All classes must be resolved before 238 // we try any objects, because the objects use classes in their 239 // resolution. 240 javaLangClass = findClass("java.lang.Class"); 241 if (javaLangClass == null) { 242 System.out.println("WARNING: hprof file does not include java.lang.Class!"); 243 javaLangClass = new JavaClass("java.lang.Class", 0, 0, 0, 0, 244 EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY, 0); 245 addFakeClass(javaLangClass); 246 } 247 javaLangString = findClass("java.lang.String"); 248 if (javaLangString == null) { 249 System.out.println("WARNING: hprof file does not include java.lang.String!"); 250 javaLangString = new JavaClass("java.lang.String", 0, 0, 0, 0, 251 EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY, 0); 252 addFakeClass(javaLangString); 253 } 254 javaLangClassLoader = findClass("java.lang.ClassLoader"); 255 if (javaLangClassLoader == null) { 256 System.out.println("WARNING: hprof file does not include java.lang.ClassLoader!"); 257 javaLangClassLoader = new JavaClass("java.lang.ClassLoader", 0, 0, 0, 0, 258 EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY, 0); 259 addFakeClass(javaLangClassLoader); 260 } 261 262 for (JavaHeapObject t : heapObjects.values()) { 263 if (t instanceof JavaClass) { 264 t.resolve(this); 265 } 266 } 267 268 // Now, resolve everything else. 269 for (JavaHeapObject t : heapObjects.values()) { 270 if (!(t instanceof JavaClass)) { 271 t.resolve(this); 272 } 273 } 274 275 heapObjects.putAll(fakeClasses); 276 fakeClasses.clear(); 277 278 weakReferenceClass = findClass("java.lang.ref.Reference"); 279 referentFieldIndex = 0; 280 if (weakReferenceClass != null) { 281 JavaField[] fields = weakReferenceClass.getFieldsForInstance(); 282 for (int i = 0; i < fields.length; i++) { 283 if ("referent".equals(fields[i].getName())) { 284 referentFieldIndex = i; 285 break; 286 } 287 } 288 } 289 290 if (calculateRefs) { 291 calculateReferencesToObjects(); 292 System.out.print("Eliminating duplicate references"); 293 System.out.flush(); 294 // This println refers to the *next* step 295 } 296 int count = 0; 297 for (JavaHeapObject t : heapObjects.values()) { 298 t.setupReferers(); 299 ++count; 300 if (calculateRefs && count % DOT_LIMIT == 0) { 301 System.out.print("."); 302 System.out.flush(); 303 } 304 } 305 if (calculateRefs) { 306 System.out.println(""); 307 } 308 309 // to ensure that Iterator.remove() on getClasses() 310 // result will throw exception.. 311 classes = Collections.unmodifiableMap(classes); 312 } 313 314 private void calculateReferencesToObjects() { 315 System.out.print("Chasing references, expect " 316 + (heapObjects.size() / DOT_LIMIT) + " dots"); 317 System.out.flush(); 318 int count = 0; 319 MyVisitor visitor = new MyVisitor(); 320 for (JavaHeapObject t : heapObjects.values()) { 321 visitor.t = t; 322 // call addReferenceFrom(t) on all objects t references: 323 t.visitReferencedObjects(visitor); 324 ++count; 325 if (count % DOT_LIMIT == 0) { 326 System.out.print("."); 327 System.out.flush(); 328 } 329 } 330 System.out.println(); 331 for (Root r : roots) { 332 r.resolve(this); 333 JavaHeapObject t = findThing(r.getId()); 334 if (t != null) { 335 t.addReferenceFromRoot(r); 336 } 337 } 338 } 339 340 public void markNewRelativeTo(Snapshot baseline) { 341 hasNewSet = true; 342 for (JavaHeapObject t : heapObjects.values()) { 343 boolean isNew; 344 long thingID = t.getId(); 345 if (thingID == 0L || thingID == -1L) { 346 isNew = false; 347 } else { 348 JavaThing other = baseline.findThing(t.getId()); 349 if (other == null) { 350 isNew = true; 351 } else { 352 isNew = !t.isSameTypeAs(other); 353 } 354 } 355 t.setNew(isNew); 356 } 357 } 358 359 public Enumeration<JavaHeapObject> getThings() { 360 return heapObjects.elements(); 361 } 362 363 364 public JavaHeapObject findThing(long id) { 365 Number idObj = makeId(id); 366 JavaHeapObject jho = heapObjects.get(idObj); 367 return jho != null? jho : fakeClasses.get(idObj); 368 } 369 370 public JavaHeapObject findThing(String id) { 371 return findThing(Misc.parseHex(id)); 372 } 373 374 public JavaClass findClass(String name) { 375 if (name.startsWith("0x")) { 376 return (JavaClass) findThing(name); 377 } else { 378 return classes.get(name); 379 } 380 } 381 382 /** 383 * Return an Iterator of all of the classes in this snapshot. 384 **/ 385 public Iterator<JavaClass> getClasses() { 386 // note that because classes is a TreeMap 387 // classes are already sorted by name 388 return classes.values().iterator(); 389 } 390 391 public JavaClass[] getClassesArray() { 392 JavaClass[] res = new JavaClass[classes.size()]; 393 classes.values().toArray(res); 394 return res; 395 } 396 397 public synchronized Enumeration<?> getFinalizerObjects() { 398 Vector<?> obj; 399 if (finalizablesCache != null && 400 (obj = finalizablesCache.get()) != null) { 401 return obj.elements(); 402 } 403 404 JavaClass clazz = findClass("java.lang.ref.Finalizer"); 405 JavaObject queue = (JavaObject) clazz.getStaticField("queue"); 406 JavaThing tmp = queue.getField("head"); 407 Vector<JavaHeapObject> finalizables = new Vector<JavaHeapObject>(); 408 if (tmp != getNullThing()) { 409 JavaObject head = (JavaObject) tmp; 410 while (true) { 411 JavaHeapObject referent = (JavaHeapObject) head.getField("referent"); 412 JavaThing next = head.getField("next"); 413 if (next == getNullThing() || next.equals(head)) { 414 break; 415 } 416 head = (JavaObject) next; 417 finalizables.add(referent); 418 } 419 } 420 finalizablesCache = new SoftReference<Vector<?>>(finalizables); 421 return finalizables.elements(); 422 } 423 424 public Enumeration<Root> getRoots() { 425 return roots.elements(); 426 } 427 428 public Root[] getRootsArray() { 429 Root[] res = new Root[roots.size()]; 430 roots.toArray(res); 431 return res; 432 } 433 434 public Root getRootAt(int i) { 435 return roots.elementAt(i); 436 } 437 438 public ReferenceChain[] 439 rootsetReferencesTo(JavaHeapObject target, boolean includeWeak) { 440 Vector<ReferenceChain> fifo = new Vector<ReferenceChain>(); // This is slow... A real fifo would help 441 // Must be a fifo to go breadth-first 442 Hashtable<JavaHeapObject, JavaHeapObject> visited = new Hashtable<JavaHeapObject, JavaHeapObject>(); 443 // Objects are added here right after being added to fifo. 444 Vector<ReferenceChain> result = new Vector<ReferenceChain>(); 445 visited.put(target, target); 446 fifo.addElement(new ReferenceChain(target, null)); 447 448 while (fifo.size() > 0) { 449 ReferenceChain chain = fifo.elementAt(0); 450 fifo.removeElementAt(0); 451 JavaHeapObject curr = chain.getObj(); 452 if (curr.getRoot() != null) { 453 result.addElement(chain); 454 // Even though curr is in the rootset, we want to explore its 455 // referers, because they might be more interesting. 456 } 457 Enumeration<JavaThing> referers = curr.getReferers(); 458 while (referers.hasMoreElements()) { 459 JavaHeapObject t = (JavaHeapObject) referers.nextElement(); 460 if (t != null && !visited.containsKey(t)) { 461 if (includeWeak || !t.refersOnlyWeaklyTo(this, curr)) { 462 visited.put(t, t); 463 fifo.addElement(new ReferenceChain(t, chain)); 464 } 465 } 466 } 467 } 468 469 ReferenceChain[] realResult = new ReferenceChain[result.size()]; 470 for (int i = 0; i < result.size(); i++) { 471 realResult[i] = result.elementAt(i); 472 } 473 return realResult; 474 } 475 476 public boolean getUnresolvedObjectsOK() { 477 return unresolvedObjectsOK; 478 } 479 480 public void setUnresolvedObjectsOK(boolean v) { 481 unresolvedObjectsOK = v; 482 } 483 484 public JavaClass getWeakReferenceClass() { 485 return weakReferenceClass; 486 } 487 488 public int getReferentFieldIndex() { 489 return referentFieldIndex; 490 } 491 492 public JavaThing getNullThing() { 493 return nullThing; 494 } 495 496 public void setReachableExcludes(ReachableExcludes e) { 497 reachableExcludes = e; 498 } 499 500 public ReachableExcludes getReachableExcludes() { 501 return reachableExcludes; 502 } 503 504 // package privates 505 void addReferenceFromRoot(Root r, JavaHeapObject obj) { 506 Root root = rootsMap.get(obj); 507 if (root == null) { 508 rootsMap.put(obj, r); 509 } else { 510 rootsMap.put(obj, root.mostInteresting(r)); 511 } 512 } 513 514 Root getRoot(JavaHeapObject obj) { 515 return rootsMap.get(obj); 516 } 517 518 JavaClass getJavaLangClass() { 519 return javaLangClass; 520 } 521 522 JavaClass getJavaLangString() { 523 return javaLangString; 524 } 525 526 JavaClass getJavaLangClassLoader() { 527 return javaLangClassLoader; 528 } 529 530 JavaClass getOtherArrayType() { 531 if (otherArrayType == null) { 532 synchronized(this) { 533 if (otherArrayType == null) { 534 addFakeClass(new JavaClass("[<other>", 0, 0, 0, 0, 535 EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY, 536 0)); 537 otherArrayType = findClass("[<other>"); 538 } 539 } 540 } 541 return otherArrayType; 542 } 543 544 JavaClass getArrayClass(String elementSignature) { 545 JavaClass clazz; 546 synchronized(classes) { 547 clazz = findClass("[" + elementSignature); 548 if (clazz == null) { 549 clazz = new JavaClass("[" + elementSignature, 0, 0, 0, 0, 550 EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY, 0); 551 addFakeClass(clazz); 552 // This is needed because the JDK only creates Class structures 553 // for array element types, not the arrays themselves. For 554 // analysis, though, we need to pretend that there's a 555 // JavaClass for the array type, too. 556 } 557 } 558 return clazz; 559 } 560 561 ReadBuffer getReadBuffer() { 562 return readBuf; 563 } 564 565 void setNew(JavaHeapObject obj, boolean isNew) { 566 initNewObjects(); 567 if (isNew) { 568 newObjects.put(obj, Boolean.TRUE); 569 } 570 } 571 572 boolean isNew(JavaHeapObject obj) { 573 if (newObjects != null) { 574 return newObjects.get(obj) != null; 575 } else { 576 return false; 577 } 578 } 579 580 // Internals only below this point 581 private Number makeId(long id) { 582 if (identifierSize == 4) { 583 return (int)id; 584 } else { 585 return id; 586 } 587 } 588 589 private void putInClassesMap(JavaClass c) { 590 String name = c.getName(); 591 if (classes.containsKey(name)) { 592 // more than one class can have the same name 593 // if so, create a unique name by appending 594 // - and id string to it. 595 name += "-" + c.getIdString(); 596 } 597 classes.put(c.getName(), c); 598 } 599 600 private void addFakeClass(JavaClass c) { 601 putInClassesMap(c); 602 c.resolve(this); 603 } 604 605 private void addFakeClass(Number id, JavaClass c) { 606 fakeClasses.put(id, c); 607 addFakeClass(c); 608 } 609 610 private synchronized void initNewObjects() { 611 if (newObjects == null) { 612 synchronized (this) { 613 if (newObjects == null) { 614 newObjects = new HashMap<JavaHeapObject, Boolean>(); 615 } 616 } 617 } 618 } 619 620 private synchronized void initSiteTraces() { 621 if (siteTraces == null) { 622 synchronized (this) { 623 if (siteTraces == null) { 624 siteTraces = new HashMap<JavaHeapObject, StackTrace>(); 625 } 626 } 627 } 628 } 629 630 @Override 631 public void close() throws Exception { 632 readBuf.close(); 633 } 634 635 }