blob: 0bf064eb24d069032d771f46460ac141173d41cc [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 Uhlerb730b782015-07-15 16:01:58 -070022import com.android.tools.perflib.heap.Snapshot;
23import com.android.tools.perflib.heap.StackFrame;
24import com.android.tools.perflib.heap.StackTrace;
Richard Uhlerc237b392015-10-20 10:03:38 -070025import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
Richard Uhlerb730b782015-07-15 16:01:58 -070026import com.google.common.collect.Iterables;
27import com.google.common.collect.Lists;
Richard Uhler35244722015-09-10 16:45:54 -070028import java.io.File;
29import java.io.IOException;
Richard Uhlerb730b782015-07-15 16:01:58 -070030import java.util.ArrayList;
31import java.util.Arrays;
Richard Uhlerb730b782015-07-15 16:01:58 -070032import java.util.Collections;
33import java.util.HashMap;
34import java.util.List;
35import java.util.Map;
36
37/**
38 * A wrapper over the perflib snapshot that provides the behavior we use in
39 * ahat.
40 */
41class AhatSnapshot {
42 private Snapshot mSnapshot;
43 private List<Heap> mHeaps;
44
45 // Map from Instance to the list of Instances it immediately dominates.
46 private Map<Instance, List<Instance>> mDominated;
47
Richard Uhler7a16adb2015-11-11 09:13:23 -080048 // Collection of objects whose immediate dominator is the SENTINEL_ROOT.
49 private List<Instance> mRooted;
50
Richard Uhlerb730b782015-07-15 16:01:58 -070051 private Site mRootSite;
52 private Map<Heap, Long> mHeapSizes;
53
Richard Uhler35244722015-09-10 16:45:54 -070054 /**
55 * Create an AhatSnapshot from an hprof file.
56 */
57 public static AhatSnapshot fromHprof(File hprof) throws IOException {
Richard Uhlerc237b392015-10-20 10:03:38 -070058 Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprof));
Richard Uhler35244722015-09-10 16:45:54 -070059 snapshot.computeDominators();
60 return new AhatSnapshot(snapshot);
61 }
62
63 /**
64 * Construct an AhatSnapshot for the given perflib snapshot.
65 * Ther user is responsible for calling snapshot.computeDominators before
66 * calling this AhatSnapshot constructor.
67 */
68 private AhatSnapshot(Snapshot snapshot) {
Richard Uhlerb730b782015-07-15 16:01:58 -070069 mSnapshot = snapshot;
70 mHeaps = new ArrayList<Heap>(mSnapshot.getHeaps());
71 mDominated = new HashMap<Instance, List<Instance>>();
72 mRootSite = new Site("ROOT");
73 mHeapSizes = new HashMap<Heap, Long>();
Richard Uhler7a16adb2015-11-11 09:13:23 -080074 mRooted = new ArrayList<Instance>();
Richard Uhlerb730b782015-07-15 16:01:58 -070075
76 ClassObj javaLangClass = mSnapshot.findClass("java.lang.Class");
77 for (Heap heap : mHeaps) {
78 long total = 0;
79 for (Instance inst : Iterables.concat(heap.getClasses(), heap.getInstances())) {
80 Instance dominator = inst.getImmediateDominator();
81 if (dominator != null) {
82 total += inst.getSize();
83
Richard Uhler7a16adb2015-11-11 09:13:23 -080084 if (dominator == Snapshot.SENTINEL_ROOT) {
85 mRooted.add(inst);
86 }
87
Richard Uhlerb730b782015-07-15 16:01:58 -070088 // Properly label the class of a class object.
89 if (inst instanceof ClassObj && javaLangClass != null && inst.getClassObj() == null) {
90 inst.setClassId(javaLangClass.getId());
91 }
92
93 // Update dominated instances.
94 List<Instance> instances = mDominated.get(dominator);
95 if (instances == null) {
96 instances = new ArrayList<Instance>();
97 mDominated.put(dominator, instances);
98 }
99 instances.add(inst);
100
101 // Update sites.
102 List<StackFrame> path = Collections.emptyList();
103 StackTrace stack = getStack(inst);
104 int stackId = getStackTraceSerialNumber(stack);
105 if (stack != null) {
106 StackFrame[] frames = getStackFrames(stack);
107 if (frames != null && frames.length > 0) {
108 path = Lists.reverse(Arrays.asList(frames));
109 }
110 }
111 mRootSite.add(stackId, 0, path.iterator(), inst);
112 }
113 }
114 mHeapSizes.put(heap, total);
115 }
116 }
117
Richard Uhler35244722015-09-10 16:45:54 -0700118 // Note: This method is exposed for testing purposes.
119 public ClassObj findClass(String name) {
120 return mSnapshot.findClass(name);
121 }
122
Richard Uhlerb730b782015-07-15 16:01:58 -0700123 public Instance findInstance(long id) {
124 return mSnapshot.findInstance(id);
125 }
126
127 public int getHeapIndex(Heap heap) {
128 return mSnapshot.getHeapIndex(heap);
129 }
130
131 public Heap getHeap(String name) {
132 return mSnapshot.getHeap(name);
133 }
134
Richard Uhler7a16adb2015-11-11 09:13:23 -0800135 /**
136 * Returns a collection of instances whose immediate dominator is the
137 * SENTINEL_ROOT.
138 */
139 public List<Instance> getRooted() {
140 return mRooted;
Richard Uhlerb730b782015-07-15 16:01:58 -0700141 }
142
143 public List<Heap> getHeaps() {
144 return mHeaps;
145 }
146
147 public Site getRootSite() {
148 return mRootSite;
149 }
150
151 /**
152 * Look up the site at which the given object was allocated.
153 */
154 public Site getSiteForInstance(Instance inst) {
155 Site site = mRootSite;
156 StackTrace stack = getStack(inst);
157 if (stack != null) {
158 StackFrame[] frames = getStackFrames(stack);
159 if (frames != null) {
160 List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
161 site = mRootSite.getChild(path.iterator());
162 }
163 }
164 return site;
165 }
166
167 /**
168 * Return a list of those objects immediately dominated by the given
169 * instance.
170 */
171 public List<Instance> getDominated(Instance inst) {
172 return mDominated.get(inst);
173 }
174
175 /**
176 * Return the total size of reachable objects allocated on the given heap.
177 */
178 public long getHeapSize(Heap heap) {
179 return mHeapSizes.get(heap);
180 }
181
182 /**
183 * Return the class name for the given class object.
184 * classObj may be null, in which case "(class unknown)" is returned.
185 */
186 public static String getClassName(ClassObj classObj) {
187 if (classObj == null) {
188 return "(class unknown)";
189 }
190 return classObj.getClassName();
191 }
192
193 // Return the stack where the given instance was allocated.
194 private static StackTrace getStack(Instance inst) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700195 return inst.getStack();
Richard Uhlerb730b782015-07-15 16:01:58 -0700196 }
197
198 // Return the list of stack frames for a stack trace.
199 private static StackFrame[] getStackFrames(StackTrace stack) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700200 return stack.getFrames();
Richard Uhlerb730b782015-07-15 16:01:58 -0700201 }
202
203 // Return the serial number of the given stack trace.
204 private static int getStackTraceSerialNumber(StackTrace stack) {
Richard Uhlerc237b392015-10-20 10:03:38 -0700205 return stack.getSerialNumber();
Richard Uhlerb730b782015-07-15 16:01:58 -0700206 }
207
208 // Get the site associated with the given stack id and depth.
209 // Returns the root site if no such site found.
210 // depth of -1 means the full stack.
211 public Site getSite(int stackId, int depth) {
212 Site site = mRootSite;
213 StackTrace stack = mSnapshot.getStackTrace(stackId);
214 if (stack != null) {
215 StackFrame[] frames = getStackFrames(stack);
216 if (frames != null) {
217 List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
218 if (depth >= 0) {
219 path = path.subList(0, depth);
220 }
221 site = mRootSite.getChild(path.iterator());
222 }
223 }
224 return site;
225 }
226}