1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ahat.heapdump;
18 
19 import com.android.ahat.dominators.DominatorsComputation;
20 import java.awt.image.BufferedImage;
21 import java.util.ArrayDeque;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Deque;
26 import java.util.List;
27 import java.util.Queue;
28 
29 /**
30  * A Java instance from a parsed heap dump. It is the base class used for all
31  * kinds of Java instances, including normal Java objects, class objects, and
32  * arrays.
33  */
34 public abstract class AhatInstance implements Diffable<AhatInstance>,
35                                               DominatorsComputation.Node {
36   // The id of this instance from the heap dump.
37   private final long mId;
38 
39   // Fields initialized in initialize().
40   private AhatHeap mHeap;
41   private AhatClassObj mClassObj;
42   private Site mSite;
43 
44   // Bit vector of the root types of this object.
45   private int mRootTypes;
46 
47   // Field initialized via addRegisterednativeSize.
48   private long mRegisteredNativeSize = 0;
49 
50   // Fields initialized in computeReverseReferences().
51   private AhatInstance mNextInstanceToGcRoot;
52   private String mNextInstanceToGcRootField;
53   private ArrayList<AhatInstance> mHardReverseReferences;
54   private ArrayList<AhatInstance> mSoftReverseReferences;
55 
56   // Fields initialized in DominatorsComputation.computeDominators().
57   // mDominated - the list of instances immediately dominated by this instance.
58   // mRetainedSizes - retained size indexed by heap index.
59   private AhatInstance mImmediateDominator;
60   private List<AhatInstance> mDominated = new ArrayList<AhatInstance>();
61   private Size[] mRetainedSizes;
62 
63   // The baseline instance for purposes of diff.
64   private AhatInstance mBaseline;
65 
66   // temporary user data associated with this instance. This is used for a
67   // couple different purposes:
68   // 1. During parsing of instances, to store temporary field data.
69   // 2. During dominators computation, to store the dominators computation state.
70   private Object mTemporaryUserData;
71 
AhatInstance(long id)72   AhatInstance(long id) {
73     mId = id;
74     mBaseline = this;
75   }
76 
77   /**
78    * Initialize this AhatInstance based on the the given info.
79    */
initialize(AhatHeap heap, Site site, AhatClassObj classObj)80   void initialize(AhatHeap heap, Site site, AhatClassObj classObj) {
81     mHeap = heap;
82     mSite = site;
83     site.addInstance(this);
84     mClassObj = classObj;
85   }
86 
87   /**
88    * Returns a unique identifier for this instance.
89    *
90    * @return id of the instance
91    */
getId()92   public long getId() {
93     return mId;
94   }
95 
96   /**
97    * Returns the number of bytes used for this object in the heap.
98    * The returned size is a shallow size for the object that does not include
99    * sizes of other objects dominated by this object.
100    *
101    * @return the shallow size of the object
102    */
getSize()103   public Size getSize() {
104     return new Size(mClassObj.getInstanceSize() + getExtraJavaSize(), mRegisteredNativeSize);
105   }
106 
107   /**
108    * Returns the number of bytes taken up by this object on the Java heap
109    * beyond the standard instance size as recorded by the class of this
110    * instance.
111    *
112    * For example, class objects will have extra size for static fields and
113    * array objects will have extra size for the array elements.
114    */
getExtraJavaSize()115   abstract long getExtraJavaSize();
116 
117   /**
118    * Returns the number of bytes retained by this object in the given heap.
119    * The returned size includes the shallow size of this object and the size
120    * of all objects directly or indirectly retained by this object. Only those
121    * objects allocated on the given heap are included in the reported size.
122    *
123    * @param heap the heap to get the retained size for
124    * @return the retained size of the object
125    */
getRetainedSize(AhatHeap heap)126   public Size getRetainedSize(AhatHeap heap) {
127     int index = heap.getIndex();
128     if (mRetainedSizes != null && 0 <= index && index < mRetainedSizes.length) {
129       return mRetainedSizes[heap.getIndex()];
130     }
131     return Size.ZERO;
132   }
133 
134   /**
135    * Returns the total number of bytes retained by this object. The returned
136    * size includes the shallow size of this object and the size of all objects
137    * directly or indirectly retained by this object.
138    *
139    * @return the total retained size of the object
140    */
getTotalRetainedSize()141   public Size getTotalRetainedSize() {
142     Size size = Size.ZERO;
143     if (mRetainedSizes != null) {
144       for (int i = 0; i < mRetainedSizes.length; i++) {
145         size = size.plus(mRetainedSizes[i]);
146       }
147     }
148     return size;
149   }
150 
151   /**
152    * Increment the number of registered native bytes tied to this object.
153    */
addRegisteredNativeSize(long size)154   void addRegisteredNativeSize(long size) {
155     mRegisteredNativeSize += size;
156   }
157 
158   /**
159    * Returns true if this object is strongly reachable. An object is strongly
160    * reachable if there exists a path of (strong) references from some root
161    * object to this object.
162    *
163    * @return true if the object is strongly reachable
164    */
isStronglyReachable()165   public boolean isStronglyReachable() {
166     return mImmediateDominator != null;
167   }
168 
169   /**
170    * Returns true if this object is reachable only through a
171    * soft/weak/phantom/finalizer reference. An object is weakly reachable if
172    * it is not strongly reachable but there still exists a path of references
173    * from some root object to this object.  Because the object is not strongly
174    * reachable, any such path must contain a SoftReference, WeakReference,
175    * PhantomReference, or FinalizerReference somewhere along it.
176    * <p>
177    * Unlike a strongly reachable object, a weakly reachable object is allowed
178    * to be garbage collected.
179    *
180    * @return true if the object is weakly reachable
181    */
isWeaklyReachable()182   public boolean isWeaklyReachable() {
183     return !isStronglyReachable() && mNextInstanceToGcRoot != null;
184   }
185 
186   /**
187    * Returns true if this object is completely unreachable. An object is
188    * completely unreachable if there is no path to the object from some root
189    * object, neither through strong nor soft/weak/phantom/finalizer
190    * references.
191    *
192    * @return true if the object is completely unreachable
193    */
isUnreachable()194   public boolean isUnreachable() {
195     return !isStronglyReachable() && !isWeaklyReachable();
196   }
197 
198   /**
199    * Returns the heap that this instance is allocated on.
200    *
201    * @return heap the instance is allocated on
202    */
getHeap()203   public AhatHeap getHeap() {
204     return mHeap;
205   }
206 
207   /**
208    * Returns an iterator over the references this AhatInstance has to other
209    * AhatInstances.
210    */
getReferences()211   abstract Iterable<Reference> getReferences();
212 
213   /**
214    * Returns true if this instance is a GC root.
215    *
216    * @return true if this instance is a GC root.
217    * @see getRootTypes
218    */
isRoot()219   public boolean isRoot() {
220     return mRootTypes != 0;
221   }
222 
223   /**
224    * Marks this instance as being a root of the given type.
225    */
addRootType(RootType type)226   void addRootType(RootType type) {
227     mRootTypes |= type.mask;
228   }
229 
230   /**
231    * Returns a list of the root types of this object.
232    * Returns null if this object is not a root.
233    *
234    * @return list of the objects root types
235    */
getRootTypes()236   public Collection<RootType> getRootTypes() {
237     if (!isRoot()) {
238       return null;
239     }
240 
241     List<RootType> types = new ArrayList<RootType>();
242     for (RootType type : RootType.values()) {
243       if ((mRootTypes & type.mask) != 0) {
244         types.add(type);
245       }
246     }
247     return types;
248   }
249 
250   /**
251    * Returns the immediate dominator of this instance.
252    * Returns null if this is a root instance.
253    *
254    * @return the immediate dominator of this instance
255    */
getImmediateDominator()256   public AhatInstance getImmediateDominator() {
257     return mImmediateDominator;
258   }
259 
260   /**
261    * Returns a list of objects immediately dominated by this instance.
262    *
263    * @return list of immediately dominated objects
264    */
getDominated()265   public List<AhatInstance> getDominated() {
266     return mDominated;
267   }
268 
269   /**
270    * Returns the site where this instance was allocated.
271    *
272    * @return the object's allocation site
273    */
getSite()274   public Site getSite() {
275     return mSite;
276   }
277 
278   /**
279    * Returns true if this instance is a class object
280    *
281    * @return true if this instance is a class object
282    */
isClassObj()283   public boolean isClassObj() {
284     // Overridden by AhatClassObj.
285     return false;
286   }
287 
288   /**
289    * Returns this as an AhatClassObj if this is an AhatClassObj.
290    * Returns null if this is not an AhatClassObj.
291    *
292    * @return this instance as a class object
293    */
asClassObj()294   public AhatClassObj asClassObj() {
295     // Overridden by AhatClassObj.
296     return null;
297   }
298 
299   /**
300    * Returns the class object for this instance.
301    * For example, if this object is an instance of java.lang.String, this
302    * method returns the AhatClassObj for java.lang.String.
303    *
304    * @return the instance's class object
305    */
getClassObj()306   public AhatClassObj getClassObj() {
307     return mClassObj;
308   }
309 
310   /**
311    * Returns the name of the class this object belongs to.
312    * For example, if this object is an instance of java.lang.String, returns
313    * "java.lang.String".
314    *
315    * @return the name of this instance's class
316    */
getClassName()317   public String getClassName() {
318     AhatClassObj classObj = getClassObj();
319     return classObj == null ? "???" : classObj.getName();
320   }
321 
322   /**
323    * Returns true if the given instance is an array instance.
324    *
325    * @return true if the given instance is an array instance
326    */
isArrayInstance()327   public boolean isArrayInstance() {
328     // Overridden by AhatArrayInstance.
329     return false;
330   }
331 
332   /**
333    * Returns this as an AhatArrayInstance if this is an AhatArrayInstance.
334    * Returns null if this is not an AhatArrayInstance.
335    *
336    * @return this instance as an array instance
337    */
asArrayInstance()338   public AhatArrayInstance asArrayInstance() {
339     // Overridden by AhatArrayInstance.
340     return null;
341   }
342 
343   /**
344    * Returns true if this instance is a class instance.
345    *
346    * @return true if this instance is a class instance
347    */
isClassInstance()348   public boolean isClassInstance() {
349     return false;
350   }
351 
352   /**
353    * Returns this as an AhatClassInstance if this is an AhatClassInstance.
354    * Returns null if this is not an AhatClassInstance.
355    *
356    * @return this instance as a class instance
357    */
asClassInstance()358   public AhatClassInstance asClassInstance() {
359     return null;
360   }
361 
362   /**
363    * Returns the <code>referent</code> associated with this instance.
364    * This is only relevant for instances of java.lang.ref.Reference or its
365    * subclasses. Returns null if the instance has no referent associated with
366    * it.
367    *
368    * @return the referent associated with this instance
369    */
getReferent()370   public AhatInstance getReferent() {
371     // Overridden by AhatClassInstance.
372     return null;
373   }
374 
375   /**
376    * Returns a list of objects with (strong) references to this object.
377    *
378    * @return the objects referencing this object
379    */
getHardReverseReferences()380   public List<AhatInstance> getHardReverseReferences() {
381     if (mHardReverseReferences != null) {
382       return mHardReverseReferences;
383     }
384     return Collections.emptyList();
385   }
386 
387   /**
388    * Returns a list of objects with soft/weak/phantom/finalizer references to
389    * this object.
390    *
391    * @return the objects weakly referencing this object
392    */
getSoftReverseReferences()393   public List<AhatInstance> getSoftReverseReferences() {
394     if (mSoftReverseReferences != null) {
395       return mSoftReverseReferences;
396     }
397     return Collections.emptyList();
398   }
399 
400   /**
401    * Returns the value of a field of this instance. Returns null if the field
402    * value is null, the field couldn't be read, or there are multiple fields
403    * with the same name.
404    *
405    * @param fieldName the name of the field to get the value of
406    * @return the field value
407    */
getField(String fieldName)408   public Value getField(String fieldName) {
409     // Overridden by AhatClassInstance.
410     return null;
411   }
412 
413   /**
414    * Reads a reference field of this instance. Returns null if the field value
415    * is null, of primitive type, or if the field couldn't be read. There is no
416    * way using this method to distinguish between a reference field with value
417    * <code>null</code> and an invalid field.
418    *
419    * @param fieldName the name of the reference field to get the value of
420    * @return the reference field value
421    */
getRefField(String fieldName)422   public AhatInstance getRefField(String fieldName) {
423     // Overridden by AhatClassInstance.
424     return null;
425   }
426 
427   /**
428    * Returns the dex location associated with this object. Only applies to
429    * instances of dalvik.system.DexCache. If this is an instance of DexCache,
430    * returns the dex location for that dex cache. Otherwise returns null.
431    * If maxChars is non-negative, the returned location is truncated to
432    * maxChars in length.
433    *
434    * @param maxChars the maximum length of the returned string
435    * @return the dex location associated with this object
436    */
getDexCacheLocation(int maxChars)437   public String getDexCacheLocation(int maxChars) {
438     return null;
439   }
440 
441   /**
442    * Returns the android.graphics.Bitmap instance associated with this object.
443    * Instances of android.graphics.Bitmap return themselves. If this is a
444    * byte[] array containing pixel data for an instance of
445    * android.graphics.Bitmap, that instance of android.graphics.Bitmap is
446    * returned. Otherwise null is returned.
447    *
448    * @return the bitmap instance associated with this object
449    */
getAssociatedBitmapInstance()450   public AhatInstance getAssociatedBitmapInstance() {
451     return null;
452   }
453 
454   /**
455    * Returns the (bounded-length) string associated with this instance.
456    * Applies to instances of java.lang.String, char[], and in some cases
457    * byte[]. Returns null if this object cannot be interpreted as a string.
458    * If maxChars is non-negative, the returned string is truncated to maxChars
459    * characters in length.
460    *
461    * @param maxChars the maximum length of the returned string
462    * @return the string associated with this instance
463    */
asString(int maxChars)464   public String asString(int maxChars) {
465     // By default instances can't be interpreted as a string. This method is
466     // overridden by AhatClassInstance and AhatArrayInstance for those cases
467     // when an instance can be interpreted as a string.
468     return null;
469   }
470 
471   /**
472    * Returns the string associated with this instance. Applies to instances of
473    * java.lang.String, char[], and in some cases byte[]. Returns null if this
474    * object cannot be interpreted as a string.
475    *
476    * @return the string associated with this instance
477    */
asString()478   public String asString() {
479     return asString(-1);
480   }
481 
482   /**
483    * Returns the bitmap pixel data associated with this instance.
484    * This is relevant for instances of android.graphics.Bitmap and byte[].
485    * Returns null if there is no bitmap pixel data associated with the given
486    * instance.
487    *
488    * @return the bitmap pixel data associated with this image
489    */
asBitmap()490   public BufferedImage asBitmap() {
491     return null;
492   }
493 
494   static class RegisteredNativeAllocation {
495     public AhatInstance referent;
496     public long size;
497   };
498 
499   /**
500    * Return the registered native allocation that this instance represents, if
501    * any. This is relevant for instances of sun.misc.Cleaner.
502    */
asRegisteredNativeAllocation()503   RegisteredNativeAllocation asRegisteredNativeAllocation() {
504     return null;
505   }
506 
507   /**
508    * Returns a sample path from a GC root to this instance. The first element
509    * of the returned path is a GC root object. This instance is included as
510    * the last element of the path with an empty field description.
511    * <p>
512    * If the instance is strongly reachable, a path of string references will
513    * be returned. If the instance is weakly reachable, the returned path will
514    * include a soft/weak/phantom/finalizer reference somewhere along it.
515    * Returns null if this instance is not reachable.
516    *
517    * @return sample path from a GC root to this instance
518    * @see PathElement
519    */
getPathFromGcRoot()520   public List<PathElement> getPathFromGcRoot() {
521     if (isUnreachable()) {
522       return null;
523     }
524 
525     List<PathElement> path = new ArrayList<PathElement>();
526 
527     AhatInstance dom = this;
528     for (PathElement elem = new PathElement(this, ""); elem != null;
529         elem = getNextPathElementToGcRoot(elem.instance)) {
530       if (elem.instance.equals(dom)) {
531         elem.isDominator = true;
532         dom = dom.getImmediateDominator();
533       }
534       path.add(elem);
535     }
536     Collections.reverse(path);
537     return path;
538   }
539 
540   /**
541    * Returns the next instance to GC root from this object and a string
542    * description of which field of that object refers to the given instance.
543    * Returns null if the given instance has no next instance to the gc root.
544    */
getNextPathElementToGcRoot(AhatInstance inst)545   private static PathElement getNextPathElementToGcRoot(AhatInstance inst) {
546     if (inst.isRoot()) {
547       return null;
548     }
549     return new PathElement(inst.mNextInstanceToGcRoot, inst.mNextInstanceToGcRootField);
550   }
551 
552   /**
553    * Returns a human-readable identifier for this object.
554    * For class objects, the string is the class name.
555    * For class instances, the string is the class name followed by '@' and the
556    * hex id of the instance.
557    * For array instances, the string is the array type followed by the size in
558    * square brackets, followed by '@' and the hex id of the instance.
559    *
560    * @return human-readable identifier for this object
561    */
toString()562   @Override public abstract String toString();
563 
564   /**
565    * Read the byte[] value from an hprof Instance.
566    * Returns null if the instance is not a byte array.
567    */
asByteArray()568   byte[] asByteArray() {
569     return null;
570   }
571 
setBaseline(AhatInstance baseline)572   void setBaseline(AhatInstance baseline) {
573     mBaseline = baseline;
574   }
575 
getBaseline()576   @Override public AhatInstance getBaseline() {
577     return mBaseline;
578   }
579 
isPlaceHolder()580   @Override public boolean isPlaceHolder() {
581     return false;
582   }
583 
584   /**
585    * Returns a new place holder instance corresponding to this instance.
586    */
newPlaceHolderInstance()587   AhatInstance newPlaceHolderInstance() {
588     return new AhatPlaceHolderInstance(this);
589   }
590 
setTemporaryUserData(Object state)591   void setTemporaryUserData(Object state) {
592     mTemporaryUserData = state;
593   }
594 
getTemporaryUserData()595   Object getTemporaryUserData() {
596     return mTemporaryUserData;
597   }
598 
599   /**
600    * Initialize the reverse reference fields of this instance and all other
601    * instances reachable from it. Initializes the following fields:
602    *   mNextInstanceToGcRoot
603    *   mNextInstanceToGcRootField
604    *   mHardReverseReferences
605    *   mSoftReverseReferences
606    */
computeReverseReferences(SuperRoot root)607   static void computeReverseReferences(SuperRoot root) {
608     // Start by doing a breadth first search through strong references.
609     // Then continue the breadth first search through weak references.
610     Queue<Reference> strong = new ArrayDeque<Reference>();
611     Queue<Reference> weak = new ArrayDeque<Reference>();
612 
613     for (Reference ref : root.getReferences()) {
614       strong.add(ref);
615     }
616 
617     while (!strong.isEmpty()) {
618       Reference ref = strong.poll();
619       assert ref.strong;
620 
621       if (ref.ref.mNextInstanceToGcRoot == null) {
622         // This is the first time we have seen ref.ref.
623         ref.ref.mNextInstanceToGcRoot = ref.src;
624         ref.ref.mNextInstanceToGcRootField = ref.field;
625         ref.ref.mHardReverseReferences = new ArrayList<AhatInstance>();
626 
627         for (Reference childRef : ref.ref.getReferences()) {
628           if (childRef.strong) {
629             strong.add(childRef);
630           } else {
631             weak.add(childRef);
632           }
633         }
634       }
635 
636       // Note: We specifically exclude 'root' from the reverse references
637       // because it is a fake SuperRoot instance not present in the original
638       // heap dump.
639       if (ref.src != root) {
640         ref.ref.mHardReverseReferences.add(ref.src);
641       }
642     }
643 
644     while (!weak.isEmpty()) {
645       Reference ref = weak.poll();
646 
647       if (ref.ref.mNextInstanceToGcRoot == null) {
648         // This is the first time we have seen ref.ref.
649         ref.ref.mNextInstanceToGcRoot = ref.src;
650         ref.ref.mNextInstanceToGcRootField = ref.field;
651         ref.ref.mHardReverseReferences = new ArrayList<AhatInstance>();
652 
653         for (Reference childRef : ref.ref.getReferences()) {
654           weak.add(childRef);
655         }
656       }
657 
658       if (ref.strong) {
659         ref.ref.mHardReverseReferences.add(ref.src);
660       } else {
661         if (ref.ref.mSoftReverseReferences == null) {
662           ref.ref.mSoftReverseReferences = new ArrayList<AhatInstance>();
663         }
664         ref.ref.mSoftReverseReferences.add(ref.src);
665       }
666     }
667   }
668 
669   /**
670    * Recursively compute the retained size of the given instance and all
671    * other instances it dominates.
672    */
computeRetainedSize(AhatInstance inst, int numHeaps)673   static void computeRetainedSize(AhatInstance inst, int numHeaps) {
674     // Note: We can't use a recursive implementation because it can lead to
675     // stack overflow. Use an iterative implementation instead.
676     //
677     // Objects not yet processed will have mRetainedSizes set to null.
678     // Once prepared, an object will have mRetaiedSizes set to an array of 0
679     // sizes.
680     Deque<AhatInstance> deque = new ArrayDeque<AhatInstance>();
681     deque.push(inst);
682 
683     while (!deque.isEmpty()) {
684       inst = deque.pop();
685       if (inst.mRetainedSizes == null) {
686         inst.mRetainedSizes = new Size[numHeaps];
687         for (int i = 0; i < numHeaps; i++) {
688           inst.mRetainedSizes[i] = Size.ZERO;
689         }
690         if (!(inst instanceof SuperRoot)) {
691           inst.mRetainedSizes[inst.mHeap.getIndex()] =
692             inst.mRetainedSizes[inst.mHeap.getIndex()].plus(inst.getSize());
693         }
694         deque.push(inst);
695         for (AhatInstance dominated : inst.mDominated) {
696           deque.push(dominated);
697         }
698       } else {
699         for (AhatInstance dominated : inst.mDominated) {
700           for (int i = 0; i < numHeaps; i++) {
701             inst.mRetainedSizes[i] = inst.mRetainedSizes[i].plus(dominated.mRetainedSizes[i]);
702           }
703         }
704       }
705     }
706   }
707 
708   @Override
setDominatorsComputationState(Object state)709   public void setDominatorsComputationState(Object state) {
710     setTemporaryUserData(state);
711   }
712 
713   @Override
getDominatorsComputationState()714   public Object getDominatorsComputationState() {
715     return getTemporaryUserData();
716   }
717 
718   @Override
getReferencesForDominators()719   public Iterable<? extends DominatorsComputation.Node> getReferencesForDominators() {
720     return new DominatorReferenceIterator(getReferences());
721   }
722 
723   @Override
setDominator(DominatorsComputation.Node dominator)724   public void setDominator(DominatorsComputation.Node dominator) {
725     mImmediateDominator = (AhatInstance)dominator;
726     mImmediateDominator.mDominated.add(this);
727   }
728 }
729