• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.ahat;
18 
19 import com.android.tools.perflib.heap.ClassObj;
20 import com.android.tools.perflib.heap.Heap;
21 import com.android.tools.perflib.heap.Instance;
22 import com.android.tools.perflib.heap.RootObj;
23 import com.android.tools.perflib.heap.RootType;
24 import com.android.tools.perflib.heap.Snapshot;
25 import com.android.tools.perflib.heap.StackFrame;
26 import com.android.tools.perflib.heap.StackTrace;
27 import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
28 import com.google.common.collect.Iterables;
29 import com.google.common.collect.Lists;
30 import java.io.File;
31 import java.io.IOException;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collection;
35 import java.util.Collections;
36 import java.util.HashMap;
37 import java.util.HashSet;
38 import java.util.List;
39 import java.util.Map;
40 
41 /**
42  * A wrapper over the perflib snapshot that provides the behavior we use in
43  * ahat.
44  */
45 class AhatSnapshot {
46   private final Snapshot mSnapshot;
47   private final List<Heap> mHeaps;
48 
49   // Map from Instance to the list of Instances it immediately dominates.
50   private final Map<Instance, List<Instance>> mDominated
51     = new HashMap<Instance, List<Instance>>();
52 
53   // Collection of objects whose immediate dominator is the SENTINEL_ROOT.
54   private final List<Instance> mRooted = new ArrayList<Instance>();
55 
56   // 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.
59   private final Map<Instance, Collection<RootType>> mRoots
60     = new HashMap<Instance, Collection<RootType>>();
61 
62   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>();
67 
68   /**
69    * Create an AhatSnapshot from an hprof file.
70    */
fromHprof(File hprof)71   public static AhatSnapshot fromHprof(File hprof) throws IOException {
72     Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprof));
73     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    */
AhatSnapshot(Snapshot snapshot)82   private AhatSnapshot(Snapshot snapshot) {
83     mSnapshot = snapshot;
84     mHeaps = new ArrayList<Heap>(mSnapshot.getHeaps());
85 
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 
94           if (dominator == Snapshot.SENTINEL_ROOT) {
95             mRooted.add(inst);
96           }
97 
98           // 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);
122 
123           // Update native allocations.
124           InstanceUtils.NativeAllocation alloc = InstanceUtils.getNativeAllocation(inst);
125           if (alloc != null) {
126             mNativeAllocations.add(alloc);
127           }
128         }
129       }
130       mHeapSizes.put(heap, total);
131     }
132 
133     // Record the roots and their types.
134     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     }
143   }
144 
145   // Note: This method is exposed for testing purposes.
findClass(String name)146   public ClassObj findClass(String name) {
147     return mSnapshot.findClass(name);
148   }
149 
findInstance(long id)150   public Instance findInstance(long id) {
151     return mSnapshot.findInstance(id);
152   }
153 
getHeapIndex(Heap heap)154   public int getHeapIndex(Heap heap) {
155     return mSnapshot.getHeapIndex(heap);
156   }
157 
getHeap(String name)158   public Heap getHeap(String name) {
159     return mSnapshot.getHeap(name);
160   }
161 
162   /**
163    * Returns a collection of instances whose immediate dominator is the
164    * SENTINEL_ROOT.
165    */
getRooted()166   public List<Instance> getRooted() {
167     return mRooted;
168   }
169 
170   /**
171    * Returns true if the given instance is a root.
172    */
isRoot(Instance inst)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    */
getRootTypes(Instance inst)181   public Collection<RootType> getRootTypes(Instance inst) {
182     return mRoots.get(inst);
183   }
184 
getHeaps()185   public List<Heap> getHeaps() {
186     return mHeaps;
187   }
188 
getRootSite()189   public Site getRootSite() {
190     return mRootSite;
191   }
192 
193   /**
194    * Look up the site at which the given object was allocated.
195    */
getSiteForInstance(Instance inst)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    */
getDominated(Instance inst)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    */
getHeapSize(Heap heap)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    */
getClassName(ClassObj classObj)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.
getStack(Instance inst)236   private static StackTrace getStack(Instance inst) {
237     return inst.getStack();
238   }
239 
240   // Return the list of stack frames for a stack trace.
getStackFrames(StackTrace stack)241   private static StackFrame[] getStackFrames(StackTrace stack) {
242     return stack.getFrames();
243   }
244 
245   // Return the serial number of the given stack trace.
getStackTraceSerialNumber(StackTrace stack)246   private static int getStackTraceSerialNumber(StackTrace stack) {
247     return stack.getSerialNumber();
248   }
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.
getSite(int stackId, int depth)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   }
268 
269   // Return a list of known native allocations in the snapshot.
getNativeAllocations()270   public List<InstanceUtils.NativeAllocation> getNativeAllocations() {
271     return mNativeAllocations;
272   }
273 }
274