Home | History | Annotate | Download | only in hit
      1 /*
      2  * Copyright (C) 2008 Google Inc.
      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.hit;
     18 
     19 import java.util.ArrayList;
     20 import java.util.Collection;
     21 import java.util.HashSet;
     22 import java.util.Iterator;
     23 import java.util.Map;
     24 import java.util.Set;
     25 import java.util.TreeMap;
     26 import java.util.TreeSet;
     27 
     28 public class Queries {
     29     /*
     30      * NOTES:  Here's a list of the queries that can be done in hat and
     31      * how you'd perform a similar query here in hit:
     32      *
     33      * hat                      hit
     34      * ------------------------------------------------------------------------
     35      * allClasses               classes
     36      * allClassesWithPlatform   allClasses
     37      * class                    findClass
     38      * instances                instancesOf
     39      * allInstances             allInstancesOf
     40      * object                   findObject
     41      * showRoots                getRoots
     42      * newInstances             newInstances
     43      *
     44      * reachableFrom            make a call to findObject to get the target
     45      *                          parent object, this will give you an Instance.
     46      *                          Then call visit(Set, Filter) on that to have
     47      *                          it build the set of objects in its subgraph.
     48      *
     49      * rootsTo                  make a call to findObject on the leaf node
     50      *                          in question, this will give you an Instance.
     51      *                          Instances have an ArrayList of all of the
     52      *                          parent objects that refer to it.  You can
     53      *                          follow those parent links until you hit an
     54      *                          object whose parent is null or a ThreadObj.
     55      *                          You've not successfully traced the paths to
     56      *                          the roots.
     57      */
     58 
     59     private static final String DEFAULT_PACKAGE = "<default>";
     60 
     61     /*
     62      * Produce a collection of all classes, broken down by package.
     63      * The keys of the resultant map iterate in sorted package order.
     64      * The values of the map are the classes defined in each package.
     65      */
     66     public static Map<String, Set<ClassObj>> allClasses(State state) {
     67         return classes(state, null);
     68     }
     69 
     70     public static Map<String, Set<ClassObj>> classes(State state,
     71             String[] excludedPrefixes) {
     72         TreeMap<String, Set<ClassObj>> result =
     73         new TreeMap<String, Set<ClassObj>>();
     74 
     75         Set<ClassObj> classes = new TreeSet<ClassObj>();
     76 
     77         //  Build a set of all classes across all heaps
     78         for (Heap heap: state.mHeaps.values()) {
     79             classes.addAll(heap.mClassesById.values());
     80         }
     81 
     82         //  Filter it if needed
     83         if (excludedPrefixes != null) {
     84             final int N = excludedPrefixes.length;
     85             Iterator<ClassObj> iter = classes.iterator();
     86 
     87             while (iter.hasNext()) {
     88                 ClassObj theClass = iter.next();
     89                 String classPath = theClass.toString();
     90 
     91                 for (int i = 0; i < N; i++) {
     92                     if (classPath.startsWith(excludedPrefixes[i])) {
     93                         iter.remove();
     94                         break;
     95                     }
     96                 }
     97             }
     98         }
     99 
    100         //  Now that we have a final list of classes, group them by package
    101         for (ClassObj theClass: classes) {
    102             String packageName = DEFAULT_PACKAGE;
    103             int lastDot = theClass.mClassName.lastIndexOf('.');
    104 
    105             if (lastDot != -1) {
    106                 packageName = theClass.mClassName.substring(0, lastDot);
    107             }
    108 
    109             Set<ClassObj> classSet = result.get(packageName);
    110 
    111             if (classSet == null) {
    112                 classSet = new TreeSet<ClassObj>();
    113                 result.put(packageName, classSet);
    114             }
    115 
    116             classSet.add(theClass);
    117         }
    118 
    119         return result;
    120     }
    121 
    122     /*
    123      * It's sorta sad that this is a pass-through call, but it seems like
    124      * having all of the hat-like query methods in one place is a good thing
    125      * even if there is duplication of effort.
    126      */
    127     public static ClassObj findClass(State state, String name) {
    128         return state.findClass(name);
    129     }
    130 
    131     /*
    132      * Return an array of instances of the given class.  This does not include
    133      * instances of subclasses.
    134      */
    135      public static Instance[] instancesOf(State state, String baseClassName) {
    136          ClassObj theClass = state.findClass(baseClassName);
    137 
    138          if (theClass == null) {
    139              throw new IllegalArgumentException("Class not found: "
    140                 + baseClassName);
    141          }
    142 
    143          Instance[] instances = new Instance[theClass.mInstances.size()];
    144 
    145          return theClass.mInstances.toArray(instances);
    146      }
    147 
    148     /*
    149      * Return an array of instances of the given class.  This includes
    150      * instances of subclasses.
    151      */
    152     public static Instance[] allInstancesOf(State state, String baseClassName) {
    153         ClassObj theClass = state.findClass(baseClassName);
    154 
    155         if (theClass == null) {
    156             throw new IllegalArgumentException("Class not found: "
    157                 + baseClassName);
    158         }
    159 
    160         ArrayList<ClassObj> classList = new ArrayList<ClassObj>();
    161 
    162         classList.add(theClass);
    163         classList.addAll(traverseSubclasses(theClass));
    164 
    165         ArrayList<Instance> instanceList = new ArrayList<Instance>();
    166 
    167         for (ClassObj someClass: classList) {
    168             instanceList.addAll(someClass.mInstances);
    169         }
    170 
    171         Instance[] result = new Instance[instanceList.size()];
    172 
    173         instanceList.toArray(result);
    174 
    175         return result;
    176     }
    177 
    178     private static ArrayList<ClassObj> traverseSubclasses(ClassObj base) {
    179         ArrayList<ClassObj> result = new ArrayList<ClassObj>();
    180 
    181         for (ClassObj subclass: base.mSubclasses) {
    182             result.add(subclass);
    183             result.addAll(traverseSubclasses(subclass));
    184         }
    185 
    186         return result;
    187     }
    188 
    189     /*
    190      * Find a reference to an object based on its id.  The id should be
    191      * in hexadecimal.
    192      */
    193     public static Instance findObject(State state, String id) {
    194         long id2 = Long.parseLong(id, 16);
    195 
    196         return state.findReference(id2);
    197     }
    198 
    199     public static Collection<RootObj> getRoots(State state) {
    200         HashSet<RootObj> result = new HashSet<RootObj>();
    201 
    202         for (Heap heap: state.mHeaps.values()) {
    203             result.addAll(heap.mRoots);
    204         }
    205 
    206         return result;
    207     }
    208 
    209     public static final Instance[] newInstances(State older, State newer) {
    210         ArrayList<Instance> resultList = new ArrayList<Instance>();
    211 
    212         for (Heap newHeap: newer.mHeaps.values()) {
    213             Heap oldHeap = older.getHeap(newHeap.mName);
    214 
    215             if (oldHeap == null) {
    216                 continue;
    217             }
    218 
    219             for (Instance instance: newHeap.mInstances.values()) {
    220                 Instance oldInstance = oldHeap.getInstance(instance.mId);
    221 
    222                 /*
    223                  * If this instance wasn't in the old heap, or was there,
    224                  * but that ID was for an obj of a different type, then we have
    225                  * a newly allocated object and we should report it in the
    226                  * results.
    227                  */
    228                 if ((oldInstance == null)
    229                         || (instance.mClassId != oldInstance.mClassId)) {
    230                     resultList.add(instance);
    231                 }
    232             }
    233         }
    234 
    235         Instance[] resultArray = new Instance[resultList.size()];
    236 
    237         return resultList.toArray(resultArray);
    238     }
    239 }
    240