blob: 0dac79647ca3690f0b9add5ace17d2fcbd091008 [file] [log] [blame]
/*
* 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 = "<default>";
/*
* 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<String, Set<ClassObj>> allClasses(State state) {
return classes(state, null);
}
public static Map<String, Set<ClassObj>> classes(State state,
String[] excludedPrefixes) {
TreeMap<String, Set<ClassObj>> result =
new TreeMap<String, Set<ClassObj>>();
Set<ClassObj> classes = new TreeSet<ClassObj>();
// 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<ClassObj> 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<ClassObj> classSet = result.get(packageName);
if (classSet == null) {
classSet = new TreeSet<ClassObj>();
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<ClassObj> classList = new ArrayList<ClassObj>();
classList.add(theClass);
classList.addAll(traverseSubclasses(theClass));
ArrayList<Instance> instanceList = new ArrayList<Instance>();
for (ClassObj someClass: classList) {
instanceList.addAll(someClass.mInstances);
}
Instance[] result = new Instance[instanceList.size()];
instanceList.toArray(result);
return result;
}
private static ArrayList<ClassObj> traverseSubclasses(ClassObj base) {
ArrayList<ClassObj> result = new ArrayList<ClassObj>();
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<RootObj> getRoots(State state) {
HashSet<RootObj> result = new HashSet<RootObj>();
for (Heap heap: state.mHeaps.values()) {
result.addAll(heap.mRoots);
}
return result;
}
public static final Instance[] newInstances(State older, State newer) {
ArrayList<Instance> resultList = new ArrayList<Instance>();
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);
}
}