summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/ahat/src/ObjectHandler.java2
-rw-r--r--tools/ahat/src/ObjectsHandler.java3
-rw-r--r--tools/ahat/src/SiteHandler.java11
-rw-r--r--tools/ahat/src/Summarizer.java2
-rw-r--r--tools/ahat/src/dominators/DominatorsComputation.java40
-rw-r--r--tools/ahat/src/heapdump/AhatArrayInstance.java14
-rw-r--r--tools/ahat/src/heapdump/AhatClassInstance.java182
-rw-r--r--tools/ahat/src/heapdump/AhatClassObj.java24
-rw-r--r--tools/ahat/src/heapdump/AhatInstance.java3
-rw-r--r--tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java8
-rw-r--r--tools/ahat/src/heapdump/AhatPlaceHolderInstance.java4
-rw-r--r--tools/ahat/src/heapdump/AhatSnapshot.java37
-rw-r--r--tools/ahat/src/heapdump/Diff.java2
-rw-r--r--tools/ahat/src/heapdump/DiffFields.java16
-rw-r--r--tools/ahat/src/heapdump/DominatorReferenceIterator.java6
-rw-r--r--tools/ahat/src/heapdump/Field.java27
-rw-r--r--tools/ahat/src/heapdump/Site.java134
-rw-r--r--tools/ahat/src/heapdump/SkipNullsIterator.java (renamed from tools/ahat/src/heapdump/ReferenceIterator.java)35
-rw-r--r--tools/ahat/src/heapdump/SuperRoot.java5
-rw-r--r--tools/ahat/src/heapdump/Value.java338
-rw-r--r--tools/ahat/test-dump/Main.java23
-rw-r--r--tools/ahat/test/DiffFieldsTest.java22
-rw-r--r--tools/ahat/test/InstanceTest.java6
-rw-r--r--tools/ahat/test/ObjectHandlerTest.java4
-rw-r--r--tools/ahat/test/SiteTest.java66
-rw-r--r--tools/ahat/test/TestDump.java53
-rw-r--r--tools/ahat/test/Tests.java1
-rw-r--r--tools/libcore_gcstress_debug_failures.txt2
28 files changed, 798 insertions, 272 deletions
diff --git a/tools/ahat/src/ObjectHandler.java b/tools/ahat/src/ObjectHandler.java
index 8262910bf0..f4926aa83b 100644
--- a/tools/ahat/src/ObjectHandler.java
+++ b/tools/ahat/src/ObjectHandler.java
@@ -152,7 +152,7 @@ class ObjectHandler implements AhatHandler {
* ignored otherwise.
*/
private static void printFields(Doc doc, Query query, String id, boolean diff,
- List<FieldValue> current, List<FieldValue> baseline) {
+ Iterable<FieldValue> current, Iterable<FieldValue> baseline) {
if (!diff) {
// To avoid having to special case when diff is disabled, always diff
diff --git a/tools/ahat/src/ObjectsHandler.java b/tools/ahat/src/ObjectsHandler.java
index fd226c24bf..1a8f018bd5 100644
--- a/tools/ahat/src/ObjectsHandler.java
+++ b/tools/ahat/src/ObjectsHandler.java
@@ -37,10 +37,9 @@ class ObjectsHandler implements AhatHandler {
@Override
public void handle(Doc doc, Query query) throws IOException {
int id = query.getInt("id", 0);
- int depth = query.getInt("depth", 0);
String className = query.get("class", null);
String heapName = query.get("heap", null);
- Site site = mSnapshot.getSite(id, depth);
+ Site site = mSnapshot.getSite(id);
List<AhatInstance> insts = new ArrayList<AhatInstance>();
site.getObjects(heapName, className, insts);
diff --git a/tools/ahat/src/SiteHandler.java b/tools/ahat/src/SiteHandler.java
index 7a831d3018..543eaa376a 100644
--- a/tools/ahat/src/SiteHandler.java
+++ b/tools/ahat/src/SiteHandler.java
@@ -21,6 +21,7 @@ import com.android.ahat.heapdump.AhatSnapshot;
import com.android.ahat.heapdump.Site;
import com.android.ahat.heapdump.Sort;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -39,8 +40,7 @@ class SiteHandler implements AhatHandler {
@Override
public void handle(Doc doc, Query query) throws IOException {
int id = query.getInt("id", 0);
- int depth = query.getInt("depth", 0);
- Site site = mSnapshot.getSite(id, depth);
+ Site site = mSnapshot.getSite(id);
doc.title("Site");
doc.big(Summarizer.summarize(site));
@@ -49,7 +49,8 @@ class SiteHandler implements AhatHandler {
SitePrinter.printSite(mSnapshot, doc, query, ALLOCATION_SITE_ID, site);
doc.section("Sites Called from Here");
- List<Site> children = site.getChildren();
+ List<Site> children = new ArrayList<Site>(site.getChildren());
+
if (children.isEmpty()) {
doc.println(DocString.text("(none)"));
} else {
@@ -99,8 +100,8 @@ class SiteHandler implements AhatHandler {
String className = info.getClassName();
SizeTable.row(doc, info.numBytes, baseinfo.numBytes,
DocString.link(
- DocString.formattedUri("objects?id=%d&depth=%d&heap=%s&class=%s",
- site.getId(), site.getDepth(), info.heap.getName(), className),
+ DocString.formattedUri("objects?id=%d&heap=%s&class=%s",
+ site.getId(), info.heap.getName(), className),
DocString.format("%,14d", info.numInstances)),
DocString.delta(false, false, info.numInstances, baseinfo.numInstances),
DocString.text(info.heap.getName()),
diff --git a/tools/ahat/src/Summarizer.java b/tools/ahat/src/Summarizer.java
index 3e9da31e96..50b2e4b3b3 100644
--- a/tools/ahat/src/Summarizer.java
+++ b/tools/ahat/src/Summarizer.java
@@ -130,7 +130,7 @@ class Summarizer {
if (site.getLineNumber() > 0) {
text.append(":").append(Integer.toString(site.getLineNumber()));
}
- URI uri = DocString.formattedUri("site?id=%d&depth=%d", site.getId(), site.getDepth());
+ URI uri = DocString.formattedUri("site?id=%d", site.getId());
return DocString.link(uri, text);
}
}
diff --git a/tools/ahat/src/dominators/DominatorsComputation.java b/tools/ahat/src/dominators/DominatorsComputation.java
index 9a2a272be0..58b7b59f9a 100644
--- a/tools/ahat/src/dominators/DominatorsComputation.java
+++ b/tools/ahat/src/dominators/DominatorsComputation.java
@@ -88,33 +88,17 @@ public class DominatorsComputation {
// Invariant: srcS.id < this.id
public NodeS srcS;
+ // The largest id of the nodes we have seen so far on a path from the root
+ // to this node. Used to keep track of which nodes we have already seen
+ // and avoid processing them again.
+ public long seenid;
+
// The set of nodes X reachable by 'this' on a path of nodes from the
// root with increasing ids (possibly excluding X) that this node does not
// dominate (this.id > X.domid).
- // We can use a List instead of a Set for this because we guarentee that
- // we don't add the same node more than once to the list (see below).
+ // We can use a List instead of a Set for this because we guarentee based
+ // on seenid that we don't add the same node more than once to the list.
public List<NodeS> undom = new ArrayList<NodeS>();
-
- // The largest id of the node X for which we did X.undom.add(this).
- // This is an optimization to avoid adding duplicate node entries to the
- // undom set.
- //
- // The first time we see this node, we reach it through a path of nodes
- // with IDs 0,...,a,this. These form our src chain to the root.
- //
- // The next time we see this node, we reach it through a path of nodes
- // with IDS 0,...,b,c,...,d,this. Where all 0,...,b < a and all c,...,d > a.
- //
- // The next time we see this node, we reach it through a path of nodes
- // with IDS 0,...,e,f,...,g,this. With all 0,...,e < d and all f,...,g > d.
- // And so on.
- //
- // The first time we see this node, we set undomid to a.id. Nodes 0,...,a
- // will be added as undom in the 'revisit' phase of the node.
- //
- // The next times we see this node, we mark a+,...,d as undom and
- // change undomid to d. And so on.
- public long undomid;
}
private static class Link {
@@ -171,7 +155,7 @@ public class DominatorsComputation {
dstS.domid = link.srcS.id;
dstS.domS = link.srcS;
dstS.srcS = link.srcS;
- dstS.undomid = dstS.domid;
+ dstS.seenid = dstS.domid;
nodes.add(dstS);
link.dst.setDominatorsComputationState(dstS);
@@ -184,13 +168,11 @@ public class DominatorsComputation {
NodeS srcS = link.srcS;
boolean revisiting = dstS.domid < dstS.domS.id;
- while (srcS.id > dstS.domid) {
- if (srcS.id > dstS.undomid) {
- srcS.undom.add(dstS);
- }
+ while (srcS.id > dstS.seenid) {
+ srcS.undom.add(dstS);
srcS = srcS.srcS;
}
- dstS.undomid = link.srcS.id;
+ dstS.seenid = link.srcS.id;
if (srcS.id < dstS.domid) {
// In this case, dstS.domid must be wrong, because we just found a
diff --git a/tools/ahat/src/heapdump/AhatArrayInstance.java b/tools/ahat/src/heapdump/AhatArrayInstance.java
index 6d4485d4b9..8d23276fde 100644
--- a/tools/ahat/src/heapdump/AhatArrayInstance.java
+++ b/tools/ahat/src/heapdump/AhatArrayInstance.java
@@ -58,8 +58,7 @@ public class AhatArrayInstance extends AhatInstance {
}
@Override public Value get(int index) {
- AhatInstance obj = insts[index];
- return obj == null ? null : new Value(insts[index]);
+ return Value.pack(insts[index]);
}
};
break;
@@ -73,7 +72,7 @@ public class AhatArrayInstance extends AhatInstance {
}
@Override public Value get(int index) {
- return new Value(chars[index]);
+ return Value.pack(chars[index]);
}
};
break;
@@ -87,7 +86,7 @@ public class AhatArrayInstance extends AhatInstance {
}
@Override public Value get(int index) {
- return new Value(bytes[index]);
+ return Value.pack(bytes[index]);
}
};
break;
@@ -100,8 +99,7 @@ public class AhatArrayInstance extends AhatInstance {
}
@Override public Value get(int index) {
- Object obj = values[index];
- return obj == null ? null : new Value(obj);
+ return Value.pack(values[index]);
}
};
break;
@@ -130,7 +128,7 @@ public class AhatArrayInstance extends AhatInstance {
}
@Override
- ReferenceIterator getReferences() {
+ Iterable<Reference> getReferences() {
// The list of references will be empty if this is a primitive array.
List<Reference> refs = Collections.emptyList();
if (!mValues.isEmpty()) {
@@ -155,7 +153,7 @@ public class AhatArrayInstance extends AhatInstance {
};
}
}
- return new ReferenceIterator(refs);
+ return new SkipNullsIterator(refs);
}
@Override public boolean isArrayInstance() {
diff --git a/tools/ahat/src/heapdump/AhatClassInstance.java b/tools/ahat/src/heapdump/AhatClassInstance.java
index 211592388c..f7d8431a7b 100644
--- a/tools/ahat/src/heapdump/AhatClassInstance.java
+++ b/tools/ahat/src/heapdump/AhatClassInstance.java
@@ -19,12 +19,16 @@ package com.android.ahat.heapdump;
import com.android.tools.perflib.heap.ClassInstance;
import com.android.tools.perflib.heap.Instance;
import java.awt.image.BufferedImage;
-import java.util.AbstractList;
-import java.util.Arrays;
+import java.util.Iterator;
import java.util.List;
+import java.util.NoSuchElementException;
public class AhatClassInstance extends AhatInstance {
- private FieldValue[] mFieldValues;
+ // Instance fields of the object. These are stored in order of the instance
+ // field descriptors from the class object, starting with this class first,
+ // followed by the super class, and so on. We store the values separate from
+ // the field types and names to save memory.
+ private Value[] mFields;
public AhatClassInstance(long id) {
super(id);
@@ -35,18 +39,14 @@ public class AhatClassInstance extends AhatInstance {
ClassInstance classInst = (ClassInstance)inst;
List<ClassInstance.FieldValue> fieldValues = classInst.getValues();
- mFieldValues = new FieldValue[fieldValues.size()];
- for (int i = 0; i < mFieldValues.length; i++) {
- ClassInstance.FieldValue field = fieldValues.get(i);
- String name = field.getField().getName();
- String type = field.getField().getType().toString();
- Value value = snapshot.getValue(field.getValue());
- mFieldValues[i] = new FieldValue(name, type, value);
+ mFields = new Value[fieldValues.size()];
+ for (int i = 0; i < mFields.length; i++) {
+ mFields[i] = snapshot.getValue(fieldValues.get(i).getValue());
}
}
@Override public Value getField(String fieldName) {
- for (FieldValue field : mFieldValues) {
+ for (FieldValue field : getInstanceFields()) {
if (fieldName.equals(field.name)) {
return field.value;
}
@@ -90,36 +90,21 @@ public class AhatClassInstance extends AhatInstance {
/**
* Returns the list of class instance fields for this instance.
*/
- public List<FieldValue> getInstanceFields() {
- return Arrays.asList(mFieldValues);
+ public Iterable<FieldValue> getInstanceFields() {
+ return new InstanceFieldIterator(mFields, getClassObj());
}
@Override
- ReferenceIterator getReferences() {
- List<Reference> refs = new AbstractList<Reference>() {
- @Override
- public int size() {
- return mFieldValues.length;
- }
-
- @Override
- public Reference get(int index) {
- FieldValue field = mFieldValues[index];
- Value value = field.value;
- if (value != null && value.isAhatInstance()) {
- boolean strong = !field.name.equals("referent")
- || !isInstanceOfClass("java.lang.ref.Reference");
- AhatInstance ref = value.asAhatInstance();
- return new Reference(AhatClassInstance.this, "." + field.name, ref, strong);
- }
- return null;
- }
- };
- return new ReferenceIterator(refs);
+ Iterable<Reference> getReferences() {
+ if (isInstanceOfClass("java.lang.ref.Reference")) {
+ return new WeakReferentReferenceIterator();
+ }
+ return new StrongReferenceIterator();
}
/**
- * Returns true if this is an instance of a class with the given name.
+ * Returns true if this is an instance of a (subclass of a) class with the
+ * given name.
*/
private boolean isInstanceOfClass(String className) {
AhatClassObj cls = getClassObj();
@@ -262,4 +247,131 @@ public class AhatClassInstance extends AhatInstance {
bitmap.setRGB(0, 0, info.width, info.height, abgr, 0, info.width);
return bitmap;
}
+
+ private static class InstanceFieldIterator implements Iterable<FieldValue>,
+ Iterator<FieldValue> {
+ // The complete list of instance field values to iterate over, including
+ // superclass field values.
+ private Value[] mValues;
+ private int mValueIndex;
+
+ // The list of field descriptors specific to the current class in the
+ // class hierarchy, not including superclass field descriptors.
+ // mFields and mFieldIndex are reset each time we walk up to the next
+ // superclass in the call hierarchy.
+ private Field[] mFields;
+ private int mFieldIndex;
+ private AhatClassObj mNextClassObj;
+
+ public InstanceFieldIterator(Value[] values, AhatClassObj classObj) {
+ mValues = values;
+ mFields = classObj.getInstanceFields();
+ mValueIndex = 0;
+ mFieldIndex = 0;
+ mNextClassObj = classObj.getSuperClassObj();
+ }
+
+ @Override
+ public boolean hasNext() {
+ // If we have reached the end of the fields in the current class,
+ // continue walking up the class hierarchy to get superclass fields as
+ // well.
+ while (mFieldIndex == mFields.length && mNextClassObj != null) {
+ mFields = mNextClassObj.getInstanceFields();
+ mFieldIndex = 0;
+ mNextClassObj = mNextClassObj.getSuperClassObj();
+ }
+ return mFieldIndex < mFields.length;
+ }
+
+ @Override
+ public FieldValue next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ Field field = mFields[mFieldIndex++];
+ Value value = mValues[mValueIndex++];
+ return new FieldValue(field.name, field.type, value);
+ }
+
+ @Override
+ public Iterator<FieldValue> iterator() {
+ return this;
+ }
+ }
+
+ /**
+ * A Reference iterator that iterates over the fields of this instance
+ * assuming all field references are strong references.
+ */
+ private class StrongReferenceIterator implements Iterable<Reference>,
+ Iterator<Reference> {
+ private Iterator<FieldValue> mIter = getInstanceFields().iterator();
+ private Reference mNext = null;
+
+ @Override
+ public boolean hasNext() {
+ while (mNext == null && mIter.hasNext()) {
+ FieldValue field = mIter.next();
+ if (field.value != null && field.value.isAhatInstance()) {
+ AhatInstance ref = field.value.asAhatInstance();
+ mNext = new Reference(AhatClassInstance.this, "." + field.name, ref, true);
+ }
+ }
+ return mNext != null;
+ }
+
+ @Override
+ public Reference next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ Reference next = mNext;
+ mNext = null;
+ return next;
+ }
+
+ @Override
+ public Iterator<Reference> iterator() {
+ return this;
+ }
+ }
+
+ /**
+ * A Reference iterator that iterates over the fields of a subclass of
+ * java.lang.ref.Reference, where the 'referent' field is considered weak.
+ */
+ private class WeakReferentReferenceIterator implements Iterable<Reference>,
+ Iterator<Reference> {
+ private Iterator<FieldValue> mIter = getInstanceFields().iterator();
+ private Reference mNext = null;
+
+ @Override
+ public boolean hasNext() {
+ while (mNext == null && mIter.hasNext()) {
+ FieldValue field = mIter.next();
+ if (field.value != null && field.value.isAhatInstance()) {
+ boolean strong = !field.name.equals("referent");
+ AhatInstance ref = field.value.asAhatInstance();
+ mNext = new Reference(AhatClassInstance.this, "." + field.name, ref, strong);
+ }
+ }
+ return mNext != null;
+ }
+
+ @Override
+ public Reference next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ Reference next = mNext;
+ mNext = null;
+ return next;
+ }
+
+ @Override
+ public Iterator<Reference> iterator() {
+ return this;
+ }
+ }
}
diff --git a/tools/ahat/src/heapdump/AhatClassObj.java b/tools/ahat/src/heapdump/AhatClassObj.java
index 052d7a8e88..08c70974c6 100644
--- a/tools/ahat/src/heapdump/AhatClassObj.java
+++ b/tools/ahat/src/heapdump/AhatClassObj.java
@@ -17,7 +17,6 @@
package com.android.ahat.heapdump;
import com.android.tools.perflib.heap.ClassObj;
-import com.android.tools.perflib.heap.Field;
import com.android.tools.perflib.heap.Instance;
import java.util.AbstractList;
import java.util.Arrays;
@@ -30,6 +29,7 @@ public class AhatClassObj extends AhatInstance {
private AhatClassObj mSuperClassObj;
private AhatInstance mClassLoader;
private FieldValue[] mStaticFieldValues;
+ private Field[] mInstanceFields;
public AhatClassObj(long id) {
super(id);
@@ -51,15 +51,22 @@ public class AhatClassObj extends AhatInstance {
mClassLoader = snapshot.findInstance(loader.getId());
}
- Collection<Map.Entry<Field, Object>> fieldValues = classObj.getStaticFieldValues().entrySet();
+ Collection<Map.Entry<com.android.tools.perflib.heap.Field, Object>> fieldValues
+ = classObj.getStaticFieldValues().entrySet();
mStaticFieldValues = new FieldValue[fieldValues.size()];
int index = 0;
- for (Map.Entry<Field, Object> field : fieldValues) {
+ for (Map.Entry<com.android.tools.perflib.heap.Field, Object> field : fieldValues) {
String name = field.getKey().getName();
String type = field.getKey().getType().toString();
Value value = snapshot.getValue(field.getValue());
mStaticFieldValues[index++] = new FieldValue(name, type, value);
}
+
+ com.android.tools.perflib.heap.Field[] fields = classObj.getFields();
+ mInstanceFields = new Field[fields.length];
+ for (int i = 0; i < fields.length; i++) {
+ mInstanceFields[i] = new Field(fields[i].getName(), fields[i].getType().toString());
+ }
}
/**
@@ -90,8 +97,15 @@ public class AhatClassObj extends AhatInstance {
return Arrays.asList(mStaticFieldValues);
}
+ /**
+ * Returns the fields of instances of this class.
+ */
+ public Field[] getInstanceFields() {
+ return mInstanceFields;
+ }
+
@Override
- ReferenceIterator getReferences() {
+ Iterable<Reference> getReferences() {
List<Reference> refs = new AbstractList<Reference>() {
@Override
public int size() {
@@ -108,7 +122,7 @@ public class AhatClassObj extends AhatInstance {
return null;
}
};
- return new ReferenceIterator(refs);
+ return new SkipNullsIterator(refs);
}
@Override public boolean isClassObj() {
diff --git a/tools/ahat/src/heapdump/AhatInstance.java b/tools/ahat/src/heapdump/AhatInstance.java
index 39a844af5f..0e7855801d 100644
--- a/tools/ahat/src/heapdump/AhatInstance.java
+++ b/tools/ahat/src/heapdump/AhatInstance.java
@@ -72,6 +72,7 @@ public abstract class AhatInstance implements Diffable<AhatInstance>,
* snapshot.findInstance have been initialized yet.
*/
void initialize(AhatSnapshot snapshot, Instance inst, Site site) {
+ site.addInstance(this);
mSize = new Size(inst.getSize(), 0);
mHeap = snapshot.getHeap(inst.getHeap().getName());
@@ -147,7 +148,7 @@ public abstract class AhatInstance implements Diffable<AhatInstance>,
* Returns an iterator over the references this AhatInstance has to other
* AhatInstances.
*/
- abstract ReferenceIterator getReferences();
+ abstract Iterable<Reference> getReferences();
/**
* Returns true if this instance is marked as a root instance.
diff --git a/tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java b/tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java
index 2b3e056a1e..8b4c6796aa 100644
--- a/tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java
+++ b/tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java
@@ -29,10 +29,6 @@ public class AhatPlaceHolderClassObj extends AhatClassObj {
baseline.setBaseline(this);
}
- @Override public Size getSize() {
- return Size.ZERO;
- }
-
@Override public Size getRetainedSize(AhatHeap heap) {
return Size.ZERO;
}
@@ -68,4 +64,8 @@ public class AhatPlaceHolderClassObj extends AhatClassObj {
@Override public AhatInstance getClassLoader() {
return getBaseline().asClassObj().getClassLoader().getBaseline();
}
+
+ @Override public Field[] getInstanceFields() {
+ return getBaseline().asClassObj().getInstanceFields();
+ }
}
diff --git a/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java b/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java
index d797b11030..9abc952072 100644
--- a/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java
+++ b/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java
@@ -65,8 +65,8 @@ public class AhatPlaceHolderInstance extends AhatInstance {
}
@Override
- ReferenceIterator getReferences() {
+ Iterable<Reference> getReferences() {
List<Reference> refs = Collections.emptyList();
- return new ReferenceIterator(refs);
+ return refs;
}
}
diff --git a/tools/ahat/src/heapdump/AhatSnapshot.java b/tools/ahat/src/heapdump/AhatSnapshot.java
index 7df78c50b5..1b2cf3c59f 100644
--- a/tools/ahat/src/heapdump/AhatSnapshot.java
+++ b/tools/ahat/src/heapdump/AhatSnapshot.java
@@ -35,7 +35,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -48,9 +47,6 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> {
// List of all ahat instances stored in increasing order by id.
private final List<AhatInstance> mInstances = new ArrayList<AhatInstance>();
- // Map from class name to class object.
- private final Map<String, AhatClassObj> mClasses = new HashMap<String, AhatClassObj>();
-
private final List<AhatHeap> mHeaps = new ArrayList<AhatHeap>();
private AhatSnapshot mBaseline = this;
@@ -113,7 +109,6 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> {
} else if (inst instanceof ClassObj) {
AhatClassObj classObj = new AhatClassObj(id);
mInstances.add(classObj);
- mClasses.put(((ClassObj)inst).getClassName(), classObj);
}
return true;
}
@@ -145,8 +140,7 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> {
if (stack != null) {
frames = stack.getFrames();
}
- Site site = mRootSite.add(frames, frames == null ? 0 : frames.length, ahat);
- ahat.initialize(this, inst, site);
+ ahat.initialize(this, inst, mRootSite.getSite(frames));
Long registeredNativeSize = registeredNative.get(inst);
if (registeredNativeSize != null) {
@@ -177,7 +171,7 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> {
heap.addToSize(superRoot.getRetainedSize(heap));
}
- mRootSite.computeObjectsInfos(mHeaps.size());
+ mRootSite.prepareForUse(0, mHeaps.size());
}
/**
@@ -213,15 +207,6 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> {
}
/**
- * Returns the class object for the class with given name.
- * Returns null if there is no class object for the given name.
- * Note: This method is exposed for testing purposes.
- */
- public AhatClassObj findClass(String name) {
- return mClasses.get(name);
- }
-
- /**
* Returns the heap with the given name, if any.
* Returns null if no heap with the given name could be found.
*/
@@ -260,19 +245,11 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> {
return mRootSite;
}
- // Get the site associated with the given id and depth.
+ // Get the site associated with the given id.
// Returns the root site if no such site found.
- public Site getSite(int id, int depth) {
- AhatInstance obj = findInstance(id);
- if (obj == null) {
- return mRootSite;
- }
-
- Site site = obj.getSite();
- for (int i = 0; i < depth && site.getParent() != null; i++) {
- site = site.getParent();
- }
- return site;
+ public Site getSite(long id) {
+ Site site = mRootSite.findSite(id);
+ return site == null ? mRootSite : site;
}
// Return the Value for the given perflib value object.
@@ -280,7 +257,7 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> {
if (value instanceof Instance) {
value = findInstance(((Instance)value).getId());
}
- return value == null ? null : new Value(value);
+ return Value.pack(value);
}
public void setBaseline(AhatSnapshot baseline) {
diff --git a/tools/ahat/src/heapdump/Diff.java b/tools/ahat/src/heapdump/Diff.java
index 489f709b04..98c7e58d56 100644
--- a/tools/ahat/src/heapdump/Diff.java
+++ b/tools/ahat/src/heapdump/Diff.java
@@ -333,7 +333,7 @@ public class Diff {
// Add placeholders to their corresponding sites.
// This requires the sites have already been diffed.
for (AhatInstance placeholder : placeholders) {
- placeholder.getBaseline().getSite().getBaseline().addPlaceHolderInstance(placeholder);
+ placeholder.getBaseline().getSite().getBaseline().addInstance(placeholder);
}
}
}
diff --git a/tools/ahat/src/heapdump/DiffFields.java b/tools/ahat/src/heapdump/DiffFields.java
index dd73456c0f..e3c671fe21 100644
--- a/tools/ahat/src/heapdump/DiffFields.java
+++ b/tools/ahat/src/heapdump/DiffFields.java
@@ -17,7 +17,6 @@
package com.android.ahat.heapdump;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -29,14 +28,19 @@ import java.util.List;
public class DiffFields {
/**
* Return the result of diffing two collections of field values.
- * The input collections 'current' and 'baseline' are not modified by this function.
*/
- public static List<DiffedFieldValue> diff(Collection<FieldValue> current,
- Collection<FieldValue> baseline) {
- List<FieldValue> currentSorted = new ArrayList<FieldValue>(current);
+ public static List<DiffedFieldValue> diff(Iterable<FieldValue> current,
+ Iterable<FieldValue> baseline) {
+ List<FieldValue> currentSorted = new ArrayList<FieldValue>();
+ for (FieldValue field : current) {
+ currentSorted.add(field);
+ }
Collections.sort(currentSorted, FOR_DIFF);
- List<FieldValue> baselineSorted = new ArrayList<FieldValue>(baseline);
+ List<FieldValue> baselineSorted = new ArrayList<FieldValue>();
+ for (FieldValue field : baseline) {
+ baselineSorted.add(field);
+ }
Collections.sort(baselineSorted, FOR_DIFF);
// Merge the two lists to form the diffed list of fields.
diff --git a/tools/ahat/src/heapdump/DominatorReferenceIterator.java b/tools/ahat/src/heapdump/DominatorReferenceIterator.java
index ce2e6efa6e..0b99e496cc 100644
--- a/tools/ahat/src/heapdump/DominatorReferenceIterator.java
+++ b/tools/ahat/src/heapdump/DominatorReferenceIterator.java
@@ -25,11 +25,11 @@ import java.util.NoSuchElementException;
*/
class DominatorReferenceIterator implements Iterator<AhatInstance>,
Iterable<AhatInstance> {
- private ReferenceIterator mIter;
+ private Iterator<Reference> mIter;
private AhatInstance mNext;
- public DominatorReferenceIterator(ReferenceIterator iter) {
- mIter = iter;
+ public DominatorReferenceIterator(Iterable<Reference> iter) {
+ mIter = iter.iterator();
mNext = null;
}
diff --git a/tools/ahat/src/heapdump/Field.java b/tools/ahat/src/heapdump/Field.java
new file mode 100644
index 0000000000..01f87c726e
--- /dev/null
+++ b/tools/ahat/src/heapdump/Field.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+public class Field {
+ public final String name;
+ public final String type;
+
+ public Field(String name, String type) {
+ this.name = name;
+ this.type = type;
+ }
+}
diff --git a/tools/ahat/src/heapdump/Site.java b/tools/ahat/src/heapdump/Site.java
index f0fc5d2d6c..82931f0056 100644
--- a/tools/ahat/src/heapdump/Site.java
+++ b/tools/ahat/src/heapdump/Site.java
@@ -19,6 +19,7 @@ package com.android.ahat.heapdump;
import com.android.tools.perflib.heap.StackFrame;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -33,16 +34,20 @@ public class Site implements Diffable<Site> {
private String mFilename;
private int mLineNumber;
- // To identify this site, we pick a stack trace that includes the site.
- // mId is the id of an object allocated at that stack trace, and mDepth
- // is the number of calls between this site and the innermost site of
- // allocation of the object with mId.
- // For the root site, mId is 0 and mDepth is 0.
- private long mId;
- private int mDepth;
+ // A unique id to identify this site with. The id is chosen based on a
+ // depth first traversal of the complete site tree, which gives it the
+ // following desired properties:
+ // * The id can easily be represented in a URL.
+ // * The id is determined by the hprof file, so that the same id can be used
+ // across different instances for viewing the same hprof file.
+ // * A binary search can be used to find a site by id from the root site in
+ // log time.
+ //
+ // The id is set by prepareForUse after the complete site tree is constructed.
+ private long mId = -1;
// The total size of objects allocated in this site (including child sites),
- // organized by heap index. Computed as part of computeObjectsInfos.
+ // organized by heap index. Computed as part of prepareForUse.
private Size[] mSizesByHeap;
// List of child sites.
@@ -99,18 +104,15 @@ public class Site implements Diffable<Site> {
* Construct a root site.
*/
public Site(String name) {
- this(null, name, "", "", 0, 0, 0);
+ this(null, name, "", "", 0);
}
- public Site(Site parent, String method, String signature, String file,
- int line, long id, int depth) {
+ private Site(Site parent, String method, String signature, String file, int line) {
mParent = parent;
mMethodName = method;
mSignature = signature;
mFilename = file;
mLineNumber = line;
- mId = id;
- mDepth = depth;
mChildren = new ArrayList<Site>();
mObjects = new ArrayList<AhatInstance>();
mObjectsInfos = new ArrayList<ObjectsInfo>();
@@ -119,50 +121,63 @@ public class Site implements Diffable<Site> {
}
/**
- * Add an instance to this site.
+ * Get a child site of this site.
* Returns the site at which the instance was allocated.
- * @param frames - The list of frames in the stack trace, starting with the inner-most frame.
- * @param depth - The number of frames remaining before the inner-most frame is reached.
+ * @param frames - The list of frames in the stack trace, starting with the
+ * inner-most frame. May be null, in which case this site is
+ * returned.
*/
- Site add(StackFrame[] frames, int depth, AhatInstance inst) {
- return add(this, frames, depth, inst);
+ Site getSite(StackFrame frames[]) {
+ return frames == null ? this : getSite(this, frames);
}
- private static Site add(Site site, StackFrame[] frames, int depth, AhatInstance inst) {
- while (depth > 0) {
- StackFrame next = frames[depth - 1];
+ private static Site getSite(Site site, StackFrame frames[]) {
+ for (int s = frames.length - 1; s >= 0; --s) {
+ StackFrame frame = frames[s];
Site child = null;
for (int i = 0; i < site.mChildren.size(); i++) {
Site curr = site.mChildren.get(i);
- if (curr.mLineNumber == next.getLineNumber()
- && curr.mMethodName.equals(next.getMethodName())
- && curr.mSignature.equals(next.getSignature())
- && curr.mFilename.equals(next.getFilename())) {
+ if (curr.mLineNumber == frame.getLineNumber()
+ && curr.mMethodName.equals(frame.getMethodName())
+ && curr.mSignature.equals(frame.getSignature())
+ && curr.mFilename.equals(frame.getFilename())) {
child = curr;
break;
}
}
if (child == null) {
- child = new Site(site, next.getMethodName(), next.getSignature(),
- next.getFilename(), next.getLineNumber(), inst.getId(), depth - 1);
+ child = new Site(site, frame.getMethodName(), frame.getSignature(),
+ frame.getFilename(), frame.getLineNumber());
site.mChildren.add(child);
}
- depth = depth - 1;
site = child;
}
- site.mObjects.add(inst);
return site;
}
/**
- * Recompute the ObjectsInfos for this and all child sites.
- * This should be done after the sites tree has been formed. It should also
- * be done after dominators computation has been performed to ensure only
- * reachable objects are included in the ObjectsInfos.
+ * Add an instance allocated at this site.
+ */
+ void addInstance(AhatInstance inst) {
+ mObjects.add(inst);
+ }
+
+ /**
+ * Prepare this and all child sites for use.
+ * Recomputes site ids, sizes, ObjectInfos for this and all child sites.
+ * This should be called after the sites tree has been formed and after
+ * dominators computation has been performed to ensure only reachable
+ * objects are included in the ObjectsInfos.
*
+ * @param id - The smallest id that is allowed to be used for this site or
+ * any of its children.
* @param numHeaps - The number of heaps in the heap dump.
+ * @return An id larger than the largest id used for this site or any of its
+ * children.
*/
- void computeObjectsInfos(int numHeaps) {
+ long prepareForUse(long id, int numHeaps) {
+ mId = id++;
+
// Count up the total sizes by heap.
mSizesByHeap = new Size[numHeaps];
for (int i = 0; i < numHeaps; ++i) {
@@ -183,7 +198,7 @@ public class Site implements Diffable<Site> {
// Add objects allocated in child sites.
for (Site child : mChildren) {
- child.computeObjectsInfos(numHeaps);
+ id = child.prepareForUse(id, numHeaps);
for (ObjectsInfo childInfo : child.mObjectsInfos) {
ObjectsInfo info = getObjectsInfo(childInfo.heap, childInfo.classObj);
info.numInstances += childInfo.numInstances;
@@ -193,6 +208,7 @@ public class Site implements Diffable<Site> {
mSizesByHeap[i] = mSizesByHeap[i].plus(child.mSizesByHeap[i]);
}
}
+ return id;
}
// Get the size of a site for a specific heap.
@@ -285,22 +301,49 @@ public class Site implements Diffable<Site> {
}
/**
- * Returns the id of some object allocated in this site.
+ * Returns the unique id of this site.
*/
public long getId() {
return mId;
}
/**
- * Returns the number of frames between this site and the site where the
- * object with id getId() was allocated.
+ * Find the child site with the given id.
+ * Returns null if no such site was found.
*/
- public int getDepth() {
- return mDepth;
+ public Site findSite(long id) {
+ if (id == mId) {
+ return this;
+ }
+
+ // Binary search over the children to find the right child to search in.
+ int start = 0;
+ int end = mChildren.size();
+ while (start < end) {
+ int mid = start + ((end - start) / 2);
+ Site midSite = mChildren.get(mid);
+ if (id < midSite.mId) {
+ end = mid;
+ } else if (mid + 1 == end) {
+ // This is the last child we could possibly find the desired site in,
+ // so search in this child.
+ return midSite.findSite(id);
+ } else if (id < mChildren.get(mid + 1).mId) {
+ // The desired site has an id between this child's id and the next
+ // child's id, so search in this child.
+ return midSite.findSite(id);
+ } else {
+ start = mid + 1;
+ }
+ }
+ return null;
}
+ /**
+ * Returns an unmodifiable list of this site's immediate children.
+ */
public List<Site> getChildren() {
- return mChildren;
+ return Collections.unmodifiableList(mChildren);
}
void setBaseline(Site baseline) {
@@ -314,13 +357,4 @@ public class Site implements Diffable<Site> {
@Override public boolean isPlaceHolder() {
return false;
}
-
- /**
- * Adds a place holder instance to this site and all parent sites.
- */
- void addPlaceHolderInstance(AhatInstance placeholder) {
- for (Site site = this; site != null; site = site.mParent) {
- site.mObjects.add(placeholder);
- }
- }
}
diff --git a/tools/ahat/src/heapdump/ReferenceIterator.java b/tools/ahat/src/heapdump/SkipNullsIterator.java
index a707fb24ef..e99fe5e8ea 100644
--- a/tools/ahat/src/heapdump/ReferenceIterator.java
+++ b/tools/ahat/src/heapdump/SkipNullsIterator.java
@@ -17,49 +17,40 @@
package com.android.ahat.heapdump;
import java.util.Iterator;
-import java.util.List;
import java.util.NoSuchElementException;
-class ReferenceIterator implements Iterator<Reference>,
- Iterable<Reference> {
- private List<Reference> mRefs;
- private int mLength;
- private int mNextIndex;
- private Reference mNext;
+/**
+ * An iterator that skips over nulls.
+ */
+class SkipNullsIterator<T> implements Iterator<T>, Iterable<T> {
+ Iterator<T> mIter;
+ private T mNext;
- /**
- * Construct a ReferenceIterator that iterators over the given list of
- * references. Elements of the given list of references may be null, in
- * which case the ReferenceIterator will skip over them.
- */
- public ReferenceIterator(List<Reference> refs) {
- mRefs = refs;
- mLength = refs.size();
- mNextIndex = 0;
+ public SkipNullsIterator(Iterable<T> iterable) {
+ mIter = iterable.iterator();
mNext = null;
}
@Override
public boolean hasNext() {
- while (mNext == null && mNextIndex < mLength) {
- mNext = mRefs.get(mNextIndex);
- mNextIndex++;
+ while (mNext == null && mIter.hasNext()) {
+ mNext = mIter.next();
}
return mNext != null;
}
@Override
- public Reference next() {
+ public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
- Reference next = mNext;
+ T next = mNext;
mNext = null;
return next;
}
@Override
- public Iterator<Reference> iterator() {
+ public Iterator<T> iterator() {
return this;
}
}
diff --git a/tools/ahat/src/heapdump/SuperRoot.java b/tools/ahat/src/heapdump/SuperRoot.java
index 54410cf1a6..d377113862 100644
--- a/tools/ahat/src/heapdump/SuperRoot.java
+++ b/tools/ahat/src/heapdump/SuperRoot.java
@@ -39,8 +39,8 @@ public class SuperRoot extends AhatInstance implements DominatorsComputation.Nod
}
@Override
- ReferenceIterator getReferences() {
- List<Reference> refs = new AbstractList<Reference>() {
+ Iterable<Reference> getReferences() {
+ return new AbstractList<Reference>() {
@Override
public int size() {
return mRoots.size();
@@ -52,6 +52,5 @@ public class SuperRoot extends AhatInstance implements DominatorsComputation.Nod
return new Reference(null, field, mRoots.get(index), true);
}
};
- return new ReferenceIterator(refs);
}
}
diff --git a/tools/ahat/src/heapdump/Value.java b/tools/ahat/src/heapdump/Value.java
index c1f30228e0..7f86c01efb 100644
--- a/tools/ahat/src/heapdump/Value.java
+++ b/tools/ahat/src/heapdump/Value.java
@@ -20,19 +20,72 @@ package com.android.ahat.heapdump;
* Value represents a field value in a heap dump. The field value is either a
* subclass of AhatInstance or a primitive Java type.
*/
-public class Value {
- private Object mObject;
+public abstract class Value {
+ public static Value pack(AhatInstance value) {
+ return value == null ? null : new InstanceValue(value);
+ }
/**
* Constructs a value from a generic Java Object.
* The Object must either be a boxed Java primitive type or a subclass of
* AhatInstance. The object must not be null.
*/
- public Value(Object object) {
- // TODO: Check that the Object is either an AhatSnapshot or boxed Java
- // primitive type?
- assert object != null;
- mObject = object;
+ public static Value pack(Object object) {
+ if (object == null) {
+ return null;
+ } else if (object instanceof AhatInstance) {
+ return Value.pack((AhatInstance)object);
+ } else if (object instanceof Boolean) {
+ return Value.pack(((Boolean)object).booleanValue());
+ } else if (object instanceof Character) {
+ return Value.pack(((Character)object).charValue());
+ } else if (object instanceof Float) {
+ return Value.pack(((Float)object).floatValue());
+ } else if (object instanceof Double) {
+ return Value.pack(((Double)object).doubleValue());
+ } else if (object instanceof Byte) {
+ return Value.pack(((Byte)object).byteValue());
+ } else if (object instanceof Short) {
+ return Value.pack(((Short)object).shortValue());
+ } else if (object instanceof Integer) {
+ return Value.pack(((Integer)object).intValue());
+ } else if (object instanceof Long) {
+ return Value.pack(((Long)object).longValue());
+ }
+ throw new IllegalArgumentException(
+ "AhatInstance or primitive type required, but got: " + object.toString());
+ }
+
+ public static Value pack(boolean value) {
+ return new BooleanValue(value);
+ }
+
+ public static Value pack(char value) {
+ return new CharValue(value);
+ }
+
+ public static Value pack(float value) {
+ return new FloatValue(value);
+ }
+
+ public static Value pack(double value) {
+ return new DoubleValue(value);
+ }
+
+ public static Value pack(byte value) {
+ return new ByteValue(value);
+ }
+
+ public static Value pack(short value) {
+ return new ShortValue(value);
+ }
+
+ public static Value pack(int value) {
+ return new IntValue(value);
+ }
+
+ public static Value pack(long value) {
+ return new LongValue(value);
}
/**
@@ -40,7 +93,7 @@ public class Value {
* primitive value.
*/
public boolean isAhatInstance() {
- return mObject instanceof AhatInstance;
+ return false;
}
/**
@@ -48,9 +101,6 @@ public class Value {
* Returns null if the Value represents a Java primitive value.
*/
public AhatInstance asAhatInstance() {
- if (isAhatInstance()) {
- return (AhatInstance)mObject;
- }
return null;
}
@@ -58,7 +108,7 @@ public class Value {
* Returns true if the Value is an Integer.
*/
public boolean isInteger() {
- return mObject instanceof Integer;
+ return false;
}
/**
@@ -66,9 +116,6 @@ public class Value {
* Returns null if the Value does not represent an Integer.
*/
public Integer asInteger() {
- if (isInteger()) {
- return (Integer)mObject;
- }
return null;
}
@@ -76,7 +123,7 @@ public class Value {
* Returns true if the Value is an Long.
*/
public boolean isLong() {
- return mObject instanceof Long;
+ return false;
}
/**
@@ -84,9 +131,6 @@ public class Value {
* Returns null if the Value does not represent an Long.
*/
public Long asLong() {
- if (isLong()) {
- return (Long)mObject;
- }
return null;
}
@@ -95,9 +139,6 @@ public class Value {
* Returns null if the Value does not represent a Byte.
*/
public Byte asByte() {
- if (mObject instanceof Byte) {
- return (Byte)mObject;
- }
return null;
}
@@ -106,28 +147,255 @@ public class Value {
* Returns null if the Value does not represent a Char.
*/
public Character asChar() {
- if (mObject instanceof Character) {
- return (Character)mObject;
- }
return null;
}
- public String toString() {
- return mObject.toString();
+ @Override
+ public abstract String toString();
+
+ public Value getBaseline() {
+ return this;
}
public static Value getBaseline(Value value) {
- if (value == null || !value.isAhatInstance()) {
- return value;
+ return value == null ? null : value.getBaseline();
+ }
+
+ @Override
+ public abstract boolean equals(Object other);
+
+ private static class BooleanValue extends Value {
+ private boolean mBool;
+
+ BooleanValue(boolean bool) {
+ mBool = bool;
+ }
+
+ @Override
+ public String toString() {
+ return Boolean.toString(mBool);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof BooleanValue) {
+ BooleanValue value = (BooleanValue)other;
+ return mBool == value.mBool;
+ }
+ return false;
}
- return new Value(value.asAhatInstance().getBaseline());
}
- @Override public boolean equals(Object other) {
- if (other instanceof Value) {
- Value value = (Value)other;
- return mObject.equals(value.mObject);
+ private static class ByteValue extends Value {
+ private byte mByte;
+
+ ByteValue(byte b) {
+ mByte = b;
+ }
+
+ @Override
+ public Byte asByte() {
+ return mByte;
+ }
+
+ @Override
+ public String toString() {
+ return Byte.toString(mByte);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof ByteValue) {
+ ByteValue value = (ByteValue)other;
+ return mByte == value.mByte;
+ }
+ return false;
+ }
+ }
+
+ private static class CharValue extends Value {
+ private char mChar;
+
+ CharValue(char c) {
+ mChar = c;
+ }
+
+ @Override
+ public Character asChar() {
+ return mChar;
+ }
+
+ @Override
+ public String toString() {
+ return Character.toString(mChar);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof CharValue) {
+ CharValue value = (CharValue)other;
+ return mChar == value.mChar;
+ }
+ return false;
+ }
+ }
+
+ private static class DoubleValue extends Value {
+ private double mDouble;
+
+ DoubleValue(double d) {
+ mDouble = d;
+ }
+
+ @Override
+ public String toString() {
+ return Double.toString(mDouble);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof DoubleValue) {
+ DoubleValue value = (DoubleValue)other;
+ return mDouble == value.mDouble;
+ }
+ return false;
+ }
+ }
+
+ private static class FloatValue extends Value {
+ private float mFloat;
+
+ FloatValue(float f) {
+ mFloat = f;
+ }
+
+ @Override
+ public String toString() {
+ return Float.toString(mFloat);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof FloatValue) {
+ FloatValue value = (FloatValue)other;
+ return mFloat == value.mFloat;
+ }
+ return false;
+ }
+ }
+
+ private static class InstanceValue extends Value {
+ private AhatInstance mInstance;
+
+ InstanceValue(AhatInstance inst) {
+ assert(inst != null);
+ mInstance = inst;
+ }
+
+ @Override
+ public boolean isAhatInstance() {
+ return true;
+ }
+
+ @Override
+ public AhatInstance asAhatInstance() {
+ return mInstance;
+ }
+
+ @Override
+ public String toString() {
+ return mInstance.toString();
+ }
+
+ @Override
+ public Value getBaseline() {
+ return InstanceValue.pack(mInstance.getBaseline());
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof InstanceValue) {
+ InstanceValue value = (InstanceValue)other;
+ return mInstance.equals(value.mInstance);
+ }
+ return false;
+ }
+ }
+
+ private static class IntValue extends Value {
+ private int mInt;
+
+ IntValue(int i) {
+ mInt = i;
+ }
+
+ @Override
+ public boolean isInteger() {
+ return true;
+ }
+
+ @Override
+ public Integer asInteger() {
+ return mInt;
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(mInt);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof IntValue) {
+ IntValue value = (IntValue)other;
+ return mInt == value.mInt;
+ }
+ return false;
+ }
+ }
+
+ private static class LongValue extends Value {
+ private long mLong;
+
+ LongValue(long l) {
+ mLong = l;
+ }
+
+ @Override
+ public boolean isLong() {
+ return true;
+ }
+
+ @Override
+ public Long asLong() {
+ return mLong;
+ }
+
+ @Override
+ public String toString() {
+ return Long.toString(mLong);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof LongValue) {
+ LongValue value = (LongValue)other;
+ return mLong == value.mLong;
+ }
+ return false;
+ }
+ }
+
+ private static class ShortValue extends Value {
+ private short mShort;
+
+ ShortValue(short s) {
+ mShort = s;
+ }
+
+ @Override
+ public String toString() {
+ return Short.toString(mShort);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof ShortValue) {
+ ShortValue value = (ShortValue)other;
+ return mShort == value.mShort;
+ }
+ return false;
}
- return false;
}
}
diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/test-dump/Main.java
index 14c09af01d..333d28c214 100644
--- a/tools/ahat/test-dump/Main.java
+++ b/tools/ahat/test-dump/Main.java
@@ -102,12 +102,23 @@ public class Main {
public StackSmasher stackSmasherAdded;
public static String modifiedStaticField;
public int[] modifiedArray;
+ public Object objectAllocatedAtKnownSite1;
+ public Object objectAllocatedAtKnownSite2;
+
+ private void allocateObjectAtKnownSite1() {
+ objectAllocatedAtKnownSite1 = new Object();
+ allocateObjectAtKnownSite2();
+ }
+
+ private void allocateObjectAtKnownSite2() {
+ objectAllocatedAtKnownSite2 = new Object();
+ }
DumpedStuff(boolean baseline) {
- int N = baseline ? 400000 : 1000000;
- bigArray = new byte[N];
- for (int i = 0; i < N; i++) {
- bigArray[i] = (byte)((i*i) & 0xFF);
+ int n = baseline ? 400000 : 1000000;
+ bigArray = new byte[n];
+ for (int i = 0; i < n; i++) {
+ bigArray[i] = (byte)((i * i) & 0xFF);
}
// 0x12345, 50000, and 0xABCDABCD are arbitrary values.
@@ -127,7 +138,9 @@ public class Main {
modifiedObject.modifiedRefField = baseline ? "A1" : "A2";
modifiedObject.unmodifiedRefField = "B";
modifiedStaticField = baseline ? "C1" : "C2";
- modifiedArray = baseline ? new int[]{0,1,2,3} : new int[]{3,1,2,0};
+ modifiedArray = baseline ? new int[]{0, 1, 2, 3} : new int[]{3, 1, 2, 0};
+
+ allocateObjectAtKnownSite1();
// Deep matching dominator trees shouldn't smash the stack when we try
// to diff them. Make some deep dominator trees to help test it.
diff --git a/tools/ahat/test/DiffFieldsTest.java b/tools/ahat/test/DiffFieldsTest.java
index 6abdd47ef6..7dc519d60b 100644
--- a/tools/ahat/test/DiffFieldsTest.java
+++ b/tools/ahat/test/DiffFieldsTest.java
@@ -30,26 +30,26 @@ import static org.junit.Assert.assertNull;
public class DiffFieldsTest {
@Test
public void normalMatchedDiffedFieldValues() {
- FieldValue normal1 = new FieldValue("name", "type", new Value(1));
- FieldValue normal2 = new FieldValue("name", "type", new Value(2));
+ FieldValue normal1 = new FieldValue("name", "type", Value.pack(1));
+ FieldValue normal2 = new FieldValue("name", "type", Value.pack(2));
DiffedFieldValue x = DiffedFieldValue.matched(normal1, normal2);
assertEquals("name", x.name);
assertEquals("type", x.type);
- assertEquals(new Value(1), x.current);
- assertEquals(new Value(2), x.baseline);
+ assertEquals(Value.pack(1), x.current);
+ assertEquals(Value.pack(2), x.baseline);
assertEquals(DiffedFieldValue.Status.MATCHED, x.status);
}
@Test
public void nulledMatchedDiffedFieldValues() {
- FieldValue normal = new FieldValue("name", "type", new Value(1));
+ FieldValue normal = new FieldValue("name", "type", Value.pack(1));
FieldValue nulled = new FieldValue("name", "type", null);
DiffedFieldValue x = DiffedFieldValue.matched(normal, nulled);
assertEquals("name", x.name);
assertEquals("type", x.type);
- assertEquals(new Value(1), x.current);
+ assertEquals(Value.pack(1), x.current);
assertNull(x.baseline);
assertEquals(DiffedFieldValue.Status.MATCHED, x.status);
@@ -57,18 +57,18 @@ public class DiffFieldsTest {
assertEquals("name", y.name);
assertEquals("type", y.type);
assertNull(y.current);
- assertEquals(new Value(1), y.baseline);
+ assertEquals(Value.pack(1), y.baseline);
assertEquals(DiffedFieldValue.Status.MATCHED, y.status);
}
@Test
public void normalAddedDiffedFieldValues() {
- FieldValue normal = new FieldValue("name", "type", new Value(1));
+ FieldValue normal = new FieldValue("name", "type", Value.pack(1));
DiffedFieldValue x = DiffedFieldValue.added(normal);
assertEquals("name", x.name);
assertEquals("type", x.type);
- assertEquals(new Value(1), x.current);
+ assertEquals(Value.pack(1), x.current);
assertEquals(DiffedFieldValue.Status.ADDED, x.status);
}
@@ -85,12 +85,12 @@ public class DiffFieldsTest {
@Test
public void normalDeletedDiffedFieldValues() {
- FieldValue normal = new FieldValue("name", "type", new Value(1));
+ FieldValue normal = new FieldValue("name", "type", Value.pack(1));
DiffedFieldValue x = DiffedFieldValue.deleted(normal);
assertEquals("name", x.name);
assertEquals("type", x.type);
- assertEquals(new Value(1), x.baseline);
+ assertEquals(Value.pack(1), x.baseline);
assertEquals(DiffedFieldValue.Status.DELETED, x.status);
}
diff --git a/tools/ahat/test/InstanceTest.java b/tools/ahat/test/InstanceTest.java
index e05782c0b6..63055db93d 100644
--- a/tools/ahat/test/InstanceTest.java
+++ b/tools/ahat/test/InstanceTest.java
@@ -237,7 +237,7 @@ public class InstanceTest {
public void gcRootPath() throws IOException {
TestDump dump = TestDump.getTestDump();
- AhatClassObj main = dump.getAhatSnapshot().findClass("Main");
+ AhatClassObj main = dump.findClass("Main");
AhatInstance gcPathArray = dump.getDumpedAhatInstance("gcPathArray");
Value value = gcPathArray.asArrayInstance().getValue(2);
AhatInstance base = value.asAhatInstance();
@@ -333,7 +333,7 @@ public class InstanceTest {
@Test
public void classObjNotABitmap() throws IOException {
TestDump dump = TestDump.getTestDump();
- AhatInstance obj = dump.getAhatSnapshot().findClass("Main");
+ AhatInstance obj = dump.findClass("Main");
assertNull(obj.asBitmap());
}
@@ -348,7 +348,7 @@ public class InstanceTest {
@Test
public void classObjToString() throws IOException {
TestDump dump = TestDump.getTestDump();
- AhatInstance obj = dump.getAhatSnapshot().findClass("Main");
+ AhatInstance obj = dump.findClass("Main");
assertEquals("class Main", obj.toString());
}
diff --git a/tools/ahat/test/ObjectHandlerTest.java b/tools/ahat/test/ObjectHandlerTest.java
index cd0ba23013..1b8a781e0c 100644
--- a/tools/ahat/test/ObjectHandlerTest.java
+++ b/tools/ahat/test/ObjectHandlerTest.java
@@ -42,7 +42,7 @@ public class ObjectHandlerTest {
AhatSnapshot snapshot = dump.getAhatSnapshot();
AhatHandler handler = new ObjectHandler(snapshot);
- AhatInstance object = snapshot.findClass("Main");
+ AhatInstance object = dump.findClass("Main");
assertNotNull(object);
TestHandler.testNoCrash(handler, "http://localhost:7100/object?id=" + object.getId());
@@ -55,7 +55,7 @@ public class ObjectHandlerTest {
AhatSnapshot snapshot = dump.getAhatSnapshot();
AhatHandler handler = new ObjectHandler(snapshot);
- AhatInstance object = snapshot.findClass("java.lang.String");
+ AhatInstance object = dump.findClass("java.lang.String");
assertNotNull(object);
TestHandler.testNoCrash(handler, "http://localhost:7100/object?id=" + object.getId());
diff --git a/tools/ahat/test/SiteTest.java b/tools/ahat/test/SiteTest.java
new file mode 100644
index 0000000000..dc0fe08297
--- /dev/null
+++ b/tools/ahat/test/SiteTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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.ahat.heapdump.AhatInstance;
+import com.android.ahat.heapdump.AhatSnapshot;
+import com.android.ahat.heapdump.Site;
+import java.io.IOException;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+public class SiteTest {
+ @Test
+ public void objectsAllocatedAtKnownSites() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ AhatSnapshot snapshot = dump.getAhatSnapshot();
+
+ AhatInstance obj1 = dump.getDumpedAhatInstance("objectAllocatedAtKnownSite1");
+ AhatInstance obj2 = dump.getDumpedAhatInstance("objectAllocatedAtKnownSite2");
+ Site s2 = obj2.getSite();
+ Site s1b = s2.getParent();
+ Site s1a = obj1.getSite();
+ Site s = s1a.getParent();
+
+ // TODO: The following commented out assertion fails due to a problem with
+ // proguard deobfuscation. That bug should be fixed.
+ // assertEquals("Main.java", s.getFilename());
+
+ assertEquals("Main.java", s1a.getFilename());
+ assertEquals("Main.java", s1b.getFilename());
+ assertEquals("Main.java", s2.getFilename());
+
+ assertEquals("allocateObjectAtKnownSite1", s1a.getMethodName());
+ assertEquals("allocateObjectAtKnownSite1", s1b.getMethodName());
+ assertEquals("allocateObjectAtKnownSite2", s2.getMethodName());
+
+ // TODO: The following commented out assertion fails due to a problem with
+ // stack frame line numbers - we don't get different line numbers
+ // for the different sites, so they are indistiguishable. The
+ // problem with line numbers should be understood and fixed.
+ // assertNotSame(s1a, s1b);
+
+ assertSame(s1a.getParent(), s1b.getParent());
+
+ assertSame(s, snapshot.getSite(s.getId()));
+ assertSame(s1a, snapshot.getSite(s1a.getId()));
+ assertSame(s1b, snapshot.getSite(s1b.getId()));
+ assertSame(s2, snapshot.getSite(s2.getId()));
+ }
+}
diff --git a/tools/ahat/test/TestDump.java b/tools/ahat/test/TestDump.java
index 3dce2dccfe..db9b25646a 100644
--- a/tools/ahat/test/TestDump.java
+++ b/tools/ahat/test/TestDump.java
@@ -21,11 +21,14 @@ import com.android.ahat.heapdump.AhatInstance;
import com.android.ahat.heapdump.AhatSnapshot;
import com.android.ahat.heapdump.Diff;
import com.android.ahat.heapdump.FieldValue;
+import com.android.ahat.heapdump.Site;
import com.android.ahat.heapdump.Value;
import com.android.tools.perflib.heap.ProguardMap;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
/**
* The TestDump class is used to get an AhatSnapshot for the test-dump
@@ -45,8 +48,10 @@ public class TestDump {
// fails and don't try to load it again.
private static boolean mTestDumpFailed = false;
- private AhatSnapshot mSnapshot = null;
- private AhatSnapshot mBaseline = null;
+ private AhatSnapshot mSnapshot;
+ private AhatSnapshot mBaseline;
+ private AhatClassObj mMain;
+ private AhatClassObj mBaselineMain;
/**
* Load the test-dump.hprof and test-dump-base.hprof files.
@@ -79,6 +84,12 @@ public class TestDump {
mSnapshot = AhatSnapshot.fromHprof(new File(hprof), map);
mBaseline = AhatSnapshot.fromHprof(new File(hprofBase), map);
Diff.snapshots(mSnapshot, mBaseline);
+
+ mMain = findClass(mSnapshot, "Main");
+ assert(mMain != null);
+
+ mBaselineMain = findClass(mBaseline, "Main");
+ assert(mBaselineMain != null);
}
/**
@@ -100,7 +111,7 @@ public class TestDump {
* snapshot for the test-dump program.
*/
public Value getDumpedValue(String name) {
- return getDumpedValue(name, mSnapshot);
+ return getDumpedValue(name, mMain);
}
/**
@@ -108,15 +119,14 @@ public class TestDump {
* baseline snapshot for the test-dump program.
*/
public Value getBaselineDumpedValue(String name) {
- return getDumpedValue(name, mBaseline);
+ return getDumpedValue(name, mBaselineMain);
}
/**
- * Returns the value of a field in the DumpedStuff instance in the
- * given snapshot for the test-dump program.
+ * Returns the value of a field in the DumpedStuff instance given the Main
+ * class object for the snapshot.
*/
- private Value getDumpedValue(String name, AhatSnapshot snapshot) {
- AhatClassObj main = snapshot.findClass("Main");
+ private static Value getDumpedValue(String name, AhatClassObj main) {
AhatInstance stuff = null;
for (FieldValue field : main.getStaticFieldValues()) {
if ("stuff".equals(field.name)) {
@@ -127,6 +137,33 @@ public class TestDump {
}
/**
+ * Returns a class object in the given heap dump whose name matches the
+ * given name, or null if no such class object could be found.
+ */
+ private static AhatClassObj findClass(AhatSnapshot snapshot, String name) {
+ Site root = snapshot.getRootSite();
+ Collection<AhatInstance> classes = new ArrayList<AhatInstance>();
+ root.getObjects(null, "java.lang.Class", classes);
+ for (AhatInstance inst : classes) {
+ if (inst.isClassObj()) {
+ AhatClassObj cls = inst.asClassObj();
+ if (name.equals(cls.getName())) {
+ return cls;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a class object in the heap dump whose name matches the given
+ * name, or null if no such class object could be found.
+ */
+ public AhatClassObj findClass(String name) {
+ return findClass(mSnapshot, name);
+ }
+
+ /**
* Returns the value of a non-primitive field in the DumpedStuff instance in
* the snapshot for the test-dump program.
*/
diff --git a/tools/ahat/test/Tests.java b/tools/ahat/test/Tests.java
index a1e3246cd1..cd33a9059b 100644
--- a/tools/ahat/test/Tests.java
+++ b/tools/ahat/test/Tests.java
@@ -33,6 +33,7 @@ public class Tests {
"com.android.ahat.RootedHandlerTest",
"com.android.ahat.QueryTest",
"com.android.ahat.SiteHandlerTest",
+ "com.android.ahat.SiteTest",
};
}
JUnitCore.main(args);
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index 5806b6107f..d27b8fc06c 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -11,9 +11,11 @@
names: ["jsr166.CompletableFutureTest#testCompleteOnTimeout_completed",
"libcore.icu.TransliteratorTest#testAll",
"libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045",
+ "libcore.icu.RelativeDateTimeFormatterTest#test_bug25883157",
"libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndTimeout",
"libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndNoTimeout",
"libcore.java.util.TimeZoneTest#testSetDefaultDeadlock",
+ "libcore.javax.crypto.CipherBasicsTest#testBasicEncryption",
"org.apache.harmony.tests.java.util.TimerTest#testThrowingTaskKillsTimerThread"]
}
]