• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.tools.perflib.heap.ClassObj;
20 import com.android.tools.perflib.heap.Instance;
21 import com.android.tools.perflib.heap.RootObj;
22 import java.awt.image.BufferedImage;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.List;
28 
29 public abstract class AhatInstance implements Diffable<AhatInstance> {
30   private long mId;
31   private long mSize;
32   private long mTotalRetainedSize;
33   private long mRetainedSizes[];      // Retained size indexed by heap index
34   private boolean mIsReachable;
35   private AhatHeap mHeap;
36   private AhatInstance mImmediateDominator;
37   private AhatInstance mNextInstanceToGcRoot;
38   private String mNextInstanceToGcRootField = "???";
39   private AhatClassObj mClassObj;
40   private AhatInstance[] mHardReverseReferences;
41   private AhatInstance[] mSoftReverseReferences;
42   private Site mSite;
43 
44   // If this instance is a root, mRootTypes contains a set of the root types.
45   // If this instance is not a root, mRootTypes is null.
46   private List<String> mRootTypes;
47 
48   // List of instances this instance immediately dominates.
49   private List<AhatInstance> mDominated = new ArrayList<AhatInstance>();
50 
51   private AhatInstance mBaseline;
52 
AhatInstance(long id)53   public AhatInstance(long id) {
54     mId = id;
55     mBaseline = this;
56   }
57 
58   /**
59    * Initializes this AhatInstance based on the given perflib instance.
60    * The AhatSnapshot should be used to look up AhatInstances and AhatHeaps.
61    * There is no guarantee that the AhatInstances returned by
62    * snapshot.findInstance have been initialized yet.
63    */
initialize(AhatSnapshot snapshot, Instance inst)64   void initialize(AhatSnapshot snapshot, Instance inst) {
65     mId = inst.getId();
66     mSize = inst.getSize();
67     mTotalRetainedSize = inst.getTotalRetainedSize();
68     mIsReachable = inst.isReachable();
69 
70     List<AhatHeap> heaps = snapshot.getHeaps();
71     mRetainedSizes = new long[heaps.size()];
72     for (AhatHeap heap : heaps) {
73       mRetainedSizes[heap.getIndex()] = inst.getRetainedSize(heap.getIndex());
74     }
75 
76     mHeap = snapshot.getHeap(inst.getHeap().getName());
77 
78     Instance dom = inst.getImmediateDominator();
79     if (dom == null || dom instanceof RootObj) {
80       mImmediateDominator = null;
81     } else {
82       mImmediateDominator = snapshot.findInstance(dom.getId());
83       mImmediateDominator.mDominated.add(this);
84     }
85 
86     ClassObj clsObj = inst.getClassObj();
87     if (clsObj != null) {
88       mClassObj = snapshot.findClassObj(clsObj.getId());
89     }
90 
91     // A couple notes about reverse references:
92     // * perflib sometimes returns unreachable reverse references. If
93     //   snapshot.findInstance returns null, it means the reverse reference is
94     //   not reachable, so we filter it out.
95     // * We store the references as AhatInstance[] instead of
96     //   ArrayList<AhatInstance> because it saves a lot of space and helps
97     //   with performance when there are a lot of AhatInstances.
98     ArrayList<AhatInstance> ahatRefs = new ArrayList<AhatInstance>();
99     ahatRefs = new ArrayList<AhatInstance>();
100     for (Instance ref : inst.getHardReverseReferences()) {
101       AhatInstance ahat = snapshot.findInstance(ref.getId());
102       if (ahat != null) {
103         ahatRefs.add(ahat);
104       }
105     }
106     mHardReverseReferences = new AhatInstance[ahatRefs.size()];
107     ahatRefs.toArray(mHardReverseReferences);
108 
109     List<Instance> refs = inst.getSoftReverseReferences();
110     ahatRefs.clear();
111     if (refs != null) {
112       for (Instance ref : refs) {
113         AhatInstance ahat = snapshot.findInstance(ref.getId());
114         if (ahat != null) {
115           ahatRefs.add(ahat);
116         }
117       }
118     }
119     mSoftReverseReferences = new AhatInstance[ahatRefs.size()];
120     ahatRefs.toArray(mSoftReverseReferences);
121   }
122 
123   /**
124    * Returns a unique identifier for the instance.
125    */
getId()126   public long getId() {
127     return mId;
128   }
129 
130   /**
131    * Returns the shallow number of bytes this object takes up.
132    */
getSize()133   public long getSize() {
134     return mSize;
135   }
136 
137   /**
138    * Returns the number of bytes belonging to the given heap that this instance
139    * retains.
140    */
getRetainedSize(AhatHeap heap)141   public long getRetainedSize(AhatHeap heap) {
142     int index = heap.getIndex();
143     return 0 <= index && index < mRetainedSizes.length ? mRetainedSizes[heap.getIndex()] : 0;
144   }
145 
146   /**
147    * Returns the total number of bytes this instance retains.
148    */
getTotalRetainedSize()149   public long getTotalRetainedSize() {
150     return mTotalRetainedSize;
151   }
152 
153   /**
154    * Returns whether this object is strongly-reachable.
155    */
isReachable()156   public boolean isReachable() {
157     return mIsReachable;
158   }
159 
160   /**
161    * Returns the heap that this instance is allocated on.
162    */
getHeap()163   public AhatHeap getHeap() {
164     return mHeap;
165   }
166 
167   /**
168    * Returns true if this instance is marked as a root instance.
169    */
isRoot()170   public boolean isRoot() {
171     return mRootTypes != null;
172   }
173 
174   /**
175    * Marks this instance as being a root of the given type.
176    */
addRootType(String type)177   void addRootType(String type) {
178     if (mRootTypes == null) {
179       mRootTypes = new ArrayList<String>();
180       mRootTypes.add(type);
181     } else if (!mRootTypes.contains(type)) {
182       mRootTypes.add(type);
183     }
184   }
185 
186   /**
187    * Returns a list of string descriptions of the root types of this object.
188    * Returns null if this object is not a root.
189    */
getRootTypes()190   public Collection<String> getRootTypes() {
191     return mRootTypes;
192   }
193 
194   /**
195    * Returns the immediate dominator of this instance.
196    * Returns null if this is a root instance.
197    */
getImmediateDominator()198   public AhatInstance getImmediateDominator() {
199     return mImmediateDominator;
200   }
201 
202   /**
203    * Returns a list of those objects immediately dominated by the given
204    * instance.
205    */
getDominated()206   public List<AhatInstance> getDominated() {
207     return mDominated;
208   }
209 
210   /**
211    * Returns the site where this instance was allocated.
212    */
getSite()213   public Site getSite() {
214     return mSite;
215   }
216 
217   /**
218    * Sets the allocation site of this instance.
219    */
setSite(Site site)220   void setSite(Site site) {
221     mSite = site;
222   }
223 
224   /**
225    * Returns true if the given instance is a class object
226    */
isClassObj()227   public boolean isClassObj() {
228     // Overridden by AhatClassObj.
229     return false;
230   }
231 
232   /**
233    * Returns this as an AhatClassObj if this is an AhatClassObj.
234    * Returns null if this is not an AhatClassObj.
235    */
asClassObj()236   public AhatClassObj asClassObj() {
237     // Overridden by AhatClassObj.
238     return null;
239   }
240 
241   /**
242    * Returns the class object instance for the class of this object.
243    */
getClassObj()244   public AhatClassObj getClassObj() {
245     return mClassObj;
246   }
247 
248   /**
249    * Returns the name of the class this object belongs to.
250    */
getClassName()251   public String getClassName() {
252     AhatClassObj classObj = getClassObj();
253     return classObj == null ? "???" : classObj.getName();
254   }
255 
256   /**
257    * Returns true if the given instance is an array instance
258    */
isArrayInstance()259   public boolean isArrayInstance() {
260     // Overridden by AhatArrayInstance.
261     return false;
262   }
263 
264   /**
265    * Returns this as an AhatArrayInstance if this is an AhatArrayInstance.
266    * Returns null if this is not an AhatArrayInstance.
267    */
asArrayInstance()268   public AhatArrayInstance asArrayInstance() {
269     // Overridden by AhatArrayInstance.
270     return null;
271   }
272 
273   /**
274    * Returns true if the given instance is a class instance
275    */
isClassInstance()276   public boolean isClassInstance() {
277     return false;
278   }
279 
280   /**
281    * Returns this as an AhatClassInstance if this is an AhatClassInstance.
282    * Returns null if this is not an AhatClassInstance.
283    */
asClassInstance()284   public AhatClassInstance asClassInstance() {
285     return null;
286   }
287 
288   /**
289    * Return the referent associated with this instance.
290    * This is relevent for instances of java.lang.ref.Reference.
291    * Returns null if the instance has no referent associated with it.
292    */
getReferent()293   public AhatInstance getReferent() {
294     // Overridden by AhatClassInstance.
295     return null;
296   }
297 
298   /**
299    * Returns a list of objects with hard references to this object.
300    */
getHardReverseReferences()301   public List<AhatInstance> getHardReverseReferences() {
302     return Arrays.asList(mHardReverseReferences);
303   }
304 
305   /**
306    * Returns a list of objects with soft references to this object.
307    */
getSoftReverseReferences()308   public List<AhatInstance> getSoftReverseReferences() {
309     return Arrays.asList(mSoftReverseReferences);
310   }
311 
312   /**
313    * Returns the value of a field of an instance.
314    * Returns null if the field value is null, the field couldn't be read, or
315    * there are multiple fields with the same name.
316    */
getField(String fieldName)317   public Value getField(String fieldName) {
318     // Overridden by AhatClassInstance.
319     return null;
320   }
321 
322   /**
323    * Reads a reference field of this instance.
324    * Returns null if the field value is null, or if the field couldn't be read.
325    */
getRefField(String fieldName)326   public AhatInstance getRefField(String fieldName) {
327     // Overridden by AhatClassInstance.
328     return null;
329   }
330 
331   /**
332    * Assuming inst represents a DexCache object, return the dex location for
333    * that dex cache. Returns null if the given instance doesn't represent a
334    * DexCache object or the location could not be found.
335    * If maxChars is non-negative, the returned location is truncated to
336    * maxChars in length.
337    */
getDexCacheLocation(int maxChars)338   public String getDexCacheLocation(int maxChars) {
339     return null;
340   }
341 
342   /**
343    * Return the bitmap instance associated with this object, or null if there
344    * is none. This works for android.graphics.Bitmap instances and their
345    * underlying Byte[] instances.
346    */
getAssociatedBitmapInstance()347   public AhatInstance getAssociatedBitmapInstance() {
348     return null;
349   }
350 
351   /**
352    * Read the string value from this instance.
353    * Returns null if this object can't be interpreted as a string.
354    * The returned string is truncated to maxChars characters.
355    * If maxChars is negative, the returned string is not truncated.
356    */
asString(int maxChars)357   public String asString(int maxChars) {
358     // By default instances can't be interpreted as a string. This method is
359     // overridden by AhatClassInstance and AhatArrayInstance for those cases
360     // when an instance can be interpreted as a string.
361     return null;
362   }
363 
364   /**
365    * Reads the string value from an hprof Instance.
366    * Returns null if the object can't be interpreted as a string.
367    */
asString()368   public String asString() {
369     return asString(-1);
370   }
371 
372   /**
373    * Return the bitmap associated with the given instance, if any.
374    * This is relevant for instances of android.graphics.Bitmap and byte[].
375    * Returns null if there is no bitmap associated with the given instance.
376    */
asBitmap()377   public BufferedImage asBitmap() {
378     return null;
379   }
380 
381   /**
382    * Returns a sample path from a GC root to this instance.
383    * This instance is included as the last element of the path with an empty
384    * field description.
385    */
getPathFromGcRoot()386   public List<PathElement> getPathFromGcRoot() {
387     List<PathElement> path = new ArrayList<PathElement>();
388 
389     AhatInstance dom = this;
390     for (PathElement elem = new PathElement(this, ""); elem != null;
391         elem = getNextPathElementToGcRoot(elem.instance)) {
392       if (elem.instance.equals(dom)) {
393         elem.isDominator = true;
394         dom = dom.getImmediateDominator();
395       }
396       path.add(elem);
397     }
398     Collections.reverse(path);
399     return path;
400   }
401 
402   /**
403    * Returns the next instance to GC root from this object and a string
404    * description of which field of that object refers to the given instance.
405    * Returns null if the given instance has no next instance to the gc root.
406    */
getNextPathElementToGcRoot(AhatInstance inst)407   private static PathElement getNextPathElementToGcRoot(AhatInstance inst) {
408     AhatInstance parent = inst.mNextInstanceToGcRoot;
409     if (parent == null) {
410       return null;
411     }
412     return new PathElement(inst.mNextInstanceToGcRoot, inst.mNextInstanceToGcRootField);
413   }
414 
setNextInstanceToGcRoot(AhatInstance inst, String field)415   void setNextInstanceToGcRoot(AhatInstance inst, String field) {
416     mNextInstanceToGcRoot = inst;
417     mNextInstanceToGcRootField = field;
418   }
419 
420   /** Returns a human-readable identifier for this object.
421    * For class objects, the string is the class name.
422    * For class instances, the string is the class name followed by '@' and the
423    * hex id of the instance.
424    * For array instances, the string is the array type followed by the size in
425    * square brackets, followed by '@' and the hex id of the instance.
426    */
toString()427   @Override public abstract String toString();
428 
429   /**
430    * Read the byte[] value from an hprof Instance.
431    * Returns null if the instance is not a byte array.
432    */
asByteArray()433   byte[] asByteArray() {
434     return null;
435   }
436 
setBaseline(AhatInstance baseline)437   public void setBaseline(AhatInstance baseline) {
438     mBaseline = baseline;
439   }
440 
getBaseline()441   @Override public AhatInstance getBaseline() {
442     return mBaseline;
443   }
444 
isPlaceHolder()445   @Override public boolean isPlaceHolder() {
446     return false;
447   }
448 
449   /**
450    * Returns a new place holder instance corresponding to this instance.
451    */
newPlaceHolderInstance()452   AhatInstance newPlaceHolderInstance() {
453     return new AhatPlaceHolderInstance(this);
454   }
455 }
456