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