blob: fc7911b71b39f45883be50f6e37ab69a00819a48 [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 {
46 private Snapshot mSnapshot;
47 private List<Heap> mHeaps;
48
49 // Map from Instance to the list of Instances it immediately dominates.
50 private Map<Instance, List<Instance>> mDominated;
51
Richard Uhler7a16adb2015-11-11 09:13:23 -080052 // Collection of objects whose immediate dominator is the SENTINEL_ROOT.
53 private List<Instance> mRooted;
54
Richard Uhler38f9eba2015-11-11 08:31:52 -080055 // Map from roots to their types.
56 // Instances are only included if they are roots, and the collection of root
57 // types is guaranteed to be non-empty.
58 private Map<Instance, Collection<RootType>> mRoots;
59
Richard Uhlerb730b782015-07-15 16:01:58 -070060 private Site mRootSite;
61 private Map<Heap, Long> mHeapSizes;
62
Richard Uhler35244722015-09-10 16:45:54 -070063 /**
64 * Create an AhatSnapshot from an hprof file.
65 */
66 public static AhatSnapshot fromHprof(File hprof) throws IOException {
Richard Uhlerc237b392015-10-20 10:03:38 -070067 Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprof));
Richard Uhler35244722015-09-10 16:45:54 -070068 snapshot.computeDominators();
69 return new AhatSnapshot(snapshot);
70 }
71
72 /**
73 * Construct an AhatSnapshot for the given perflib snapshot.
74 * Ther user is responsible for calling snapshot.computeDominators before
75 * calling this AhatSnapshot constructor.
76 */
77 private AhatSnapshot(Snapshot snapshot) {
Richard Uhlerb730b782015-07-15 16:01:58 -070078 mSnapshot = snapshot;
79 mHeaps = new ArrayList<Heap>(mSnapshot.getHeaps());
80 mDominated = new HashMap<Instance, List<Instance>>();
81 mRootSite = new Site("ROOT");
82 mHeapSizes = new HashMap<Heap, Long>();
Richard Uhler7a16adb2015-11-11 09:13:23 -080083 mRooted = new ArrayList<Instance>();
Richard Uhlerb730b782015-07-15 16:01:58 -070084
85 ClassObj javaLangClass = mSnapshot.findClass("java.lang.Class");
86 for (Heap heap : mHeaps) {
87 long total = 0;
88 for (Instance inst : Iterables.concat(heap.getClasses(), heap.getInstances())) {
89 Instance dominator = inst.getImmediateDominator();
90 if (dominator != null) {
91 total += inst.getSize();
92
Richard Uhler7a16adb2015-11-11 09:13:23 -080093 if (dominator == Snapshot.SENTINEL_ROOT) {
94 mRooted.add(inst);
95 }
96
Richard Uhlerb730b782015-07-15 16:01:58 -070097 // Properly label the class of a class object.
98 if (inst instanceof ClassObj && javaLangClass != null && inst.getClassObj() == null) {
99 inst.setClassId(javaLangClass.getId());
100 }
101
102 // Update dominated instances.
103 List<Instance> instances = mDominated.get(dominator);
104 if (instances == null) {
105 instances = new ArrayList<Instance>();
106 mDominated.put(dominator, instances);
107 }
108 instances.add(inst);
109
110 // Update sites.
111 List<StackFrame> path = Collections.emptyList();
112 StackTrace stack = getStack(inst);
113 int stackId = getStackTraceSerialNumber(stack);
114 if (stack != null) {
115 StackFrame[] frames = getStackFrames(stack);
116 if (frames != null && frames.length > 0) {
117 path = Lists.reverse(Arrays.asList(frames));
118 }
119 }
120 mRootSite.add(stackId, 0, path.iterator(), inst);
121 }
122 }
123 mHeapSizes.put(heap, total);
124 }
Richard Uhler38f9eba2015-11-11 08:31:52 -0800125
126 // Record the roots and their types.
127 mRoots = new HashMap<Instance, Collection<RootType>>();
128 for (RootObj root : snapshot.getGCRoots()) {
129 Instance inst = root.getReferredInstance();
130 Collection<RootType> types = mRoots.get(inst);
131 if (types == null) {
132 types = new HashSet<RootType>();
133 mRoots.put(inst, types);
134 }
135 types.add(root.getRootType());
136 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700137 }
138
Richard Uhler35244722015-09-10 16:45:54 -0700139 // Note: This method is exposed for testing purposes.
140 public ClassObj findClass(String name) {
141 return mSnapshot.findClass(name);
142 }
143
Richard Uhlerb730b782015-07-15 16:01:58 -0700144 public Instance findInstance(long id) {
145 return mSnapshot.findInstance(id);
146 }
147
148 public int getHeapIndex(Heap heap) {
149 return mSnapshot.getHeapIndex(heap);
150 }
151
152 public Heap getHeap(String name) {
153 return mSnapshot.getHeap(name);
154 }
155
Richard Uhler7a16adb2015-11-11 09:13:23 -0800156 /**
157 * Returns a collection of instances whose immediate dominator is the
158 * SENTINEL_ROOT.
159 */
160 public List<Instance> getRooted() {
161 return mRooted;
Richard Uhlerb730b782015-07-15 16:01:58 -0700162 }
163
Richard Uhler38f9eba2015-11-11 08:31:52 -0800164 /**
165 * Returns true if the given instance is a root.
166 */
167 public boolean isRoot(Instance inst) {
168 return mRoots.containsKey(inst);
169 }
170
171 /**
172 * Returns the list of root types for the given instance, or null if the
173 * instance is not a root.
174 */
175 public Collection<RootType> getRootTypes(Instance inst) {
176 return mRoots.get(inst);
177 }
178
Richard Uhlerb730b782015-07-15 16:01:58 -0700179 public List<Heap> getHeaps() {
180 return mHeaps;
181 }
182
183 public Site getRootSite() {
184 return mRootSite;
185 }
186
187 /**
188 * Look up the site at which the given object was allocated.
189 */
190 public Site getSiteForInstance(Instance inst) {
191 Site site = mRootSite;
192 StackTrace stack = getStack(inst);
193 if (stack != null) {
194 StackFrame[] frames = getStackFrames(stack);
195 if (frames != null) {
196 List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
197 site = mRootSite.getChild(path.iterator());
198 }
199 }
200 return site;
201 }
202
203 /**
204 * Return a list of those objects immediately dominated by the given
205 * instance.
206 */
207 public List<Instance> getDominated(Instance inst) {
208 return mDominated.get(inst);
209 }
210
211 /**
212 * Return the total size of reachable objects allocated on the given heap.
213 */
214 public long getHeapSize(Heap heap) {
215 return mHeapSizes.get(heap);
216 }
217
218 /**
219 * Return the class name for the given class object.
220 * classObj may be null, in which case "(class unknown)" is returned.
221 */
222 public static String getClassName(ClassObj classObj) {
223 if (classObj == null) {
224 return "(class unknown)";
225 }
226 return classObj.getClassName();
227 }
228
229 // Return the stack where the given instance was allocated.
230 private static StackTrace getStack(Instance inst) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700231 return inst.getStack();
Richard Uhlerb730b782015-07-15 16:01:58 -0700232 }
233
234 // Return the list of stack frames for a stack trace.
235 private static StackFrame[] getStackFrames(StackTrace stack) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700236 return stack.getFrames();
Richard Uhlerb730b782015-07-15 16:01:58 -0700237 }
238
239 // Return the serial number of the given stack trace.
240 private static int getStackTraceSerialNumber(StackTrace stack) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700241 return stack.getSerialNumber();
Richard Uhlerb730b782015-07-15 16:01:58 -0700242 }
243
244 // Get the site associated with the given stack id and depth.
245 // Returns the root site if no such site found.
246 // depth of -1 means the full stack.
247 public Site getSite(int stackId, int depth) {
248 Site site = mRootSite;
249 StackTrace stack = mSnapshot.getStackTrace(stackId);
250 if (stack != null) {
251 StackFrame[] frames = getStackFrames(stack);
252 if (frames != null) {
253 List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
254 if (depth >= 0) {
255 path = path.subList(0, depth);
256 }
257 site = mRootSite.getChild(path.iterator());
258 }
259 }
260 return site;
261 }
262}