diff options
| -rw-r--r-- | tools/ahat/README.txt | 2 | ||||
| -rw-r--r-- | tools/ahat/src/AhatSnapshot.java | 36 | ||||
| -rw-r--r-- | tools/ahat/src/DominatedList.java | 2 | ||||
| -rw-r--r-- | tools/ahat/src/ObjectHandler.java | 57 | ||||
| -rw-r--r-- | tools/ahat/src/ObjectsHandler.java | 2 | ||||
| -rw-r--r-- | tools/ahat/src/SiteHandler.java | 2 | ||||
| -rw-r--r-- | tools/ahat/src/Value.java | 24 |
7 files changed, 95 insertions, 30 deletions
diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt index 362ae2536f..adc4d03a7a 100644 --- a/tools/ahat/README.txt +++ b/tools/ahat/README.txt @@ -19,7 +19,6 @@ TODO: * Show site context and heap and class filter in "Objects" view? * Have a menu at the top of an object view with links to the sections? * Include ahat version and hprof file in the menu at the top of the page? - * Show root types. * Heaped Table - Make sortable by clicking on headers. * For HeapTable with single heap shown, the heap name isn't centered? @@ -77,6 +76,7 @@ Things to move to perflib: * Extracting bitmap data from bitmap instances. * Adding up allocations by stack frame. * Computing, for each instance, the other instances it dominates. + * Instance.isRoot and Instance.getRootTypes. Release History: 0.2 Oct 20, 2015 diff --git a/tools/ahat/src/AhatSnapshot.java b/tools/ahat/src/AhatSnapshot.java index 0bf064eb24..fc7911b71b 100644 --- a/tools/ahat/src/AhatSnapshot.java +++ b/tools/ahat/src/AhatSnapshot.java @@ -19,6 +19,8 @@ package com.android.ahat; import com.android.tools.perflib.heap.ClassObj; import com.android.tools.perflib.heap.Heap; import com.android.tools.perflib.heap.Instance; +import com.android.tools.perflib.heap.RootObj; +import com.android.tools.perflib.heap.RootType; import com.android.tools.perflib.heap.Snapshot; import com.android.tools.perflib.heap.StackFrame; import com.android.tools.perflib.heap.StackTrace; @@ -29,8 +31,10 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; @@ -48,6 +52,11 @@ class AhatSnapshot { // Collection of objects whose immediate dominator is the SENTINEL_ROOT. private List<Instance> mRooted; + // Map from roots to their types. + // Instances are only included if they are roots, and the collection of root + // types is guaranteed to be non-empty. + private Map<Instance, Collection<RootType>> mRoots; + private Site mRootSite; private Map<Heap, Long> mHeapSizes; @@ -113,6 +122,18 @@ class AhatSnapshot { } mHeapSizes.put(heap, total); } + + // Record the roots and their types. + mRoots = new HashMap<Instance, Collection<RootType>>(); + for (RootObj root : snapshot.getGCRoots()) { + Instance inst = root.getReferredInstance(); + Collection<RootType> types = mRoots.get(inst); + if (types == null) { + types = new HashSet<RootType>(); + mRoots.put(inst, types); + } + types.add(root.getRootType()); + } } // Note: This method is exposed for testing purposes. @@ -140,6 +161,21 @@ class AhatSnapshot { return mRooted; } + /** + * Returns true if the given instance is a root. + */ + public boolean isRoot(Instance inst) { + return mRoots.containsKey(inst); + } + + /** + * Returns the list of root types for the given instance, or null if the + * instance is not a root. + */ + public Collection<RootType> getRootTypes(Instance inst) { + return mRoots.get(inst); + } + public List<Heap> getHeaps() { return mHeaps; } diff --git a/tools/ahat/src/DominatedList.java b/tools/ahat/src/DominatedList.java index 34a5665b3e..7a673f556e 100644 --- a/tools/ahat/src/DominatedList.java +++ b/tools/ahat/src/DominatedList.java @@ -71,7 +71,7 @@ class DominatedList { } public DocString render(Instance element) { - return Value.render(element); + return Value.render(mSnapshot, element); } }; return Collections.singletonList(value); diff --git a/tools/ahat/src/ObjectHandler.java b/tools/ahat/src/ObjectHandler.java index 1305070b0f..06023dab7f 100644 --- a/tools/ahat/src/ObjectHandler.java +++ b/tools/ahat/src/ObjectHandler.java @@ -23,9 +23,11 @@ import com.android.tools.perflib.heap.Field; import com.android.tools.perflib.heap.Heap; import com.android.tools.perflib.heap.Instance; import com.android.tools.perflib.heap.RootObj; +import com.android.tools.perflib.heap.RootType; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -57,7 +59,7 @@ class ObjectHandler implements AhatHandler { } doc.title("Object %08x", inst.getUniqueId()); - doc.big(Value.render(inst)); + doc.big(Value.render(mSnapshot, inst)); printAllocationSite(doc, query, inst); printDominatorPath(doc, query, inst); @@ -65,27 +67,41 @@ class ObjectHandler implements AhatHandler { doc.section("Object Info"); ClassObj cls = inst.getClassObj(); doc.descriptions(); - doc.description(DocString.text("Class"), Value.render(cls)); + doc.description(DocString.text("Class"), Value.render(mSnapshot, cls)); doc.description(DocString.text("Size"), DocString.format("%d", inst.getSize())); doc.description( DocString.text("Retained Size"), DocString.format("%d", inst.getTotalRetainedSize())); doc.description(DocString.text("Heap"), DocString.text(inst.getHeap().getName())); + + Collection<RootType> rootTypes = mSnapshot.getRootTypes(inst); + if (rootTypes != null) { + DocString types = new DocString(); + String comma = ""; + for (RootType type : rootTypes) { + types.append(comma); + types.append(type.getName()); + comma = ", "; + } + doc.description(DocString.text("Root Types"), types); + } + doc.end(); printBitmap(doc, inst); if (inst instanceof ClassInstance) { - printClassInstanceFields(doc, query, (ClassInstance)inst); + printClassInstanceFields(doc, query, mSnapshot, (ClassInstance)inst); } else if (inst instanceof ArrayInstance) { - printArrayElements(doc, query, (ArrayInstance)inst); + printArrayElements(doc, query, mSnapshot, (ArrayInstance)inst); } else if (inst instanceof ClassObj) { - printClassInfo(doc, query, (ClassObj)inst); + printClassInfo(doc, query, mSnapshot, (ClassObj)inst); } - printReferences(doc, query, inst); + printReferences(doc, query, mSnapshot, inst); printDominatedObjects(doc, query, inst); } - private static void printClassInstanceFields(Doc doc, Query query, ClassInstance inst) { + private static void printClassInstanceFields( + Doc doc, Query query, AhatSnapshot snapshot, ClassInstance inst) { doc.section("Fields"); doc.table(new Column("Type"), new Column("Name"), new Column("Value")); SubsetSelector<ClassInstance.FieldValue> selector @@ -94,31 +110,35 @@ class ObjectHandler implements AhatHandler { doc.row( DocString.text(field.getField().getType().toString()), DocString.text(field.getField().getName()), - Value.render(field.getValue())); + Value.render(snapshot, field.getValue())); } doc.end(); selector.render(doc); } - private static void printArrayElements(Doc doc, Query query, ArrayInstance array) { + private static void printArrayElements( + Doc doc, Query query, AhatSnapshot snapshot, ArrayInstance array) { doc.section("Array Elements"); doc.table(new Column("Index", Column.Align.RIGHT), new Column("Value")); List<Object> elements = Arrays.asList(array.getValues()); SubsetSelector<Object> selector = new SubsetSelector(query, ARRAY_ELEMENTS_ID, elements); int i = 0; for (Object elem : selector.selected()) { - doc.row(DocString.format("%d", i), Value.render(elem)); + doc.row(DocString.format("%d", i), Value.render(snapshot, elem)); i++; } doc.end(); selector.render(doc); } - private static void printClassInfo(Doc doc, Query query, ClassObj clsobj) { + private static void printClassInfo( + Doc doc, Query query, AhatSnapshot snapshot, ClassObj clsobj) { doc.section("Class Info"); doc.descriptions(); - doc.description(DocString.text("Super Class"), Value.render(clsobj.getSuperClassObj())); - doc.description(DocString.text("Class Loader"), Value.render(clsobj.getClassLoader())); + doc.description(DocString.text("Super Class"), + Value.render(snapshot, clsobj.getSuperClassObj())); + doc.description(DocString.text("Class Loader"), + Value.render(snapshot, clsobj.getClassLoader())); doc.end(); doc.section("Static Fields"); @@ -131,13 +151,14 @@ class ObjectHandler implements AhatHandler { doc.row( DocString.text(field.getKey().getType().toString()), DocString.text(field.getKey().getName()), - Value.render(field.getValue())); + Value.render(snapshot, field.getValue())); } doc.end(); selector.render(doc); } - private static void printReferences(Doc doc, Query query, Instance inst) { + private static void printReferences( + Doc doc, Query query, AhatSnapshot snapshot, Instance inst) { doc.section("Objects with References to this Object"); if (inst.getHardReferences().isEmpty()) { doc.println(DocString.text("(none)")); @@ -146,7 +167,7 @@ class ObjectHandler implements AhatHandler { List<Instance> references = inst.getHardReferences(); SubsetSelector<Instance> selector = new SubsetSelector(query, HARD_REFS_ID, references); for (Instance ref : selector.selected()) { - doc.row(Value.render(ref)); + doc.row(Value.render(snapshot, ref)); } doc.end(); selector.render(doc); @@ -158,7 +179,7 @@ class ObjectHandler implements AhatHandler { List<Instance> references = inst.getSoftReferences(); SubsetSelector<Instance> selector = new SubsetSelector(query, SOFT_REFS_ID, references); for (Instance ref : selector.selected()) { - doc.row(Value.render(ref)); + doc.row(Value.render(snapshot, ref)); } doc.end(); selector.render(doc); @@ -217,7 +238,7 @@ class ObjectHandler implements AhatHandler { if (element == null) { return DocString.link(DocString.uri("rooted"), DocString.text("ROOT")); } else { - return DocString.text("→ ").append(Value.render(element)); + return DocString.text("→ ").append(Value.render(mSnapshot, element)); } } }; diff --git a/tools/ahat/src/ObjectsHandler.java b/tools/ahat/src/ObjectsHandler.java index 8ad3f481da..4cfb0a55cf 100644 --- a/tools/ahat/src/ObjectsHandler.java +++ b/tools/ahat/src/ObjectsHandler.java @@ -60,7 +60,7 @@ class ObjectsHandler implements AhatHandler { doc.row( DocString.format("%,d", inst.getSize()), DocString.text(inst.getHeap().getName()), - Value.render(inst)); + Value.render(mSnapshot, inst)); } doc.end(); selector.render(doc); diff --git a/tools/ahat/src/SiteHandler.java b/tools/ahat/src/SiteHandler.java index 0425a5a825..839e220ca4 100644 --- a/tools/ahat/src/SiteHandler.java +++ b/tools/ahat/src/SiteHandler.java @@ -101,7 +101,7 @@ class SiteHandler implements AhatHandler { site.getStackId(), site.getStackDepth(), info.heap.getName(), className), DocString.format("%,14d", info.numInstances)), DocString.text(info.heap.getName()), - Value.render(info.classObj)); + Value.render(mSnapshot, info.classObj)); } doc.end(); selector.render(doc); diff --git a/tools/ahat/src/Value.java b/tools/ahat/src/Value.java index 7c969b3645..847692bd10 100644 --- a/tools/ahat/src/Value.java +++ b/tools/ahat/src/Value.java @@ -32,21 +32,29 @@ class Value { /** * Create a DocString representing a summary of the given instance. */ - private static DocString renderInstance(Instance inst) { - DocString link = new DocString(); + private static DocString renderInstance(AhatSnapshot snapshot, Instance inst) { + DocString formatted = new DocString(); if (inst == null) { - link.append("(null)"); - return link; + formatted.append("(null)"); + return formatted; + } + + // Annotate roots as roots. + if (snapshot.isRoot(inst)) { + formatted.append("(root) "); } + // Annotate classes as classes. + DocString link = new DocString(); if (inst instanceof ClassObj) { link.append("class "); } link.append(inst.toString()); + URI objTarget = DocString.formattedUri("object?id=%d", inst.getId()); - DocString formatted = DocString.link(objTarget, link); + formatted.appendLink(objTarget, link); // Annotate Strings with their values. String stringValue = InstanceUtils.asString(inst, kMaxChars); @@ -63,7 +71,7 @@ class Value { // It should not be possible for a referent to refer back to the // reference object, even indirectly, so there shouldn't be any issues // with infinite recursion here. - formatted.append(renderInstance(referent)); + formatted.append(renderInstance(snapshot, referent)); } // Annotate DexCache with its location. @@ -89,9 +97,9 @@ class Value { /** * Create a DocString summarizing the given value. */ - public static DocString render(Object val) { + public static DocString render(AhatSnapshot snapshot, Object val) { if (val instanceof Instance) { - return renderInstance((Instance)val); + return renderInstance(snapshot, (Instance)val); } else { return DocString.format("%s", val); } |