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