Home | History | Annotate | Download | only in heapdump
      1 /*
      2  * Copyright (C) 2015 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.proguard.ProguardMap;
     20 import java.util.ArrayList;
     21 import java.util.Collection;
     22 import java.util.Collections;
     23 import java.util.HashMap;
     24 import java.util.List;
     25 import java.util.Map;
     26 
     27 /**
     28  * Used to collection information about objects allocated at a particular
     29  * allocation site.
     30  */
     31 public class Site implements Diffable<Site> {
     32   // The site that this site was directly called from.
     33   // mParent is null for the root site.
     34   private Site mParent;
     35 
     36   private String mMethodName;
     37   private String mSignature;
     38   private String mFilename;
     39   private int mLineNumber;
     40 
     41   // A unique id to identify this site with. The id is chosen based on a
     42   // depth first traversal of the complete site tree, which gives it the
     43   // following desired properties:
     44   // * The id can easily be represented in a URL.
     45   // * The id is determined by the hprof file, so that the same id can be used
     46   //   across different instances for viewing the same hprof file.
     47   // * A binary search can be used to find a site by id from the root site in
     48   //   log time.
     49   //
     50   // The id is set by prepareForUse after the complete site tree is constructed.
     51   private long mId = -1;
     52 
     53   // The total size of objects allocated in this site (including child sites),
     54   // organized by heap index. Computed as part of prepareForUse.
     55   private Size[] mSizesByHeap;
     56 
     57   // List of child sites.
     58   private List<Site> mChildren;
     59 
     60   // List of objects allocated at this site (not including child sites).
     61   private List<AhatInstance> mObjects;
     62 
     63   private List<ObjectsInfo> mObjectsInfos;
     64   private Map<AhatHeap, Map<AhatClassObj, ObjectsInfo>> mObjectsInfoMap;
     65 
     66   private Site mBaseline;
     67 
     68   /**
     69    * Summary information about instances allocated at a particular allocation
     70    * site that are instances of a particular class and allocated on a
     71    * particular heap.
     72    */
     73   public static class ObjectsInfo implements Diffable<ObjectsInfo> {
     74     /**
     75      * The heap that the summarized objects belong to.
     76      */
     77     public AhatHeap heap;
     78 
     79     /**
     80      * The class of the summarized objects.
     81      */
     82     public AhatClassObj classObj;   // May be null. Not sure why.
     83 
     84     /**
     85      * The number of instances included in the summary.
     86      */
     87     public long numInstances;
     88 
     89     /**
     90      * The sum of the shallow size of each instance included in the summary.
     91      */
     92     public Size numBytes;
     93 
     94     private ObjectsInfo baseline;
     95 
     96     /**
     97      * Constructs a new, empty objects info for the given heap and class
     98      * combination.
     99      */
    100     ObjectsInfo(AhatHeap heap, AhatClassObj classObj) {
    101       this.heap = heap;
    102       this.classObj = classObj;
    103       this.numInstances = 0;
    104       this.numBytes = Size.ZERO;
    105       this.baseline = this;
    106     }
    107 
    108     /**
    109      * Returns the name of the class this ObjectsInfo is associated with.
    110      *
    111      * @return the name of this object info's class
    112      */
    113     public String getClassName() {
    114       return classObj == null ? "???" : classObj.getName();
    115     }
    116 
    117     void setBaseline(ObjectsInfo baseline) {
    118       this.baseline = baseline;
    119     }
    120 
    121     @Override public ObjectsInfo getBaseline() {
    122       return baseline;
    123     }
    124 
    125     @Override public boolean isPlaceHolder() {
    126       return false;
    127     }
    128   }
    129 
    130   /**
    131    * Construct a root site.
    132    */
    133   Site(String name) {
    134     this(null, name, "", "", 0);
    135   }
    136 
    137   private Site(Site parent, String method, String signature, String file, int line) {
    138     mParent = parent;
    139     mMethodName = method;
    140     mSignature = signature;
    141     mFilename = file;
    142     mLineNumber = line;
    143     mChildren = new ArrayList<Site>();
    144     mObjects = new ArrayList<AhatInstance>();
    145     mObjectsInfos = new ArrayList<ObjectsInfo>();
    146     mObjectsInfoMap = new HashMap<AhatHeap, Map<AhatClassObj, ObjectsInfo>>();
    147     mBaseline = this;
    148   }
    149 
    150   /**
    151    * Gets a child site of this site.
    152    * @param frames the list of frames in the stack trace, starting with the
    153    *               inner-most frame. May be null, in which case this site is
    154    *               returned.
    155    * @return the child site
    156    */
    157   Site getSite(ProguardMap.Frame[] frames) {
    158     return frames == null ? this : getSite(this, frames);
    159   }
    160 
    161   private static Site getSite(Site site, ProguardMap.Frame[] frames) {
    162     for (int s = frames.length - 1; s >= 0; --s) {
    163       ProguardMap.Frame frame = frames[s];
    164       Site child = null;
    165       for (int i = 0; i < site.mChildren.size(); i++) {
    166         Site curr = site.mChildren.get(i);
    167         if (curr.mLineNumber == frame.line
    168             && curr.mMethodName.equals(frame.method)
    169             && curr.mSignature.equals(frame.signature)
    170             && curr.mFilename.equals(frame.filename)) {
    171           child = curr;
    172           break;
    173         }
    174       }
    175       if (child == null) {
    176         child = new Site(site, frame.method, frame.signature,
    177             frame.filename, frame.line);
    178         site.mChildren.add(child);
    179       }
    180       site = child;
    181     }
    182     return site;
    183   }
    184 
    185   /**
    186    * Add an instance allocated at this site.
    187    */
    188   void addInstance(AhatInstance inst) {
    189     mObjects.add(inst);
    190   }
    191 
    192   /**
    193    * Prepare this and all child sites for use.
    194    * Recomputes site ids, sizes, ObjectInfos for this and all child sites.
    195    * This should be called after the sites tree has been formed and after
    196    * dominators computation has been performed to ensure only reachable
    197    * objects are included in the ObjectsInfos.
    198    *
    199    * @param id - The smallest id that is allowed to be used for this site or
    200    * any of its children.
    201    * @param numHeaps - The number of heaps in the heap dump.
    202    * @return An id larger than the largest id used for this site or any of its
    203    * children.
    204    */
    205   long prepareForUse(long id, int numHeaps) {
    206     mId = id++;
    207 
    208     // Count up the total sizes by heap.
    209     mSizesByHeap = new Size[numHeaps];
    210     for (int i = 0; i < numHeaps; ++i) {
    211       mSizesByHeap[i] = Size.ZERO;
    212     }
    213 
    214     // Add all reachable objects allocated at this site.
    215     for (AhatInstance inst : mObjects) {
    216       if (inst.isStronglyReachable()) {
    217         AhatHeap heap = inst.getHeap();
    218         Size size = inst.getSize();
    219         ObjectsInfo info = getObjectsInfo(heap, inst.getClassObj());
    220         info.numInstances++;
    221         info.numBytes = info.numBytes.plus(size);
    222         mSizesByHeap[heap.getIndex()] = mSizesByHeap[heap.getIndex()].plus(size);
    223       }
    224     }
    225 
    226     // Add objects allocated in child sites.
    227     for (Site child : mChildren) {
    228       id = child.prepareForUse(id, numHeaps);
    229       for (ObjectsInfo childInfo : child.mObjectsInfos) {
    230         ObjectsInfo info = getObjectsInfo(childInfo.heap, childInfo.classObj);
    231         info.numInstances += childInfo.numInstances;
    232         info.numBytes = info.numBytes.plus(childInfo.numBytes);
    233       }
    234       for (int i = 0; i < numHeaps; ++i) {
    235         mSizesByHeap[i] = mSizesByHeap[i].plus(child.mSizesByHeap[i]);
    236       }
    237     }
    238     return id;
    239   }
    240 
    241   /**
    242    * Returns the size of all objects on the given heap allocated at this site.
    243    * Includes objects belonging to <code>heap</code> allocated at this and
    244    * child sites.
    245    *
    246    * @param heap the heap to query the size for
    247    * @return the total shallow size of objects in this site
    248    */
    249   public Size getSize(AhatHeap heap) {
    250     return mSizesByHeap[heap.getIndex()];
    251   }
    252 
    253   /**
    254    * Collects the objects allocated under this site, optionally filtered by
    255    * heap name or class name. Includes objects allocated in children sites.
    256    * @param heapName the name of the heap the collected objects should
    257    *                 belong to. This may be null to indicate objects of
    258    *                 every heap should be collected.
    259    * @param className the name of the class the collected objects should
    260    *                  belong to. This may be null to indicate objects of
    261    *                  every class should be collected.
    262    * @param objects out parameter. A collection of objects that all
    263    *                collected objects should be added to.
    264    */
    265   public void getObjects(String heapName, String className, Collection<AhatInstance> objects) {
    266     for (AhatInstance inst : mObjects) {
    267       if ((heapName == null || inst.getHeap().getName().equals(heapName))
    268           && (className == null || inst.getClassName().equals(className))) {
    269         objects.add(inst);
    270       }
    271     }
    272 
    273     // Recursively visit children. Recursion should be okay here because the
    274     // stack depth is limited by a reasonable amount (128 frames or so).
    275     for (Site child : mChildren) {
    276       child.getObjects(heapName, className, objects);
    277     }
    278   }
    279 
    280   /**
    281    * Returns the ObjectsInfo at this site for the given heap and class
    282    * objects. Creates a new empty ObjectsInfo if none existed before.
    283    */
    284   ObjectsInfo getObjectsInfo(AhatHeap heap, AhatClassObj classObj) {
    285     Map<AhatClassObj, ObjectsInfo> classToObjectsInfo = mObjectsInfoMap.get(heap);
    286     if (classToObjectsInfo == null) {
    287       classToObjectsInfo = new HashMap<AhatClassObj, ObjectsInfo>();
    288       mObjectsInfoMap.put(heap, classToObjectsInfo);
    289     }
    290 
    291     ObjectsInfo info = classToObjectsInfo.get(classObj);
    292     if (info == null) {
    293       info = new ObjectsInfo(heap, classObj);
    294       mObjectsInfos.add(info);
    295       classToObjectsInfo.put(classObj, info);
    296     }
    297     return info;
    298   }
    299 
    300   /**
    301    * Return a summary breakdown of the objects allocated at this site.
    302    * Objects are grouped by class and heap and summarized into a single
    303    * {@link ObjectsInfo}. This method returns all the groups for this
    304    * allocation site.
    305    *
    306    * @return all ObjectInfo summaries for instances allocated at this site
    307    */
    308   public List<ObjectsInfo> getObjectsInfos() {
    309     return mObjectsInfos;
    310   }
    311 
    312   /**
    313    * Returns the combined size of the site for all heaps.
    314    * Includes all objects allocated at this and child sites.
    315    *
    316    * @return total shallow size of objects in this site
    317    */
    318   public Size getTotalSize() {
    319     Size total = Size.ZERO;
    320     for (Size size : mSizesByHeap) {
    321       total = total.plus(size);
    322     }
    323     return total;
    324   }
    325 
    326   /**
    327    * Returns the site this site was called from.
    328    * Returns null for the root site.
    329    *
    330    * @return the site this site was called from
    331    */
    332   public Site getParent() {
    333     return mParent;
    334   }
    335 
    336   /**
    337    * Returns the name of the method this allocation site belongs to.
    338    * For example, "equals".
    339    *
    340    * @return the method name of the allocation site
    341    */
    342   public String getMethodName() {
    343     return mMethodName;
    344   }
    345 
    346   /**
    347    * Returns the signature of the method this allocation site belongs to.
    348    * For example, "(Ljava/lang/Object;)Z".
    349    *
    350    * @return the signature of method the allocation site belongs to
    351    */
    352   public String getSignature() {
    353     return mSignature;
    354   }
    355 
    356   /**
    357    * Returns the name of the Java file where this allocation site is found.
    358    *
    359    * @return the file the allocation site belongs to
    360    */
    361   public String getFilename() {
    362     return mFilename;
    363   }
    364 
    365   /**
    366    * Returns the line number of the code in the source file that the
    367    * allocation site refers to.
    368    *
    369    * @return the allocation site line number
    370    */
    371   public int getLineNumber() {
    372     return mLineNumber;
    373   }
    374 
    375   /**
    376    * Returns the unique id of this site.
    377    * This is an arbitrary unique id computed after processing the heap dump.
    378    *
    379    * @return the site id
    380    */
    381   public long getId() {
    382     return mId;
    383   }
    384 
    385   /**
    386    * Returns the child site with the given id.
    387    * Returns null if no such site was found.
    388    *
    389    * @param id the id of the child site to find
    390    * @return the found child site
    391    */
    392   public Site findSite(long id) {
    393     if (id == mId) {
    394       return this;
    395     }
    396 
    397     // Binary search over the children to find the right child to search in.
    398     int start = 0;
    399     int end = mChildren.size();
    400     while (start < end) {
    401       int mid = start + ((end - start) / 2);
    402       Site midSite = mChildren.get(mid);
    403       if (id < midSite.mId) {
    404         end = mid;
    405       } else if (mid + 1 == end) {
    406         // This is the last child we could possibly find the desired site in,
    407         // so search in this child.
    408         return midSite.findSite(id);
    409       } else if (id < mChildren.get(mid + 1).mId) {
    410         // The desired site has an id between this child's id and the next
    411         // child's id, so search in this child.
    412         return midSite.findSite(id);
    413       } else {
    414         start = mid + 1;
    415       }
    416     }
    417     return null;
    418   }
    419 
    420   /**
    421    * Returns an unmodifiable list of this site's immediate children.
    422    *
    423    * @return this site's child sites
    424    */
    425   public List<Site> getChildren() {
    426     return Collections.unmodifiableList(mChildren);
    427   }
    428 
    429   void setBaseline(Site baseline) {
    430     mBaseline = baseline;
    431   }
    432 
    433   @Override public Site getBaseline() {
    434     return mBaseline;
    435   }
    436 
    437   @Override public boolean isPlaceHolder() {
    438     return false;
    439   }
    440 }
    441