blob: 1b2cf3c59f56bf00a865c6d5120f9b3683bec913 [file] [log] [blame]
Richard Uhlercda4f2e2016-09-09 09:56:20 +01001/*
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
17package com.android.ahat.heapdump;
18
Richard Uhler26870502017-07-04 15:55:19 +010019import com.android.ahat.dominators.DominatorsComputation;
Richard Uhlercda4f2e2016-09-09 09:56:20 +010020import com.android.tools.perflib.captures.DataBuffer;
21import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
22import com.android.tools.perflib.heap.ArrayInstance;
23import com.android.tools.perflib.heap.ClassInstance;
24import com.android.tools.perflib.heap.ClassObj;
25import com.android.tools.perflib.heap.Heap;
26import com.android.tools.perflib.heap.Instance;
27import com.android.tools.perflib.heap.ProguardMap;
28import com.android.tools.perflib.heap.RootObj;
29import com.android.tools.perflib.heap.Snapshot;
30import com.android.tools.perflib.heap.StackFrame;
31import com.android.tools.perflib.heap.StackTrace;
32import gnu.trove.TObjectProcedure;
33import java.io.File;
34import java.io.IOException;
35import java.util.ArrayList;
36import java.util.Collection;
37import java.util.Comparator;
Richard Uhlercda4f2e2016-09-09 09:56:20 +010038import java.util.List;
39import java.util.Map;
40
Richard Uhlerf629cfd2016-12-12 13:11:26 +000041public class AhatSnapshot implements Diffable<AhatSnapshot> {
Richard Uhlercda4f2e2016-09-09 09:56:20 +010042 private final Site mRootSite = new Site("ROOT");
43
44 // Collection of objects whose immediate dominator is the SENTINEL_ROOT.
Richard Uhler26870502017-07-04 15:55:19 +010045 private final List<AhatInstance> mRooted;
Richard Uhlercda4f2e2016-09-09 09:56:20 +010046
47 // List of all ahat instances stored in increasing order by id.
48 private final List<AhatInstance> mInstances = new ArrayList<AhatInstance>();
49
Richard Uhlerf629cfd2016-12-12 13:11:26 +000050 private final List<AhatHeap> mHeaps = new ArrayList<AhatHeap>();
Richard Uhlercda4f2e2016-09-09 09:56:20 +010051
Richard Uhlerf629cfd2016-12-12 13:11:26 +000052 private AhatSnapshot mBaseline = this;
Richard Uhlercda4f2e2016-09-09 09:56:20 +010053
54 /**
55 * Create an AhatSnapshot from an hprof file.
56 */
57 public static AhatSnapshot fromHprof(File hprof, ProguardMap map) throws IOException {
58 return fromDataBuffer(new MemoryMappedFileBuffer(hprof), map);
59 }
60
61 /**
62 * Create an AhatSnapshot from an in-memory data buffer.
63 */
64 public static AhatSnapshot fromDataBuffer(DataBuffer buffer, ProguardMap map) throws IOException {
65 AhatSnapshot snapshot = new AhatSnapshot(buffer, map);
66
67 // Request a GC now to clean up memory used by perflib. This helps to
68 // avoid a noticable pause when visiting the first interesting page in
69 // ahat.
70 System.gc();
71
72 return snapshot;
73 }
74
75 /**
76 * Constructs an AhatSnapshot for the given hprof binary data.
77 */
78 private AhatSnapshot(DataBuffer buffer, ProguardMap map) throws IOException {
79 Snapshot snapshot = Snapshot.createSnapshot(buffer, map);
Richard Uhlercda4f2e2016-09-09 09:56:20 +010080
Richard Uhler3ee4bff2017-05-16 13:31:01 +010081 // Properly label the class of class objects in the perflib snapshot.
Richard Uhlercda4f2e2016-09-09 09:56:20 +010082 final ClassObj javaLangClass = snapshot.findClass("java.lang.Class");
83 if (javaLangClass != null) {
84 for (Heap heap : snapshot.getHeaps()) {
85 Collection<ClassObj> classes = heap.getClasses();
86 for (ClassObj clsObj : classes) {
87 if (clsObj.getClassObj() == null) {
88 clsObj.setClassId(javaLangClass.getId());
89 }
90 }
91 }
92 }
93
94 // Create mappings from id to ahat instance and heaps.
95 Collection<Heap> heaps = snapshot.getHeaps();
Richard Uhlercda4f2e2016-09-09 09:56:20 +010096 for (Heap heap : heaps) {
Richard Uhlerf629cfd2016-12-12 13:11:26 +000097 // Note: mHeaps will not be in index order if snapshot.getHeaps does not
98 // return heaps in index order. That's fine, because we don't rely on
99 // mHeaps being in index order.
100 mHeaps.add(new AhatHeap(heap.getName(), snapshot.getHeapIndex(heap)));
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100101 TObjectProcedure<Instance> doCreate = new TObjectProcedure<Instance>() {
102 @Override
103 public boolean execute(Instance inst) {
Richard Uhlerd640e292016-12-28 15:46:03 +0000104 long id = inst.getId();
105 if (inst instanceof ClassInstance) {
106 mInstances.add(new AhatClassInstance(id));
107 } else if (inst instanceof ArrayInstance) {
108 mInstances.add(new AhatArrayInstance(id));
109 } else if (inst instanceof ClassObj) {
110 AhatClassObj classObj = new AhatClassObj(id);
111 mInstances.add(classObj);
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100112 }
113 return true;
114 }
115 };
116 for (Instance instance : heap.getClasses()) {
117 doCreate.execute(instance);
118 }
119 heap.forEachInstance(doCreate);
120 }
121
122 // Sort the instances by id so we can use binary search to lookup
123 // instances by id.
124 mInstances.sort(new Comparator<AhatInstance>() {
125 @Override
126 public int compare(AhatInstance a, AhatInstance b) {
127 return Long.compare(a.getId(), b.getId());
128 }
129 });
130
Richard Uhler3ee4bff2017-05-16 13:31:01 +0100131 Map<Instance, Long> registeredNative = Perflib.getRegisteredNativeAllocations(snapshot);
132
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100133 // Initialize ahat snapshot and instances based on the perflib snapshot
134 // and instances.
135 for (AhatInstance ahat : mInstances) {
136 Instance inst = snapshot.findInstance(ahat.getId());
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100137
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100138 StackFrame[] frames = null;
139 StackTrace stack = inst.getStack();
140 if (stack != null) {
141 frames = stack.getFrames();
142 }
Richard Uhler29c4bee2017-08-10 10:16:15 +0100143 ahat.initialize(this, inst, mRootSite.getSite(frames));
Richard Uhler26870502017-07-04 15:55:19 +0100144
145 Long registeredNativeSize = registeredNative.get(inst);
146 if (registeredNativeSize != null) {
147 ahat.addRegisteredNativeSize(registeredNativeSize);
148 }
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100149 }
150
151 // Record the roots and their types.
Richard Uhler26870502017-07-04 15:55:19 +0100152 SuperRoot superRoot = new SuperRoot();
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100153 for (RootObj root : snapshot.getGCRoots()) {
154 Instance inst = root.getReferredInstance();
155 if (inst != null) {
Richard Uhler26870502017-07-04 15:55:19 +0100156 AhatInstance ahat = findInstance(inst.getId());
157 if (!ahat.isRoot()) {
158 superRoot.addRoot(ahat);
159 }
160 ahat.addRootType(root.getRootType().toString());
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100161 }
162 }
163 snapshot.dispose();
Richard Uhler3ee4bff2017-05-16 13:31:01 +0100164
Richard Uhler26870502017-07-04 15:55:19 +0100165 AhatInstance.computeReverseReferences(superRoot);
166 DominatorsComputation.computeDominators(superRoot);
167 AhatInstance.computeRetainedSize(superRoot, mHeaps.size());
168
169 mRooted = superRoot.getDominated();
170 for (AhatHeap heap : mHeaps) {
171 heap.addToSize(superRoot.getRetainedSize(heap));
Richard Uhler3ee4bff2017-05-16 13:31:01 +0100172 }
Richard Uhler26870502017-07-04 15:55:19 +0100173
Richard Uhler29c4bee2017-08-10 10:16:15 +0100174 mRootSite.prepareForUse(0, mHeaps.size());
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100175 }
176
177 /**
178 * Returns the instance with given id in this snapshot.
179 * Returns null if no instance with the given id is found.
180 */
181 public AhatInstance findInstance(long id) {
182 // Binary search over the sorted instances.
183 int start = 0;
184 int end = mInstances.size();
185 while (start < end) {
186 int mid = start + ((end - start) / 2);
187 AhatInstance midInst = mInstances.get(mid);
188 long midId = midInst.getId();
189 if (id == midId) {
190 return midInst;
191 } else if (id < midId) {
192 end = mid;
193 } else {
194 start = mid + 1;
195 }
196 }
197 return null;
198 }
199
200 /**
201 * Returns the AhatClassObj with given id in this snapshot.
202 * Returns null if no class object with the given id is found.
203 */
204 public AhatClassObj findClassObj(long id) {
205 AhatInstance inst = findInstance(id);
206 return inst == null ? null : inst.asClassObj();
207 }
208
209 /**
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100210 * Returns the heap with the given name, if any.
211 * Returns null if no heap with the given name could be found.
212 */
213 public AhatHeap getHeap(String name) {
214 // We expect a small number of heaps (maybe 3 or 4 total), so a linear
215 // search should be acceptable here performance wise.
216 for (AhatHeap heap : getHeaps()) {
217 if (heap.getName().equals(name)) {
218 return heap;
219 }
220 }
221 return null;
222 }
223
224 /**
225 * Returns a list of heaps in the snapshot in canonical order.
Richard Uhlerf629cfd2016-12-12 13:11:26 +0000226 * Modifications to the returned list are visible to this AhatSnapshot,
227 * which is used by diff to insert place holder heaps.
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100228 */
Richard Uhlerf629cfd2016-12-12 13:11:26 +0000229 public List<AhatHeap> getHeaps() {
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100230 return mHeaps;
231 }
232
233 /**
234 * Returns a collection of instances whose immediate dominator is the
235 * SENTINEL_ROOT.
236 */
237 public List<AhatInstance> getRooted() {
238 return mRooted;
239 }
240
241 /**
Richard Uhlerf629cfd2016-12-12 13:11:26 +0000242 * Returns the root site for this snapshot.
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100243 */
Richard Uhlerf629cfd2016-12-12 13:11:26 +0000244 public Site getRootSite() {
245 return mRootSite;
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100246 }
247
Richard Uhler29c4bee2017-08-10 10:16:15 +0100248 // Get the site associated with the given id.
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100249 // Returns the root site if no such site found.
Richard Uhler29c4bee2017-08-10 10:16:15 +0100250 public Site getSite(long id) {
251 Site site = mRootSite.findSite(id);
252 return site == null ? mRootSite : site;
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100253 }
254
255 // Return the Value for the given perflib value object.
256 Value getValue(Object value) {
257 if (value instanceof Instance) {
258 value = findInstance(((Instance)value).getId());
259 }
Richard Uhler21f89d22017-08-26 16:30:04 +0100260 return Value.pack(value);
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100261 }
Richard Uhlerf629cfd2016-12-12 13:11:26 +0000262
263 public void setBaseline(AhatSnapshot baseline) {
264 mBaseline = baseline;
265 }
266
267 /**
268 * Returns true if this snapshot has been diffed against another, different
269 * snapshot.
270 */
271 public boolean isDiffed() {
272 return mBaseline != this;
273 }
274
275 @Override public AhatSnapshot getBaseline() {
276 return mBaseline;
277 }
278
279 @Override public boolean isPlaceHolder() {
280 return false;
281 }
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100282}