/* * 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.tools.perflib.heap; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import java.util.*; 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. */ @NonNull public static Map<String, Set<ClassObj>> allClasses(@NonNull Snapshot snapshot) { return classes(snapshot, null); } @NonNull public static Map<String, Set<ClassObj>> classes(@NonNull Snapshot snapshot, @Nullable 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 : snapshot.mHeaps) { classes.addAll(heap.getClasses()); } // 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; } /** * Returns a collection of classes common to both snapshots. * * <p>The query returns instances from the first snapshot. Note: two classes having the same * fully-qualified name are considered equal, even if they differ in static fields, superclass, * classloader, etc. */ @NonNull public static Collection<ClassObj> commonClasses(@NonNull Snapshot first, @NonNull Snapshot second) { Collection<ClassObj> classes = new ArrayList<ClassObj>(); for (Heap heap : first.getHeaps()) { for (ClassObj clazz : heap.getClasses()) { if (second.findClass(clazz.getClassName()) != null) { classes.add(clazz); } } } return classes; } /* * 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(@NonNull Snapshot snapshot, String name) { return snapshot.findClass(name); } /* * Return an array of instances of the given class. This does not include * instances of subclasses. */ @NonNull public static Instance[] instancesOf(@NonNull Snapshot snapshot, String baseClassName) { ClassObj theClass = snapshot.findClass(baseClassName); if (theClass == null) { throw new IllegalArgumentException("Class not found: " + baseClassName); } List<Instance> instances = theClass.getInstancesList(); return instances.toArray(new Instance[instances.size()]); } /* * Return an array of instances of the given class. This includes * instances of subclasses. */ @NonNull public static Instance[] allInstancesOf(@NonNull Snapshot snapshot, String baseClassName) { ClassObj theClass = snapshot.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.getInstancesList()); } Instance[] result = new Instance[instanceList.size()]; instanceList.toArray(result); return result; } @NonNull private static ArrayList<ClassObj> traverseSubclasses(@NonNull 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(@NonNull Snapshot snapshot, String id) { long id2 = Long.parseLong(id, 16); return snapshot.findInstance(id2); } @NonNull public static Collection<RootObj> getRoots(@NonNull Snapshot snapshot) { HashSet<RootObj> result = new HashSet<RootObj>(); for (Heap heap : snapshot.mHeaps) { result.addAll(heap.mRoots); } return result; } @NonNull public static final Instance[] newInstances(@NonNull Snapshot older, @NonNull Snapshot newer) { ArrayList<Instance> resultList = new ArrayList<Instance>(); for (Heap newHeap : newer.mHeaps) { Heap oldHeap = older.getHeap(newHeap.getName()); if (oldHeap == null) { continue; } for (Instance instance : newHeap.getInstances()) { 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.getClassObj() != oldInstance.getClassObj())) { resultList.add(instance); } } } Instance[] resultArray = new Instance[resultList.size()]; return resultList.toArray(resultArray); } }