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.Dominators;
20 import com.android.ahat.progress.Progress;
21 import java.util.List;
22 
23 /**
24  * A parsed heap dump.
25  * It contains methods to access the heaps, allocation sites, roots, classes,
26  * and instances from the parsed heap dump.
27  */
28 public class AhatSnapshot implements Diffable<AhatSnapshot> {
29   private final Site mRootSite;
30 
31   private final SuperRoot mSuperRoot;
32 
33   // List of all ahat instances.
34   private final Instances<AhatInstance> mInstances;
35 
36   private List<AhatHeap> mHeaps;
37 
38   private AhatSnapshot mBaseline = this;
39 
40   private AhatBitmapInstance.BitmapDumpData mBitmapDumpData = null;
41 
AhatSnapshot(SuperRoot root, Instances<AhatInstance> instances, List<AhatHeap> heaps, Site rootSite, Progress progress, Reachability retained)42   AhatSnapshot(SuperRoot root,
43                Instances<AhatInstance> instances,
44                List<AhatHeap> heaps,
45                Site rootSite,
46                Progress progress,
47                Reachability retained) {
48     mSuperRoot = root;
49     mInstances = instances;
50     mHeaps = heaps;
51     mRootSite = rootSite;
52 
53     AhatInstance.computeReachability(mSuperRoot, progress, mInstances.size());
54 
55     mBitmapDumpData = AhatBitmapInstance.findBitmapDumpData(mSuperRoot, mInstances);
56 
57     for (AhatInstance inst : mInstances) {
58       // Add this instance to its site.
59       inst.getSite().addInstance(inst);
60 
61       // Update registered native allocation size.
62       AhatInstance.RegisteredNativeAllocation nra = inst.asRegisteredNativeAllocation();
63       if (nra != null) {
64         nra.referent.addRegisteredNativeSize(nra.size);
65       }
66 
67       if (retained == Reachability.UNREACHABLE && inst.isUnreachable()) {
68         mSuperRoot.addRoot(inst);
69       }
70     }
71 
72     Dominators.Graph<AhatInstance> graph = new Dominators.Graph<AhatInstance>() {
73       @Override
74       public void setDominatorsComputationState(AhatInstance node, Object state) {
75         node.setTemporaryUserData(state);
76       }
77 
78       @Override
79       public Object getDominatorsComputationState(AhatInstance node) {
80         return node.getTemporaryUserData();
81       }
82 
83       @Override
84       public Iterable<AhatInstance> getReferencesForDominators(AhatInstance node) {
85         return node.getReferencesForDominators(retained);
86       }
87 
88       @Override
89       public void setDominator(AhatInstance node, AhatInstance dominator) {
90         node.setDominator(dominator);
91       }
92     };
93     new Dominators(graph).progress(progress, mInstances.size()).computeDominators(mSuperRoot);
94 
95     AhatInstance.computeRetainedSize(mSuperRoot, mHeaps.size());
96 
97     for (AhatHeap heap : mHeaps) {
98       heap.addToSize(mSuperRoot.getRetainedSize(heap));
99     }
100 
101     mRootSite.prepareForUse(0, mHeaps.size(), retained);
102   }
103 
104   /**
105    * Returns the instance with the given id in this snapshot.
106    * Where the id of an instance x is x.getId().
107    * Returns null if no instance with the given id is found.
108    *
109    * @param id the id of the instance to find
110    * @return the instance with the given id
111    */
findInstance(long id)112   public AhatInstance findInstance(long id) {
113     return mInstances.get(id);
114   }
115 
116   /**
117    * Returns the AhatClassObj with the given id in this snapshot.
118    * Where the id of a class object x is x.getId().
119    * Returns null if no class object with the given id is found.
120    *
121    * @param id the id of the class object to find
122    * @return the class object with the given id
123    */
findClassObj(long id)124   public AhatClassObj findClassObj(long id) {
125     AhatInstance inst = findInstance(id);
126     return inst == null ? null : inst.asClassObj();
127   }
128 
129   /**
130    * Returns the heap with the given name.
131    * Where the name of a heap x is x.getName().
132    * Returns null if no heap with the given name could be found.
133    *
134    * @param name the name of the heap to get
135    * @return the heap with the given name
136    */
getHeap(String name)137   public AhatHeap getHeap(String name) {
138     // We expect a small number of heaps (maybe 3 or 4 total), so a linear
139     // search should be acceptable here performance wise.
140     for (AhatHeap heap : getHeaps()) {
141       if (heap.getName().equals(name)) {
142         return heap;
143       }
144     }
145     return null;
146   }
147 
148   /**
149    * Returns a list of heaps in the snapshot in canonical order.
150    * <p>
151    * Note: modifications to the returned list are visible to this
152    * AhatSnapshot, which is used by diff to insert placeholder heaps.
153    *
154    * @return list of heaps
155    */
getHeaps()156   public List<AhatHeap> getHeaps() {
157     return mHeaps;
158   }
159 
160   /**
161    * Returns a collection of "rooted" instances.
162    * An instance is "rooted" if it is a GC root, or if it is retained by more
163    * than one GC root. These are reachable instances that are not immediately
164    * dominated by any other instance in the heap.
165    *
166    * @return collection of rooted instances
167    */
getRooted()168   public List<AhatInstance> getRooted() {
169     return mSuperRoot.getDominated();
170   }
171 
172   /**
173    * Returns the root allocation site for this snapshot.
174    *
175    * @return the root allocation site
176    */
getRootSite()177   public Site getRootSite() {
178     return mRootSite;
179   }
180 
181   /**
182    * Returns the site associated with the given id.
183    * Where the id of a site x is x.getId().
184    * Returns the root site if no site with the given id is found.
185    *
186    * @param id the id of the site to get
187    * @return the site with the given id
188    */
getSite(long id)189   public Site getSite(long id) {
190     Site site = mRootSite.findSite(id);
191     return site == null ? mRootSite : site;
192   }
193 
setBaseline(AhatSnapshot baseline)194   void setBaseline(AhatSnapshot baseline) {
195     mBaseline = baseline;
196   }
197 
198   /**
199    * Returns true if this snapshot has been diffed against a different
200    * snapshot.
201    *
202    * @return true if the snapshot has been diffed
203    */
isDiffed()204   public boolean isDiffed() {
205     return mBaseline != this;
206   }
207 
getBaseline()208   @Override public AhatSnapshot getBaseline() {
209     return mBaseline;
210   }
211 
isPlaceHolder()212   @Override public boolean isPlaceHolder() {
213     return false;
214   }
215 
findDuplicateBitmaps()216   public List<List<AhatBitmapInstance>> findDuplicateBitmaps() {
217     return AhatBitmapInstance.findDuplicates(mBitmapDumpData);
218   }
219 }
220