summaryrefslogtreecommitdiff
path: root/tools/ahat/src/heapdump/AhatInstance.java
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ahat/src/heapdump/AhatInstance.java')
-rw-r--r--tools/ahat/src/heapdump/AhatInstance.java455
1 files changed, 455 insertions, 0 deletions
diff --git a/tools/ahat/src/heapdump/AhatInstance.java b/tools/ahat/src/heapdump/AhatInstance.java
new file mode 100644
index 0000000000..e6b9c00384
--- /dev/null
+++ b/tools/ahat/src/heapdump/AhatInstance.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2016 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.heapdump;
+
+import com.android.tools.perflib.heap.ClassObj;
+import com.android.tools.perflib.heap.Instance;
+import com.android.tools.perflib.heap.RootObj;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public abstract class AhatInstance implements Diffable<AhatInstance> {
+ private long mId;
+ private long mSize;
+ private long mTotalRetainedSize;
+ private long mRetainedSizes[]; // Retained size indexed by heap index
+ private boolean mIsReachable;
+ private AhatHeap mHeap;
+ private AhatInstance mImmediateDominator;
+ private AhatInstance mNextInstanceToGcRoot;
+ private String mNextInstanceToGcRootField = "???";
+ private AhatClassObj mClassObj;
+ private AhatInstance[] mHardReverseReferences;
+ private AhatInstance[] mSoftReverseReferences;
+ private Site mSite;
+
+ // If this instance is a root, mRootTypes contains a set of the root types.
+ // If this instance is not a root, mRootTypes is null.
+ private List<String> mRootTypes;
+
+ // List of instances this instance immediately dominates.
+ private List<AhatInstance> mDominated = new ArrayList<AhatInstance>();
+
+ private AhatInstance mBaseline;
+
+ public AhatInstance(long id) {
+ mId = id;
+ mBaseline = this;
+ }
+
+ /**
+ * Initializes this AhatInstance based on the given perflib instance.
+ * The AhatSnapshot should be used to look up AhatInstances and AhatHeaps.
+ * There is no guarantee that the AhatInstances returned by
+ * snapshot.findInstance have been initialized yet.
+ */
+ void initialize(AhatSnapshot snapshot, Instance inst) {
+ mId = inst.getId();
+ mSize = inst.getSize();
+ mTotalRetainedSize = inst.getTotalRetainedSize();
+ mIsReachable = inst.isReachable();
+
+ List<AhatHeap> heaps = snapshot.getHeaps();
+ mRetainedSizes = new long[heaps.size()];
+ for (AhatHeap heap : heaps) {
+ mRetainedSizes[heap.getIndex()] = inst.getRetainedSize(heap.getIndex());
+ }
+
+ mHeap = snapshot.getHeap(inst.getHeap().getName());
+
+ Instance dom = inst.getImmediateDominator();
+ if (dom == null || dom instanceof RootObj) {
+ mImmediateDominator = null;
+ } else {
+ mImmediateDominator = snapshot.findInstance(dom.getId());
+ mImmediateDominator.mDominated.add(this);
+ }
+
+ ClassObj clsObj = inst.getClassObj();
+ if (clsObj != null) {
+ mClassObj = snapshot.findClassObj(clsObj.getId());
+ }
+
+ // A couple notes about reverse references:
+ // * perflib sometimes returns unreachable reverse references. If
+ // snapshot.findInstance returns null, it means the reverse reference is
+ // not reachable, so we filter it out.
+ // * We store the references as AhatInstance[] instead of
+ // ArrayList<AhatInstance> because it saves a lot of space and helps
+ // with performance when there are a lot of AhatInstances.
+ ArrayList<AhatInstance> ahatRefs = new ArrayList<AhatInstance>();
+ ahatRefs = new ArrayList<AhatInstance>();
+ for (Instance ref : inst.getHardReverseReferences()) {
+ AhatInstance ahat = snapshot.findInstance(ref.getId());
+ if (ahat != null) {
+ ahatRefs.add(ahat);
+ }
+ }
+ mHardReverseReferences = new AhatInstance[ahatRefs.size()];
+ ahatRefs.toArray(mHardReverseReferences);
+
+ List<Instance> refs = inst.getSoftReverseReferences();
+ ahatRefs.clear();
+ if (refs != null) {
+ for (Instance ref : refs) {
+ AhatInstance ahat = snapshot.findInstance(ref.getId());
+ if (ahat != null) {
+ ahatRefs.add(ahat);
+ }
+ }
+ }
+ mSoftReverseReferences = new AhatInstance[ahatRefs.size()];
+ ahatRefs.toArray(mSoftReverseReferences);
+ }
+
+ /**
+ * Returns a unique identifier for the instance.
+ */
+ public long getId() {
+ return mId;
+ }
+
+ /**
+ * Returns the shallow number of bytes this object takes up.
+ */
+ public long getSize() {
+ return mSize;
+ }
+
+ /**
+ * Returns the number of bytes belonging to the given heap that this instance
+ * retains.
+ */
+ public long getRetainedSize(AhatHeap heap) {
+ int index = heap.getIndex();
+ return 0 <= index && index < mRetainedSizes.length ? mRetainedSizes[heap.getIndex()] : 0;
+ }
+
+ /**
+ * Returns the total number of bytes this instance retains.
+ */
+ public long getTotalRetainedSize() {
+ return mTotalRetainedSize;
+ }
+
+ /**
+ * Returns whether this object is strongly-reachable.
+ */
+ public boolean isReachable() {
+ return mIsReachable;
+ }
+
+ /**
+ * Returns the heap that this instance is allocated on.
+ */
+ public AhatHeap getHeap() {
+ return mHeap;
+ }
+
+ /**
+ * Returns true if this instance is marked as a root instance.
+ */
+ public boolean isRoot() {
+ return mRootTypes != null;
+ }
+
+ /**
+ * Marks this instance as being a root of the given type.
+ */
+ void addRootType(String type) {
+ if (mRootTypes == null) {
+ mRootTypes = new ArrayList<String>();
+ mRootTypes.add(type);
+ } else if (!mRootTypes.contains(type)) {
+ mRootTypes.add(type);
+ }
+ }
+
+ /**
+ * Returns a list of string descriptions of the root types of this object.
+ * Returns null if this object is not a root.
+ */
+ public Collection<String> getRootTypes() {
+ return mRootTypes;
+ }
+
+ /**
+ * Returns the immediate dominator of this instance.
+ * Returns null if this is a root instance.
+ */
+ public AhatInstance getImmediateDominator() {
+ return mImmediateDominator;
+ }
+
+ /**
+ * Returns a list of those objects immediately dominated by the given
+ * instance.
+ */
+ public List<AhatInstance> getDominated() {
+ return mDominated;
+ }
+
+ /**
+ * Returns the site where this instance was allocated.
+ */
+ public Site getSite() {
+ return mSite;
+ }
+
+ /**
+ * Sets the allocation site of this instance.
+ */
+ void setSite(Site site) {
+ mSite = site;
+ }
+
+ /**
+ * Returns true if the given instance is a class object
+ */
+ public boolean isClassObj() {
+ // Overridden by AhatClassObj.
+ return false;
+ }
+
+ /**
+ * Returns this as an AhatClassObj if this is an AhatClassObj.
+ * Returns null if this is not an AhatClassObj.
+ */
+ public AhatClassObj asClassObj() {
+ // Overridden by AhatClassObj.
+ return null;
+ }
+
+ /**
+ * Returns the class object instance for the class of this object.
+ */
+ public AhatClassObj getClassObj() {
+ return mClassObj;
+ }
+
+ /**
+ * Returns the name of the class this object belongs to.
+ */
+ public String getClassName() {
+ AhatClassObj classObj = getClassObj();
+ return classObj == null ? "???" : classObj.getName();
+ }
+
+ /**
+ * Returns true if the given instance is an array instance
+ */
+ public boolean isArrayInstance() {
+ // Overridden by AhatArrayInstance.
+ return false;
+ }
+
+ /**
+ * Returns this as an AhatArrayInstance if this is an AhatArrayInstance.
+ * Returns null if this is not an AhatArrayInstance.
+ */
+ public AhatArrayInstance asArrayInstance() {
+ // Overridden by AhatArrayInstance.
+ return null;
+ }
+
+ /**
+ * Returns true if the given instance is a class instance
+ */
+ public boolean isClassInstance() {
+ return false;
+ }
+
+ /**
+ * Returns this as an AhatClassInstance if this is an AhatClassInstance.
+ * Returns null if this is not an AhatClassInstance.
+ */
+ public AhatClassInstance asClassInstance() {
+ return null;
+ }
+
+ /**
+ * Return the referent associated with this instance.
+ * This is relevent for instances of java.lang.ref.Reference.
+ * Returns null if the instance has no referent associated with it.
+ */
+ public AhatInstance getReferent() {
+ // Overridden by AhatClassInstance.
+ return null;
+ }
+
+ /**
+ * Returns a list of objects with hard references to this object.
+ */
+ public List<AhatInstance> getHardReverseReferences() {
+ return Arrays.asList(mHardReverseReferences);
+ }
+
+ /**
+ * Returns a list of objects with soft references to this object.
+ */
+ public List<AhatInstance> getSoftReverseReferences() {
+ return Arrays.asList(mSoftReverseReferences);
+ }
+
+ /**
+ * Returns the value of a field of an instance.
+ * Returns null if the field value is null, the field couldn't be read, or
+ * there are multiple fields with the same name.
+ */
+ public Value getField(String fieldName) {
+ // Overridden by AhatClassInstance.
+ return null;
+ }
+
+ /**
+ * Reads a reference field of this instance.
+ * Returns null if the field value is null, or if the field couldn't be read.
+ */
+ public AhatInstance getRefField(String fieldName) {
+ // Overridden by AhatClassInstance.
+ return null;
+ }
+
+ /**
+ * Assuming inst represents a DexCache object, return the dex location for
+ * that dex cache. Returns null if the given instance doesn't represent a
+ * DexCache object or the location could not be found.
+ * If maxChars is non-negative, the returned location is truncated to
+ * maxChars in length.
+ */
+ public String getDexCacheLocation(int maxChars) {
+ return null;
+ }
+
+ /**
+ * Return the bitmap instance associated with this object, or null if there
+ * is none. This works for android.graphics.Bitmap instances and their
+ * underlying Byte[] instances.
+ */
+ public AhatInstance getAssociatedBitmapInstance() {
+ return null;
+ }
+
+ /**
+ * Read the string value from this instance.
+ * Returns null if this object can't be interpreted as a string.
+ * The returned string is truncated to maxChars characters.
+ * If maxChars is negative, the returned string is not truncated.
+ */
+ public String asString(int maxChars) {
+ // By default instances can't be interpreted as a string. This method is
+ // overridden by AhatClassInstance and AhatArrayInstance for those cases
+ // when an instance can be interpreted as a string.
+ return null;
+ }
+
+ /**
+ * Reads the string value from an hprof Instance.
+ * Returns null if the object can't be interpreted as a string.
+ */
+ public String asString() {
+ return asString(-1);
+ }
+
+ /**
+ * Return the bitmap associated with the given instance, if any.
+ * This is relevant for instances of android.graphics.Bitmap and byte[].
+ * Returns null if there is no bitmap associated with the given instance.
+ */
+ public BufferedImage asBitmap() {
+ return null;
+ }
+
+ /**
+ * Returns a sample path from a GC root to this instance.
+ * This instance is included as the last element of the path with an empty
+ * field description.
+ */
+ public List<PathElement> getPathFromGcRoot() {
+ List<PathElement> path = new ArrayList<PathElement>();
+
+ AhatInstance dom = this;
+ for (PathElement elem = new PathElement(this, ""); elem != null;
+ elem = getNextPathElementToGcRoot(elem.instance)) {
+ if (elem.instance.equals(dom)) {
+ elem.isDominator = true;
+ dom = dom.getImmediateDominator();
+ }
+ path.add(elem);
+ }
+ Collections.reverse(path);
+ return path;
+ }
+
+ /**
+ * Returns the next instance to GC root from this object and a string
+ * description of which field of that object refers to the given instance.
+ * Returns null if the given instance has no next instance to the gc root.
+ */
+ private static PathElement getNextPathElementToGcRoot(AhatInstance inst) {
+ AhatInstance parent = inst.mNextInstanceToGcRoot;
+ if (parent == null) {
+ return null;
+ }
+ return new PathElement(inst.mNextInstanceToGcRoot, inst.mNextInstanceToGcRootField);
+ }
+
+ void setNextInstanceToGcRoot(AhatInstance inst, String field) {
+ mNextInstanceToGcRoot = inst;
+ mNextInstanceToGcRootField = field;
+ }
+
+ /** Returns a human-readable identifier for this object.
+ * For class objects, the string is the class name.
+ * For class instances, the string is the class name followed by '@' and the
+ * hex id of the instance.
+ * For array instances, the string is the array type followed by the size in
+ * square brackets, followed by '@' and the hex id of the instance.
+ */
+ @Override public abstract String toString();
+
+ /**
+ * Read the byte[] value from an hprof Instance.
+ * Returns null if the instance is not a byte array.
+ */
+ byte[] asByteArray() {
+ return null;
+ }
+
+ public void setBaseline(AhatInstance baseline) {
+ mBaseline = baseline;
+ }
+
+ @Override public AhatInstance getBaseline() {
+ return mBaseline;
+ }
+
+ @Override public boolean isPlaceHolder() {
+ return false;
+ }
+
+ /**
+ * Returns a new place holder instance corresponding to this instance.
+ */
+ AhatInstance newPlaceHolderInstance() {
+ return new AhatPlaceHolderInstance(this);
+ }
+}