blob: ba8243f744c4355f4847070ff321f764a07f5a17 [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 Uhlerec78c782016-05-13 14:19:37 -070023import com.android.tools.perflib.heap.ProguardMap;
Richard Uhler38f9eba2015-11-11 08:31:52 -080024import com.android.tools.perflib.heap.RootObj;
25import com.android.tools.perflib.heap.RootType;
Richard Uhlerb730b782015-07-15 16:01:58 -070026import com.android.tools.perflib.heap.Snapshot;
27import com.android.tools.perflib.heap.StackFrame;
28import com.android.tools.perflib.heap.StackTrace;
Gus Smithe6a63872016-06-09 16:50:44 -070029
Richard Uhlerb730b782015-07-15 16:01:58 -070030import com.google.common.collect.Lists;
Gus Smithe6a63872016-06-09 16:50:44 -070031
Richard Uhler6919a012015-12-15 09:15:00 -080032import gnu.trove.TObjectProcedure;
Gus Smithe6a63872016-06-09 16:50:44 -070033
Richard Uhler35244722015-09-10 16:45:54 -070034import java.io.File;
35import java.io.IOException;
Richard Uhlerb730b782015-07-15 16:01:58 -070036import java.util.ArrayList;
37import java.util.Arrays;
Richard Uhler38f9eba2015-11-11 08:31:52 -080038import java.util.Collection;
Richard Uhlerb730b782015-07-15 16:01:58 -070039import java.util.Collections;
40import java.util.HashMap;
Richard Uhler38f9eba2015-11-11 08:31:52 -080041import java.util.HashSet;
Richard Uhlerb730b782015-07-15 16:01:58 -070042import java.util.List;
43import java.util.Map;
44
45/**
46 * A wrapper over the perflib snapshot that provides the behavior we use in
47 * ahat.
48 */
49class AhatSnapshot {
Richard Uhler1a5baaa2015-12-21 12:47:26 -080050 private final Snapshot mSnapshot;
51 private final List<Heap> mHeaps;
Richard Uhlerb730b782015-07-15 16:01:58 -070052
53 // Map from Instance to the list of Instances it immediately dominates.
Richard Uhler1a5baaa2015-12-21 12:47:26 -080054 private final Map<Instance, List<Instance>> mDominated
55 = new HashMap<Instance, List<Instance>>();
Richard Uhlerb730b782015-07-15 16:01:58 -070056
Richard Uhler7a16adb2015-11-11 09:13:23 -080057 // Collection of objects whose immediate dominator is the SENTINEL_ROOT.
Richard Uhler1a5baaa2015-12-21 12:47:26 -080058 private final List<Instance> mRooted = new ArrayList<Instance>();
Richard Uhler7a16adb2015-11-11 09:13:23 -080059
Richard Uhler38f9eba2015-11-11 08:31:52 -080060 // Map from roots to their types.
61 // Instances are only included if they are roots, and the collection of root
62 // types is guaranteed to be non-empty.
Richard Uhler1a5baaa2015-12-21 12:47:26 -080063 private final Map<Instance, Collection<RootType>> mRoots
64 = new HashMap<Instance, Collection<RootType>>();
Richard Uhler38f9eba2015-11-11 08:31:52 -080065
Richard Uhler1a5baaa2015-12-21 12:47:26 -080066 private final Site mRootSite = new Site("ROOT");
67 private final Map<Heap, Long> mHeapSizes = new HashMap<Heap, Long>();
68
69 private final List<InstanceUtils.NativeAllocation> mNativeAllocations
70 = new ArrayList<InstanceUtils.NativeAllocation>();
Richard Uhlerb730b782015-07-15 16:01:58 -070071
Richard Uhler35244722015-09-10 16:45:54 -070072 /**
73 * Create an AhatSnapshot from an hprof file.
74 */
Richard Uhlerec78c782016-05-13 14:19:37 -070075 public static AhatSnapshot fromHprof(File hprof, ProguardMap map) throws IOException {
76 Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprof), map);
Richard Uhler35244722015-09-10 16:45:54 -070077 snapshot.computeDominators();
78 return new AhatSnapshot(snapshot);
79 }
80
81 /**
82 * Construct an AhatSnapshot for the given perflib snapshot.
83 * Ther user is responsible for calling snapshot.computeDominators before
84 * calling this AhatSnapshot constructor.
85 */
86 private AhatSnapshot(Snapshot snapshot) {
Richard Uhlerb730b782015-07-15 16:01:58 -070087 mSnapshot = snapshot;
88 mHeaps = new ArrayList<Heap>(mSnapshot.getHeaps());
Richard Uhlerb730b782015-07-15 16:01:58 -070089
Richard Uhler69272db2016-08-05 16:26:57 -070090 final ClassObj javaLangClass = mSnapshot.findClass("java.lang.Class");
Richard Uhlerb730b782015-07-15 16:01:58 -070091 for (Heap heap : mHeaps) {
Richard Uhler6919a012015-12-15 09:15:00 -080092 // Use a single element array for the total to act as a reference to a
93 // long.
94 final long[] total = new long[]{0};
95 TObjectProcedure<Instance> processInstance = new TObjectProcedure<Instance>() {
96 @Override
97 public boolean execute(Instance inst) {
98 Instance dominator = inst.getImmediateDominator();
99 if (dominator != null) {
100 total[0] += inst.getSize();
Richard Uhlerb730b782015-07-15 16:01:58 -0700101
Richard Uhler6919a012015-12-15 09:15:00 -0800102 if (dominator == Snapshot.SENTINEL_ROOT) {
103 mRooted.add(inst);
104 }
Richard Uhler7a16adb2015-11-11 09:13:23 -0800105
Richard Uhler6919a012015-12-15 09:15:00 -0800106 // Properly label the class of a class object.
107 if (inst instanceof ClassObj && javaLangClass != null && inst.getClassObj() == null) {
108 inst.setClassId(javaLangClass.getId());
109 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700110
Richard Uhler6919a012015-12-15 09:15:00 -0800111 // Update dominated instances.
112 List<Instance> instances = mDominated.get(dominator);
113 if (instances == null) {
114 instances = new ArrayList<Instance>();
115 mDominated.put(dominator, instances);
116 }
117 instances.add(inst);
Richard Uhlerb730b782015-07-15 16:01:58 -0700118
Richard Uhler6919a012015-12-15 09:15:00 -0800119 // Update sites.
120 List<StackFrame> path = Collections.emptyList();
121 StackTrace stack = getStack(inst);
122 int stackId = getStackTraceSerialNumber(stack);
123 if (stack != null) {
124 StackFrame[] frames = getStackFrames(stack);
125 if (frames != null && frames.length > 0) {
126 path = Lists.reverse(Arrays.asList(frames));
127 }
128 }
129 mRootSite.add(stackId, 0, path.iterator(), inst);
130
131 // Update native allocations.
132 InstanceUtils.NativeAllocation alloc = InstanceUtils.getNativeAllocation(inst);
133 if (alloc != null) {
134 mNativeAllocations.add(alloc);
Richard Uhlerb730b782015-07-15 16:01:58 -0700135 }
136 }
Richard Uhler6919a012015-12-15 09:15:00 -0800137 return true;
Richard Uhlerb730b782015-07-15 16:01:58 -0700138 }
Richard Uhler6919a012015-12-15 09:15:00 -0800139 };
140 for (Instance instance : heap.getClasses()) {
141 processInstance.execute(instance);
Richard Uhlerb730b782015-07-15 16:01:58 -0700142 }
Richard Uhler6919a012015-12-15 09:15:00 -0800143 heap.forEachInstance(processInstance);
144 mHeapSizes.put(heap, total[0]);
Richard Uhlerb730b782015-07-15 16:01:58 -0700145 }
Richard Uhler38f9eba2015-11-11 08:31:52 -0800146
147 // Record the roots and their types.
Richard Uhler38f9eba2015-11-11 08:31:52 -0800148 for (RootObj root : snapshot.getGCRoots()) {
149 Instance inst = root.getReferredInstance();
150 Collection<RootType> types = mRoots.get(inst);
151 if (types == null) {
152 types = new HashSet<RootType>();
153 mRoots.put(inst, types);
154 }
155 types.add(root.getRootType());
156 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700157 }
158
Richard Uhler35244722015-09-10 16:45:54 -0700159 // Note: This method is exposed for testing purposes.
160 public ClassObj findClass(String name) {
161 return mSnapshot.findClass(name);
162 }
163
Richard Uhlerb730b782015-07-15 16:01:58 -0700164 public Instance findInstance(long id) {
165 return mSnapshot.findInstance(id);
166 }
167
168 public int getHeapIndex(Heap heap) {
169 return mSnapshot.getHeapIndex(heap);
170 }
171
172 public Heap getHeap(String name) {
173 return mSnapshot.getHeap(name);
174 }
175
Richard Uhler7a16adb2015-11-11 09:13:23 -0800176 /**
177 * Returns a collection of instances whose immediate dominator is the
178 * SENTINEL_ROOT.
179 */
180 public List<Instance> getRooted() {
181 return mRooted;
Richard Uhlerb730b782015-07-15 16:01:58 -0700182 }
183
Richard Uhler38f9eba2015-11-11 08:31:52 -0800184 /**
185 * Returns true if the given instance is a root.
186 */
187 public boolean isRoot(Instance inst) {
188 return mRoots.containsKey(inst);
189 }
190
191 /**
192 * Returns the list of root types for the given instance, or null if the
193 * instance is not a root.
194 */
195 public Collection<RootType> getRootTypes(Instance inst) {
196 return mRoots.get(inst);
197 }
198
Richard Uhlerb730b782015-07-15 16:01:58 -0700199 public List<Heap> getHeaps() {
200 return mHeaps;
201 }
202
203 public Site getRootSite() {
204 return mRootSite;
205 }
206
207 /**
208 * Look up the site at which the given object was allocated.
209 */
210 public Site getSiteForInstance(Instance inst) {
211 Site site = mRootSite;
212 StackTrace stack = getStack(inst);
213 if (stack != null) {
214 StackFrame[] frames = getStackFrames(stack);
215 if (frames != null) {
216 List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
217 site = mRootSite.getChild(path.iterator());
218 }
219 }
220 return site;
221 }
222
223 /**
224 * Return a list of those objects immediately dominated by the given
225 * instance.
226 */
227 public List<Instance> getDominated(Instance inst) {
228 return mDominated.get(inst);
229 }
230
231 /**
232 * Return the total size of reachable objects allocated on the given heap.
233 */
234 public long getHeapSize(Heap heap) {
235 return mHeapSizes.get(heap);
236 }
237
238 /**
239 * Return the class name for the given class object.
240 * classObj may be null, in which case "(class unknown)" is returned.
241 */
242 public static String getClassName(ClassObj classObj) {
243 if (classObj == null) {
244 return "(class unknown)";
245 }
246 return classObj.getClassName();
247 }
248
249 // Return the stack where the given instance was allocated.
250 private static StackTrace getStack(Instance inst) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700251 return inst.getStack();
Richard Uhlerb730b782015-07-15 16:01:58 -0700252 }
253
254 // Return the list of stack frames for a stack trace.
255 private static StackFrame[] getStackFrames(StackTrace stack) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700256 return stack.getFrames();
Richard Uhlerb730b782015-07-15 16:01:58 -0700257 }
258
259 // Return the serial number of the given stack trace.
260 private static int getStackTraceSerialNumber(StackTrace stack) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700261 return stack.getSerialNumber();
Richard Uhlerb730b782015-07-15 16:01:58 -0700262 }
263
264 // Get the site associated with the given stack id and depth.
265 // Returns the root site if no such site found.
266 // depth of -1 means the full stack.
267 public Site getSite(int stackId, int depth) {
268 Site site = mRootSite;
269 StackTrace stack = mSnapshot.getStackTrace(stackId);
270 if (stack != null) {
271 StackFrame[] frames = getStackFrames(stack);
272 if (frames != null) {
273 List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
274 if (depth >= 0) {
275 path = path.subList(0, depth);
276 }
277 site = mRootSite.getChild(path.iterator());
278 }
279 }
280 return site;
281 }
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800282
283 // Return a list of known native allocations in the snapshot.
284 public List<InstanceUtils.NativeAllocation> getNativeAllocations() {
285 return mNativeAllocations;
286 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700287}