Home | History | Annotate | Download | only in src
      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    */
     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    */
     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.
    146   public ClassObj findClass(String name) {
    147     return mSnapshot.findClass(name);
    148   }
    149 
    150   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 
    162   /**
    163    * Returns a collection of instances whose immediate dominator is the
    164    * SENTINEL_ROOT.
    165    */
    166   public List<Instance> getRooted() {
    167     return mRooted;
    168   }
    169 
    170   /**
    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 
    185   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) {
    237     return inst.getStack();
    238   }
    239 
    240   // Return the list of stack frames for a stack trace.
    241   private static StackFrame[] getStackFrames(StackTrace stack) {
    242     return stack.getFrames();
    243   }
    244 
    245   // Return the serial number of the given stack trace.
    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.
    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.
    270   public List<InstanceUtils.NativeAllocation> getNativeAllocations() {
    271     return mNativeAllocations;
    272   }
    273 }
    274