/* * Copyright (C) 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.hit; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; public class Queries { /* * NOTES: Here's a list of the queries that can be done in hat and * how you'd perform a similar query here in hit: * * hat hit * ------------------------------------------------------------------------ * allClasses classes * allClassesWithPlatform allClasses * class findClass * instances instancesOf * allInstances allInstancesOf * object findObject * showRoots getRoots * newInstances newInstances * * reachableFrom make a call to findObject to get the target * parent object, this will give you an Instance. * Then call visit(Set, Filter) on that to have * it build the set of objects in its subgraph. * * rootsTo make a call to findObject on the leaf node * in question, this will give you an Instance. * Instances have an ArrayList of all of the * parent objects that refer to it. You can * follow those parent links until you hit an * object whose parent is null or a ThreadObj. * You've not successfully traced the paths to * the roots. */ private static final String DEFAULT_PACKAGE = ""; /* * Produce a collection of all classes, broken down by package. * The keys of the resultant map iterate in sorted package order. * The values of the map are the classes defined in each package. */ public static Map> allClasses(State state) { return classes(state, null); } public static Map> classes(State state, String[] excludedPrefixes) { TreeMap> result = new TreeMap>(); Set classes = new TreeSet(); // Build a set of all classes across all heaps for (Heap heap: state.mHeaps.values()) { classes.addAll(heap.mClassesById.values()); } // Filter it if needed if (excludedPrefixes != null) { final int N = excludedPrefixes.length; Iterator iter = classes.iterator(); while (iter.hasNext()) { ClassObj theClass = iter.next(); String classPath = theClass.toString(); for (int i = 0; i < N; i++) { if (classPath.startsWith(excludedPrefixes[i])) { iter.remove(); break; } } } } // Now that we have a final list of classes, group them by package for (ClassObj theClass: classes) { String packageName = DEFAULT_PACKAGE; int lastDot = theClass.mClassName.lastIndexOf('.'); if (lastDot != -1) { packageName = theClass.mClassName.substring(0, lastDot); } Set classSet = result.get(packageName); if (classSet == null) { classSet = new TreeSet(); result.put(packageName, classSet); } classSet.add(theClass); } return result; } /* * It's sorta sad that this is a pass-through call, but it seems like * having all of the hat-like query methods in one place is a good thing * even if there is duplication of effort. */ public static ClassObj findClass(State state, String name) { return state.findClass(name); } /* * Return an array of instances of the given class. This does not include * instances of subclasses. */ public static Instance[] instancesOf(State state, String baseClassName) { ClassObj theClass = state.findClass(baseClassName); if (theClass == null) { throw new IllegalArgumentException("Class not found: " + baseClassName); } Instance[] instances = new Instance[theClass.mInstances.size()]; return theClass.mInstances.toArray(instances); } /* * Return an array of instances of the given class. This includes * instances of subclasses. */ public static Instance[] allInstancesOf(State state, String baseClassName) { ClassObj theClass = state.findClass(baseClassName); if (theClass == null) { throw new IllegalArgumentException("Class not found: " + baseClassName); } ArrayList classList = new ArrayList(); classList.add(theClass); classList.addAll(traverseSubclasses(theClass)); ArrayList instanceList = new ArrayList(); for (ClassObj someClass: classList) { instanceList.addAll(someClass.mInstances); } Instance[] result = new Instance[instanceList.size()]; instanceList.toArray(result); return result; } private static ArrayList traverseSubclasses(ClassObj base) { ArrayList result = new ArrayList(); for (ClassObj subclass: base.mSubclasses) { result.add(subclass); result.addAll(traverseSubclasses(subclass)); } return result; } /* * Find a reference to an object based on its id. The id should be * in hexadecimal. */ public static Instance findObject(State state, String id) { long id2 = Long.parseLong(id, 16); return state.findReference(id2); } public static Collection getRoots(State state) { HashSet result = new HashSet(); for (Heap heap: state.mHeaps.values()) { result.addAll(heap.mRoots); } return result; } public static final Instance[] newInstances(State older, State newer) { ArrayList resultList = new ArrayList(); for (Heap newHeap: newer.mHeaps.values()) { Heap oldHeap = older.getHeap(newHeap.mName); if (oldHeap == null) { continue; } for (Instance instance: newHeap.mInstances.values()) { Instance oldInstance = oldHeap.getInstance(instance.mId); /* * If this instance wasn't in the old heap, or was there, * but that ID was for an obj of a different type, then we have * a newly allocated object and we should report it in the * results. */ if ((oldInstance == null) || (instance.mClassId != oldInstance.mClassId)) { resultList.add(instance); } } } Instance[] resultArray = new Instance[resultList.size()]; return resultList.toArray(resultArray); } }