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