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