ahat - An android heap dump viewer. Initial checkin.
ahat is an android-aware heap dump viewer based on perflib with a
simple html interface.
Change-Id: I7c18a7603dbbe735f778a95cd047f4f9ec1705ef
diff --git a/tools/ahat/src/AhatSnapshot.java b/tools/ahat/src/AhatSnapshot.java
new file mode 100644
index 0000000..2437d03
--- /dev/null
+++ b/tools/ahat/src/AhatSnapshot.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * 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.ahat;
+
+import com.android.tools.perflib.heap.ClassObj;
+import com.android.tools.perflib.heap.Heap;
+import com.android.tools.perflib.heap.Instance;
+import com.android.tools.perflib.heap.RootObj;
+import com.android.tools.perflib.heap.Snapshot;
+import com.android.tools.perflib.heap.StackFrame;
+import com.android.tools.perflib.heap.StackTrace;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A wrapper over the perflib snapshot that provides the behavior we use in
+ * ahat.
+ */
+class AhatSnapshot {
+ private Snapshot mSnapshot;
+ private List<Heap> mHeaps;
+
+ // Map from Instance to the list of Instances it immediately dominates.
+ private Map<Instance, List<Instance>> mDominated;
+
+ private Site mRootSite;
+ private Map<Heap, Long> mHeapSizes;
+
+ public AhatSnapshot(Snapshot snapshot) {
+ mSnapshot = snapshot;
+ mHeaps = new ArrayList<Heap>(mSnapshot.getHeaps());
+ mDominated = new HashMap<Instance, List<Instance>>();
+ mRootSite = new Site("ROOT");
+ mHeapSizes = new HashMap<Heap, Long>();
+
+ ClassObj javaLangClass = mSnapshot.findClass("java.lang.Class");
+ for (Heap heap : mHeaps) {
+ long total = 0;
+ for (Instance inst : Iterables.concat(heap.getClasses(), heap.getInstances())) {
+ Instance dominator = inst.getImmediateDominator();
+ if (dominator != null) {
+ total += inst.getSize();
+
+ // Properly label the class of a class object.
+ if (inst instanceof ClassObj && javaLangClass != null && inst.getClassObj() == null) {
+ inst.setClassId(javaLangClass.getId());
+ }
+
+ // Update dominated instances.
+ List<Instance> instances = mDominated.get(dominator);
+ if (instances == null) {
+ instances = new ArrayList<Instance>();
+ mDominated.put(dominator, instances);
+ }
+ instances.add(inst);
+
+ // Update sites.
+ List<StackFrame> path = Collections.emptyList();
+ StackTrace stack = getStack(inst);
+ int stackId = getStackTraceSerialNumber(stack);
+ if (stack != null) {
+ StackFrame[] frames = getStackFrames(stack);
+ if (frames != null && frames.length > 0) {
+ path = Lists.reverse(Arrays.asList(frames));
+ }
+ }
+ mRootSite.add(stackId, 0, path.iterator(), inst);
+ }
+ }
+ mHeapSizes.put(heap, total);
+ }
+ }
+
+ public Instance findInstance(long id) {
+ return mSnapshot.findInstance(id);
+ }
+
+ public int getHeapIndex(Heap heap) {
+ return mSnapshot.getHeapIndex(heap);
+ }
+
+ public Heap getHeap(String name) {
+ return mSnapshot.getHeap(name);
+ }
+
+ public Collection<RootObj> getGCRoots() {
+ return mSnapshot.getGCRoots();
+ }
+
+ public List<Heap> getHeaps() {
+ return mHeaps;
+ }
+
+ public Site getRootSite() {
+ return mRootSite;
+ }
+
+ /**
+ * Look up the site at which the given object was allocated.
+ */
+ public Site getSiteForInstance(Instance inst) {
+ Site site = mRootSite;
+ StackTrace stack = getStack(inst);
+ if (stack != null) {
+ StackFrame[] frames = getStackFrames(stack);
+ if (frames != null) {
+ List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
+ site = mRootSite.getChild(path.iterator());
+ }
+ }
+ return site;
+ }
+
+ /**
+ * Return a list of those objects immediately dominated by the given
+ * instance.
+ */
+ public List<Instance> getDominated(Instance inst) {
+ return mDominated.get(inst);
+ }
+
+ /**
+ * Return the total size of reachable objects allocated on the given heap.
+ */
+ public long getHeapSize(Heap heap) {
+ return mHeapSizes.get(heap);
+ }
+
+ /**
+ * Return the class name for the given class object.
+ * classObj may be null, in which case "(class unknown)" is returned.
+ */
+ public static String getClassName(ClassObj classObj) {
+ if (classObj == null) {
+ return "(class unknown)";
+ }
+ return classObj.getClassName();
+ }
+
+ // Return the stack where the given instance was allocated.
+ private static StackTrace getStack(Instance inst) {
+ // TODO: return inst.getStack() once perflib is fixed.
+ return null;
+ }
+
+ // Return the list of stack frames for a stack trace.
+ private static StackFrame[] getStackFrames(StackTrace stack) {
+ // TODO: Use stack.getFrames() once perflib is fixed.
+ return null;
+ }
+
+ // Return the serial number of the given stack trace.
+ private static int getStackTraceSerialNumber(StackTrace stack) {
+ // TODO: Use stack.getSerialNumber() once perflib is fixed.
+ return 0;
+ }
+
+ // Get the site associated with the given stack id and depth.
+ // Returns the root site if no such site found.
+ // depth of -1 means the full stack.
+ public Site getSite(int stackId, int depth) {
+ Site site = mRootSite;
+ StackTrace stack = mSnapshot.getStackTrace(stackId);
+ if (stack != null) {
+ StackFrame[] frames = getStackFrames(stack);
+ if (frames != null) {
+ List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
+ if (depth >= 0) {
+ path = path.subList(0, depth);
+ }
+ site = mRootSite.getChild(path.iterator());
+ }
+ }
+ return site;
+ }
+}