Home | History | Annotate | Download | only in heapdump
      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 
     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    */
     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    */
    126   public long getId() {
    127     return mId;
    128   }
    129 
    130   /**
    131    * Returns the shallow number of bytes this object takes up.
    132    */
    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    */
    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    */
    149   public long getTotalRetainedSize() {
    150     return mTotalRetainedSize;
    151   }
    152 
    153   /**
    154    * Returns whether this object is strongly-reachable.
    155    */
    156   public boolean isReachable() {
    157     return mIsReachable;
    158   }
    159 
    160   /**
    161    * Returns the heap that this instance is allocated on.
    162    */
    163   public AhatHeap getHeap() {
    164     return mHeap;
    165   }
    166 
    167   /**
    168    * Returns true if this instance is marked as a root instance.
    169    */
    170   public boolean isRoot() {
    171     return mRootTypes != null;
    172   }
    173 
    174   /**
    175    * Marks this instance as being a root of the given type.
    176    */
    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    */
    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    */
    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    */
    206   public List<AhatInstance> getDominated() {
    207     return mDominated;
    208   }
    209 
    210   /**
    211    * Returns the site where this instance was allocated.
    212    */
    213   public Site getSite() {
    214     return mSite;
    215   }
    216 
    217   /**
    218    * Sets the allocation site of this instance.
    219    */
    220   void setSite(Site site) {
    221     mSite = site;
    222   }
    223 
    224   /**
    225    * Returns true if the given instance is a class object
    226    */
    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    */
    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    */
    244   public AhatClassObj getClassObj() {
    245     return mClassObj;
    246   }
    247 
    248   /**
    249    * Returns the name of the class this object belongs to.
    250    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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 
    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    */
    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    */
    433   byte[] asByteArray() {
    434     return null;
    435   }
    436 
    437   public void setBaseline(AhatInstance baseline) {
    438     mBaseline = baseline;
    439   }
    440 
    441   @Override public AhatInstance getBaseline() {
    442     return mBaseline;
    443   }
    444 
    445   @Override public boolean isPlaceHolder() {
    446     return false;
    447   }
    448 
    449   /**
    450    * Returns a new place holder instance corresponding to this instance.
    451    */
    452   AhatInstance newPlaceHolderInstance() {
    453     return new AhatPlaceHolderInstance(this);
    454   }
    455 }
    456