diff options
Diffstat (limited to 'tools')
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"] } ] |