blob: 2adec6f17b9e26d134c6a0bfb38e46aa1fc4451b [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.Iterables;
29import com.google.common.collect.Lists;
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) {
88 long total = 0;
89 for (Instance inst : Iterables.concat(heap.getClasses(), heap.getInstances())) {
90 Instance dominator = inst.getImmediateDominator();
91 if (dominator != null) {
92 total += inst.getSize();
93
Richard Uhler7a16adb2015-11-11 09:13:23 -080094 if (dominator == Snapshot.SENTINEL_ROOT) {
95 mRooted.add(inst);
96 }
97
Richard Uhlerb730b782015-07-15 16:01:58 -070098 // Properly label the class of a class object.
99 if (inst instanceof ClassObj && javaLangClass != null && inst.getClassObj() == null) {
100 inst.setClassId(javaLangClass.getId());
101 }
102
103 // Update dominated instances.
104 List<Instance> instances = mDominated.get(dominator);
105 if (instances == null) {
106 instances = new ArrayList<Instance>();
107 mDominated.put(dominator, instances);
108 }
109 instances.add(inst);
110
111 // Update sites.
112 List<StackFrame> path = Collections.emptyList();
113 StackTrace stack = getStack(inst);
114 int stackId = getStackTraceSerialNumber(stack);
115 if (stack != null) {
116 StackFrame[] frames = getStackFrames(stack);
117 if (frames != null && frames.length > 0) {
118 path = Lists.reverse(Arrays.asList(frames));
119 }
120 }
121 mRootSite.add(stackId, 0, path.iterator(), inst);
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800122
123 // Update native allocations.
124 InstanceUtils.NativeAllocation alloc = InstanceUtils.getNativeAllocation(inst);
125 if (alloc != null) {
126 mNativeAllocations.add(alloc);
127 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700128 }
129 }
130 mHeapSizes.put(heap, total);
131 }
Richard Uhler38f9eba2015-11-11 08:31:52 -0800132
133 // Record the roots and their types.
Richard Uhler38f9eba2015-11-11 08:31:52 -0800134 for (RootObj root : snapshot.getGCRoots()) {
135 Instance inst = root.getReferredInstance();
136 Collection<RootType> types = mRoots.get(inst);
137 if (types == null) {
138 types = new HashSet<RootType>();
139 mRoots.put(inst, types);
140 }
141 types.add(root.getRootType());
142 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700143 }
144
Richard Uhler35244722015-09-10 16:45:54 -0700145 // Note: This method is exposed for testing purposes.
146 public ClassObj findClass(String name) {
147 return mSnapshot.findClass(name);
148 }
149
Richard Uhlerb730b782015-07-15 16:01:58 -0700150 public Instance findInstance(long id) {
151 return mSnapshot.findInstance(id);
152 }
153
154 public int getHeapIndex(Heap heap) {
155 return mSnapshot.getHeapIndex(heap);
156 }
157
158 public Heap getHeap(String name) {
159 return mSnapshot.getHeap(name);
160 }
161
Richard Uhler7a16adb2015-11-11 09:13:23 -0800162 /**
163 * Returns a collection of instances whose immediate dominator is the
164 * SENTINEL_ROOT.
165 */
166 public List<Instance> getRooted() {
167 return mRooted;
Richard Uhlerb730b782015-07-15 16:01:58 -0700168 }
169
Richard Uhler38f9eba2015-11-11 08:31:52 -0800170 /**
171 * Returns true if the given instance is a root.
172 */
173 public boolean isRoot(Instance inst) {
174 return mRoots.containsKey(inst);
175 }
176
177 /**
178 * Returns the list of root types for the given instance, or null if the
179 * instance is not a root.
180 */
181 public Collection<RootType> getRootTypes(Instance inst) {
182 return mRoots.get(inst);
183 }
184
Richard Uhlerb730b782015-07-15 16:01:58 -0700185 public List<Heap> getHeaps() {
186 return mHeaps;
187 }
188
189 public Site getRootSite() {
190 return mRootSite;
191 }
192
193 /**
194 * Look up the site at which the given object was allocated.
195 */
196 public Site getSiteForInstance(Instance inst) {
197 Site site = mRootSite;
198 StackTrace stack = getStack(inst);
199 if (stack != null) {
200 StackFrame[] frames = getStackFrames(stack);
201 if (frames != null) {
202 List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
203 site = mRootSite.getChild(path.iterator());
204 }
205 }
206 return site;
207 }
208
209 /**
210 * Return a list of those objects immediately dominated by the given
211 * instance.
212 */
213 public List<Instance> getDominated(Instance inst) {
214 return mDominated.get(inst);
215 }
216
217 /**
218 * Return the total size of reachable objects allocated on the given heap.
219 */
220 public long getHeapSize(Heap heap) {
221 return mHeapSizes.get(heap);
222 }
223
224 /**
225 * Return the class name for the given class object.
226 * classObj may be null, in which case "(class unknown)" is returned.
227 */
228 public static String getClassName(ClassObj classObj) {
229 if (classObj == null) {
230 return "(class unknown)";
231 }
232 return classObj.getClassName();
233 }
234
235 // Return the stack where the given instance was allocated.
236 private static StackTrace getStack(Instance inst) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700237 return inst.getStack();
Richard Uhlerb730b782015-07-15 16:01:58 -0700238 }
239
240 // Return the list of stack frames for a stack trace.
241 private static StackFrame[] getStackFrames(StackTrace stack) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700242 return stack.getFrames();
Richard Uhlerb730b782015-07-15 16:01:58 -0700243 }
244
245 // Return the serial number of the given stack trace.
246 private static int getStackTraceSerialNumber(StackTrace stack) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700247 return stack.getSerialNumber();
Richard Uhlerb730b782015-07-15 16:01:58 -0700248 }
249
250 // Get the site associated with the given stack id and depth.
251 // Returns the root site if no such site found.
252 // depth of -1 means the full stack.
253 public Site getSite(int stackId, int depth) {
254 Site site = mRootSite;
255 StackTrace stack = mSnapshot.getStackTrace(stackId);
256 if (stack != null) {
257 StackFrame[] frames = getStackFrames(stack);
258 if (frames != null) {
259 List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
260 if (depth >= 0) {
261 path = path.subList(0, depth);
262 }
263 site = mRootSite.getChild(path.iterator());
264 }
265 }
266 return site;
267 }
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800268
269 // Return a list of known native allocations in the snapshot.
270 public List<InstanceUtils.NativeAllocation> getNativeAllocations() {
271 return mNativeAllocations;
272 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700273}