Snapshot.java revision 2779:56d1e05e0def
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
33package jdk.test.lib.hprof.model;
34
35import java.lang.ref.SoftReference;
36import java.util.*;
37
38import jdk.test.lib.hprof.parser.ReadBuffer;
39import 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
52public 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}
636