blob: d088e8c43f6720d2fe70fccd319926784443f80b [file] [log] [blame]
Richard Uhlerb730b782015-07-15 16:01:58 -07001/*
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
17package com.android.ahat;
18
19import com.android.tools.perflib.heap.ClassObj;
20import com.android.tools.perflib.heap.Heap;
21import com.android.tools.perflib.heap.Instance;
Richard Uhler38f9eba2015-11-11 08:31:52 -080022import com.android.tools.perflib.heap.RootObj;
23import com.android.tools.perflib.heap.RootType;
Richard Uhlerb730b782015-07-15 16:01:58 -070024import com.android.tools.perflib.heap.Snapshot;
25import com.android.tools.perflib.heap.StackFrame;
26import com.android.tools.perflib.heap.StackTrace;
Richard Uhlerc237b392015-10-20 10:03:38 -070027import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
Richard Uhlerb730b782015-07-15 16:01:58 -070028import com.google.common.collect.Lists;
Richard Uhler6919a012015-12-15 09:15:00 -080029import gnu.trove.TObjectProcedure;
Richard Uhler35244722015-09-10 16:45:54 -070030import java.io.File;
31import java.io.IOException;
Richard Uhlerb730b782015-07-15 16:01:58 -070032import java.util.ArrayList;
33import java.util.Arrays;
Richard Uhler38f9eba2015-11-11 08:31:52 -080034import java.util.Collection;
Richard Uhlerb730b782015-07-15 16:01:58 -070035import java.util.Collections;
36import java.util.HashMap;
Richard Uhler38f9eba2015-11-11 08:31:52 -080037import java.util.HashSet;
Richard Uhlerb730b782015-07-15 16:01:58 -070038import java.util.List;
39import java.util.Map;
40
41/**
42 * A wrapper over the perflib snapshot that provides the behavior we use in
43 * ahat.
44 */
45class AhatSnapshot {
Richard Uhler1a5baaa2015-12-21 12:47:26 -080046 private final Snapshot mSnapshot;
47 private final List<Heap> mHeaps;
Richard Uhlerb730b782015-07-15 16:01:58 -070048
49 // Map from Instance to the list of Instances it immediately dominates.
Richard Uhler1a5baaa2015-12-21 12:47:26 -080050 private final Map<Instance, List<Instance>> mDominated
51 = new HashMap<Instance, List<Instance>>();
Richard Uhlerb730b782015-07-15 16:01:58 -070052
Richard Uhler7a16adb2015-11-11 09:13:23 -080053 // Collection of objects whose immediate dominator is the SENTINEL_ROOT.
Richard Uhler1a5baaa2015-12-21 12:47:26 -080054 private final List<Instance> mRooted = new ArrayList<Instance>();
Richard Uhler7a16adb2015-11-11 09:13:23 -080055
Richard Uhler38f9eba2015-11-11 08:31:52 -080056 // Map from roots to their types.
57 // Instances are only included if they are roots, and the collection of root
58 // types is guaranteed to be non-empty.
Richard Uhler1a5baaa2015-12-21 12:47:26 -080059 private final Map<Instance, Collection<RootType>> mRoots
60 = new HashMap<Instance, Collection<RootType>>();
Richard Uhler38f9eba2015-11-11 08:31:52 -080061
Richard Uhler1a5baaa2015-12-21 12:47:26 -080062 private final Site mRootSite = new Site("ROOT");
63 private final Map<Heap, Long> mHeapSizes = new HashMap<Heap, Long>();
64
65 private final List<InstanceUtils.NativeAllocation> mNativeAllocations
66 = new ArrayList<InstanceUtils.NativeAllocation>();
Richard Uhlerb730b782015-07-15 16:01:58 -070067
Richard Uhler35244722015-09-10 16:45:54 -070068 /**
69 * Create an AhatSnapshot from an hprof file.
70 */
71 public static AhatSnapshot fromHprof(File hprof) throws IOException {
Richard Uhlerc237b392015-10-20 10:03:38 -070072 Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprof));
Richard Uhler35244722015-09-10 16:45:54 -070073 snapshot.computeDominators();
74 return new AhatSnapshot(snapshot);
75 }
76
77 /**
78 * Construct an AhatSnapshot for the given perflib snapshot.
79 * Ther user is responsible for calling snapshot.computeDominators before
80 * calling this AhatSnapshot constructor.
81 */
82 private AhatSnapshot(Snapshot snapshot) {
Richard Uhlerb730b782015-07-15 16:01:58 -070083 mSnapshot = snapshot;
84 mHeaps = new ArrayList<Heap>(mSnapshot.getHeaps());
Richard Uhlerb730b782015-07-15 16:01:58 -070085
86 ClassObj javaLangClass = mSnapshot.findClass("java.lang.Class");
87 for (Heap heap : mHeaps) {
Richard Uhler6919a012015-12-15 09:15:00 -080088 // Use a single element array for the total to act as a reference to a
89 // long.
90 final long[] total = new long[]{0};
91 TObjectProcedure<Instance> processInstance = new TObjectProcedure<Instance>() {
92 @Override
93 public boolean execute(Instance inst) {
94 Instance dominator = inst.getImmediateDominator();
95 if (dominator != null) {
96 total[0] += inst.getSize();
Richard Uhlerb730b782015-07-15 16:01:58 -070097
Richard Uhler6919a012015-12-15 09:15:00 -080098 if (dominator == Snapshot.SENTINEL_ROOT) {
99 mRooted.add(inst);
100 }
Richard Uhler7a16adb2015-11-11 09:13:23 -0800101
Richard Uhler6919a012015-12-15 09:15:00 -0800102 // Properly label the class of a class object.
103 if (inst instanceof ClassObj && javaLangClass != null && inst.getClassObj() == null) {
104 inst.setClassId(javaLangClass.getId());
105 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700106
Richard Uhler6919a012015-12-15 09:15:00 -0800107 // Update dominated instances.
108 List<Instance> instances = mDominated.get(dominator);
109 if (instances == null) {
110 instances = new ArrayList<Instance>();
111 mDominated.put(dominator, instances);
112 }
113 instances.add(inst);
Richard Uhlerb730b782015-07-15 16:01:58 -0700114
Richard Uhler6919a012015-12-15 09:15:00 -0800115 // Update sites.
116 List<StackFrame> path = Collections.emptyList();
117 StackTrace stack = getStack(inst);
118 int stackId = getStackTraceSerialNumber(stack);
119 if (stack != null) {
120 StackFrame[] frames = getStackFrames(stack);
121 if (frames != null && frames.length > 0) {
122 path = Lists.reverse(Arrays.asList(frames));
123 }
124 }
125 mRootSite.add(stackId, 0, path.iterator(), inst);
126
127 // Update native allocations.
128 InstanceUtils.NativeAllocation alloc = InstanceUtils.getNativeAllocation(inst);
129 if (alloc != null) {
130 mNativeAllocations.add(alloc);
Richard Uhlerb730b782015-07-15 16:01:58 -0700131 }
132 }
Richard Uhler6919a012015-12-15 09:15:00 -0800133 return true;
Richard Uhlerb730b782015-07-15 16:01:58 -0700134 }
Richard Uhler6919a012015-12-15 09:15:00 -0800135 };
136 for (Instance instance : heap.getClasses()) {
137 processInstance.execute(instance);
Richard Uhlerb730b782015-07-15 16:01:58 -0700138 }
Richard Uhler6919a012015-12-15 09:15:00 -0800139 heap.forEachInstance(processInstance);
140 mHeapSizes.put(heap, total[0]);
Richard Uhlerb730b782015-07-15 16:01:58 -0700141 }
Richard Uhler38f9eba2015-11-11 08:31:52 -0800142
143 // Record the roots and their types.
Richard Uhler38f9eba2015-11-11 08:31:52 -0800144 for (RootObj root : snapshot.getGCRoots()) {
145 Instance inst = root.getReferredInstance();
146 Collection<RootType> types = mRoots.get(inst);
147 if (types == null) {
148 types = new HashSet<RootType>();
149 mRoots.put(inst, types);
150 }
151 types.add(root.getRootType());
152 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700153 }
154
Richard Uhler35244722015-09-10 16:45:54 -0700155 // Note: This method is exposed for testing purposes.
156 public ClassObj findClass(String name) {
157 return mSnapshot.findClass(name);
158 }
159
Richard Uhlerb730b782015-07-15 16:01:58 -0700160 public Instance findInstance(long id) {
161 return mSnapshot.findInstance(id);
162 }
163
164 public int getHeapIndex(Heap heap) {
165 return mSnapshot.getHeapIndex(heap);
166 }
167
168 public Heap getHeap(String name) {
169 return mSnapshot.getHeap(name);
170 }
171
Richard Uhler7a16adb2015-11-11 09:13:23 -0800172 /**
173 * Returns a collection of instances whose immediate dominator is the
174 * SENTINEL_ROOT.
175 */
176 public List<Instance> getRooted() {
177 return mRooted;
Richard Uhlerb730b782015-07-15 16:01:58 -0700178 }
179
Richard Uhler38f9eba2015-11-11 08:31:52 -0800180 /**
181 * Returns true if the given instance is a root.
182 */
183 public boolean isRoot(Instance inst) {
184 return mRoots.containsKey(inst);
185 }
186
187 /**
188 * Returns the list of root types for the given instance, or null if the
189 * instance is not a root.
190 */
191 public Collection<RootType> getRootTypes(Instance inst) {
192 return mRoots.get(inst);
193 }
194
Richard Uhlerb730b782015-07-15 16:01:58 -0700195 public List<Heap> getHeaps() {
196 return mHeaps;
197 }
198
199 public Site getRootSite() {
200 return mRootSite;
201 }
202
203 /**
204 * Look up the site at which the given object was allocated.
205 */
206 public Site getSiteForInstance(Instance inst) {
207 Site site = mRootSite;
208 StackTrace stack = getStack(inst);
209 if (stack != null) {
210 StackFrame[] frames = getStackFrames(stack);
211 if (frames != null) {
212 List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
213 site = mRootSite.getChild(path.iterator());
214 }
215 }
216 return site;
217 }
218
219 /**
220 * Return a list of those objects immediately dominated by the given
221 * instance.
222 */
223 public List<Instance> getDominated(Instance inst) {
224 return mDominated.get(inst);
225 }
226
227 /**
228 * Return the total size of reachable objects allocated on the given heap.
229 */
230 public long getHeapSize(Heap heap) {
231 return mHeapSizes.get(heap);
232 }
233
234 /**
235 * Return the class name for the given class object.
236 * classObj may be null, in which case "(class unknown)" is returned.
237 */
238 public static String getClassName(ClassObj classObj) {
239 if (classObj == null) {
240 return "(class unknown)";
241 }
242 return classObj.getClassName();
243 }
244
245 // Return the stack where the given instance was allocated.
246 private static StackTrace getStack(Instance inst) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700247 return inst.getStack();
Richard Uhlerb730b782015-07-15 16:01:58 -0700248 }
249
250 // Return the list of stack frames for a stack trace.
251 private static StackFrame[] getStackFrames(StackTrace stack) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700252 return stack.getFrames();
Richard Uhlerb730b782015-07-15 16:01:58 -0700253 }
254
255 // Return the serial number of the given stack trace.
256 private static int getStackTraceSerialNumber(StackTrace stack) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700257 return stack.getSerialNumber();
Richard Uhlerb730b782015-07-15 16:01:58 -0700258 }
259
260 // Get the site associated with the given stack id and depth.
261 // Returns the root site if no such site found.
262 // depth of -1 means the full stack.
263 public Site getSite(int stackId, int depth) {
264 Site site = mRootSite;
265 StackTrace stack = mSnapshot.getStackTrace(stackId);
266 if (stack != null) {
267 StackFrame[] frames = getStackFrames(stack);
268 if (frames != null) {
269 List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
270 if (depth >= 0) {
271 path = path.subList(0, depth);
272 }
273 site = mRootSite.getChild(path.iterator());
274 }
275 }
276 return site;
277 }
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800278
279 // Return a list of known native allocations in the snapshot.
280 public List<InstanceUtils.NativeAllocation> getNativeAllocations() {
281 return mNativeAllocations;
282 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700283}