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