blob: e6f8411c9054ccf2e7e4af73204b59311bd282f9 [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
Gus Smithe6a63872016-06-09 16:50:44 -070019import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
Richard Uhlerb730b782015-07-15 16:01:58 -070020import com.android.tools.perflib.heap.ClassObj;
21import com.android.tools.perflib.heap.Heap;
22import com.android.tools.perflib.heap.Instance;
Richard Uhler38f9eba2015-11-11 08:31:52 -080023import com.android.tools.perflib.heap.RootObj;
24import com.android.tools.perflib.heap.RootType;
Richard Uhlerb730b782015-07-15 16:01:58 -070025import com.android.tools.perflib.heap.Snapshot;
26import com.android.tools.perflib.heap.StackFrame;
27import com.android.tools.perflib.heap.StackTrace;
Gus Smithe6a63872016-06-09 16:50:44 -070028
Richard Uhlerb730b782015-07-15 16:01:58 -070029import com.google.common.collect.Lists;
Gus Smithe6a63872016-06-09 16:50:44 -070030
Richard Uhler6919a012015-12-15 09:15:00 -080031import gnu.trove.TObjectProcedure;
Gus Smithe6a63872016-06-09 16:50:44 -070032
Richard Uhler35244722015-09-10 16:45:54 -070033import java.io.File;
34import java.io.IOException;
Richard Uhlerb730b782015-07-15 16:01:58 -070035import java.util.ArrayList;
36import java.util.Arrays;
Richard Uhler38f9eba2015-11-11 08:31:52 -080037import java.util.Collection;
Richard Uhlerb730b782015-07-15 16:01:58 -070038import java.util.Collections;
39import java.util.HashMap;
Richard Uhler38f9eba2015-11-11 08:31:52 -080040import java.util.HashSet;
Richard Uhlerb730b782015-07-15 16:01:58 -070041import java.util.List;
42import java.util.Map;
43
44/**
45 * A wrapper over the perflib snapshot that provides the behavior we use in
46 * ahat.
47 */
48class AhatSnapshot {
Richard Uhler1a5baaa2015-12-21 12:47:26 -080049 private final Snapshot mSnapshot;
50 private final List<Heap> mHeaps;
Richard Uhlerb730b782015-07-15 16:01:58 -070051
52 // Map from Instance to the list of Instances it immediately dominates.
Richard Uhler1a5baaa2015-12-21 12:47:26 -080053 private final Map<Instance, List<Instance>> mDominated
54 = new HashMap<Instance, List<Instance>>();
Richard Uhlerb730b782015-07-15 16:01:58 -070055
Richard Uhler7a16adb2015-11-11 09:13:23 -080056 // Collection of objects whose immediate dominator is the SENTINEL_ROOT.
Richard Uhler1a5baaa2015-12-21 12:47:26 -080057 private final List<Instance> mRooted = new ArrayList<Instance>();
Richard Uhler7a16adb2015-11-11 09:13:23 -080058
Richard Uhler38f9eba2015-11-11 08:31:52 -080059 // Map from roots to their types.
60 // Instances are only included if they are roots, and the collection of root
61 // types is guaranteed to be non-empty.
Richard Uhler1a5baaa2015-12-21 12:47:26 -080062 private final Map<Instance, Collection<RootType>> mRoots
63 = new HashMap<Instance, Collection<RootType>>();
Richard Uhler38f9eba2015-11-11 08:31:52 -080064
Richard Uhler1a5baaa2015-12-21 12:47:26 -080065 private final Site mRootSite = new Site("ROOT");
66 private final Map<Heap, Long> mHeapSizes = new HashMap<Heap, Long>();
67
68 private final List<InstanceUtils.NativeAllocation> mNativeAllocations
69 = new ArrayList<InstanceUtils.NativeAllocation>();
Richard Uhlerb730b782015-07-15 16:01:58 -070070
Richard Uhler35244722015-09-10 16:45:54 -070071 /**
72 * Create an AhatSnapshot from an hprof file.
73 */
74 public static AhatSnapshot fromHprof(File hprof) throws IOException {
Richard Uhlerc237b392015-10-20 10:03:38 -070075 Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprof));
Richard Uhler35244722015-09-10 16:45:54 -070076 snapshot.computeDominators();
77 return new AhatSnapshot(snapshot);
78 }
79
80 /**
81 * Construct an AhatSnapshot for the given perflib snapshot.
82 * Ther user is responsible for calling snapshot.computeDominators before
83 * calling this AhatSnapshot constructor.
84 */
85 private AhatSnapshot(Snapshot snapshot) {
Richard Uhlerb730b782015-07-15 16:01:58 -070086 mSnapshot = snapshot;
87 mHeaps = new ArrayList<Heap>(mSnapshot.getHeaps());
Richard Uhlerb730b782015-07-15 16:01:58 -070088
89 ClassObj javaLangClass = mSnapshot.findClass("java.lang.Class");
90 for (Heap heap : mHeaps) {
Richard Uhler6919a012015-12-15 09:15:00 -080091 // Use a single element array for the total to act as a reference to a
92 // long.
93 final long[] total = new long[]{0};
94 TObjectProcedure<Instance> processInstance = new TObjectProcedure<Instance>() {
95 @Override
96 public boolean execute(Instance inst) {
97 Instance dominator = inst.getImmediateDominator();
98 if (dominator != null) {
99 total[0] += inst.getSize();
Richard Uhlerb730b782015-07-15 16:01:58 -0700100
Richard Uhler6919a012015-12-15 09:15:00 -0800101 if (dominator == Snapshot.SENTINEL_ROOT) {
102 mRooted.add(inst);
103 }
Richard Uhler7a16adb2015-11-11 09:13:23 -0800104
Richard Uhler6919a012015-12-15 09:15:00 -0800105 // Properly label the class of a class object.
106 if (inst instanceof ClassObj && javaLangClass != null && inst.getClassObj() == null) {
107 inst.setClassId(javaLangClass.getId());
108 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700109
Richard Uhler6919a012015-12-15 09:15:00 -0800110 // Update dominated instances.
111 List<Instance> instances = mDominated.get(dominator);
112 if (instances == null) {
113 instances = new ArrayList<Instance>();
114 mDominated.put(dominator, instances);
115 }
116 instances.add(inst);
Richard Uhlerb730b782015-07-15 16:01:58 -0700117
Richard Uhler6919a012015-12-15 09:15:00 -0800118 // Update sites.
119 List<StackFrame> path = Collections.emptyList();
120 StackTrace stack = getStack(inst);
121 int stackId = getStackTraceSerialNumber(stack);
122 if (stack != null) {
123 StackFrame[] frames = getStackFrames(stack);
124 if (frames != null && frames.length > 0) {
125 path = Lists.reverse(Arrays.asList(frames));
126 }
127 }
128 mRootSite.add(stackId, 0, path.iterator(), inst);
129
130 // Update native allocations.
131 InstanceUtils.NativeAllocation alloc = InstanceUtils.getNativeAllocation(inst);
132 if (alloc != null) {
133 mNativeAllocations.add(alloc);
Richard Uhlerb730b782015-07-15 16:01:58 -0700134 }
135 }
Richard Uhler6919a012015-12-15 09:15:00 -0800136 return true;
Richard Uhlerb730b782015-07-15 16:01:58 -0700137 }
Richard Uhler6919a012015-12-15 09:15:00 -0800138 };
139 for (Instance instance : heap.getClasses()) {
140 processInstance.execute(instance);
Richard Uhlerb730b782015-07-15 16:01:58 -0700141 }
Richard Uhler6919a012015-12-15 09:15:00 -0800142 heap.forEachInstance(processInstance);
143 mHeapSizes.put(heap, total[0]);
Richard Uhlerb730b782015-07-15 16:01:58 -0700144 }
Richard Uhler38f9eba2015-11-11 08:31:52 -0800145
146 // Record the roots and their types.
Richard Uhler38f9eba2015-11-11 08:31:52 -0800147 for (RootObj root : snapshot.getGCRoots()) {
148 Instance inst = root.getReferredInstance();
149 Collection<RootType> types = mRoots.get(inst);
150 if (types == null) {
151 types = new HashSet<RootType>();
152 mRoots.put(inst, types);
153 }
154 types.add(root.getRootType());
155 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700156 }
157
Richard Uhler35244722015-09-10 16:45:54 -0700158 // Note: This method is exposed for testing purposes.
159 public ClassObj findClass(String name) {
160 return mSnapshot.findClass(name);
161 }
162
Richard Uhlerb730b782015-07-15 16:01:58 -0700163 public Instance findInstance(long id) {
164 return mSnapshot.findInstance(id);
165 }
166
167 public int getHeapIndex(Heap heap) {
168 return mSnapshot.getHeapIndex(heap);
169 }
170
171 public Heap getHeap(String name) {
172 return mSnapshot.getHeap(name);
173 }
174
Richard Uhler7a16adb2015-11-11 09:13:23 -0800175 /**
176 * Returns a collection of instances whose immediate dominator is the
177 * SENTINEL_ROOT.
178 */
179 public List<Instance> getRooted() {
180 return mRooted;
Richard Uhlerb730b782015-07-15 16:01:58 -0700181 }
182
Richard Uhler38f9eba2015-11-11 08:31:52 -0800183 /**
184 * Returns true if the given instance is a root.
185 */
186 public boolean isRoot(Instance inst) {
187 return mRoots.containsKey(inst);
188 }
189
190 /**
191 * Returns the list of root types for the given instance, or null if the
192 * instance is not a root.
193 */
194 public Collection<RootType> getRootTypes(Instance inst) {
195 return mRoots.get(inst);
196 }
197
Richard Uhlerb730b782015-07-15 16:01:58 -0700198 public List<Heap> getHeaps() {
199 return mHeaps;
200 }
201
202 public Site getRootSite() {
203 return mRootSite;
204 }
205
206 /**
207 * Look up the site at which the given object was allocated.
208 */
209 public Site getSiteForInstance(Instance inst) {
210 Site site = mRootSite;
211 StackTrace stack = getStack(inst);
212 if (stack != null) {
213 StackFrame[] frames = getStackFrames(stack);
214 if (frames != null) {
215 List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
216 site = mRootSite.getChild(path.iterator());
217 }
218 }
219 return site;
220 }
221
222 /**
223 * Return a list of those objects immediately dominated by the given
224 * instance.
225 */
226 public List<Instance> getDominated(Instance inst) {
227 return mDominated.get(inst);
228 }
229
230 /**
231 * Return the total size of reachable objects allocated on the given heap.
232 */
233 public long getHeapSize(Heap heap) {
234 return mHeapSizes.get(heap);
235 }
236
237 /**
238 * Return the class name for the given class object.
239 * classObj may be null, in which case "(class unknown)" is returned.
240 */
241 public static String getClassName(ClassObj classObj) {
242 if (classObj == null) {
243 return "(class unknown)";
244 }
245 return classObj.getClassName();
246 }
247
248 // Return the stack where the given instance was allocated.
249 private static StackTrace getStack(Instance inst) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700250 return inst.getStack();
Richard Uhlerb730b782015-07-15 16:01:58 -0700251 }
252
253 // Return the list of stack frames for a stack trace.
254 private static StackFrame[] getStackFrames(StackTrace stack) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700255 return stack.getFrames();
Richard Uhlerb730b782015-07-15 16:01:58 -0700256 }
257
258 // Return the serial number of the given stack trace.
259 private static int getStackTraceSerialNumber(StackTrace stack) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700260 return stack.getSerialNumber();
Richard Uhlerb730b782015-07-15 16:01:58 -0700261 }
262
263 // Get the site associated with the given stack id and depth.
264 // Returns the root site if no such site found.
265 // depth of -1 means the full stack.
266 public Site getSite(int stackId, int depth) {
267 Site site = mRootSite;
268 StackTrace stack = mSnapshot.getStackTrace(stackId);
269 if (stack != null) {
270 StackFrame[] frames = getStackFrames(stack);
271 if (frames != null) {
272 List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
273 if (depth >= 0) {
274 path = path.subList(0, depth);
275 }
276 site = mRootSite.getChild(path.iterator());
277 }
278 }
279 return site;
280 }
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800281
282 // Return a list of known native allocations in the snapshot.
283 public List<InstanceUtils.NativeAllocation> getNativeAllocations() {
284 return mNativeAllocations;
285 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700286}