summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Richard Uhler <ruhler@google.com> 2015-11-11 08:31:52 -0800
committer Richard Uhler <ruhler@google.com> 2015-11-17 11:13:57 -0800
commit38f9eba1ad2094205410b47cd72ae3e4152c9432 (patch)
tree931a6ece1ae91b397db35dff21f85e62a8645f20
parenta04f57badca0a9211d45eb7bde44c1d1e8f159ff (diff)
Annotate root objects and show their types.
The summaries for root objects are now prefixed with "(root)" to show they are roots, and the object's root types are listed in the object view. Bug: 23784153 Change-Id: Ib52ccb59cf0e3f28ac754c6caa564ebef34866ea
-rw-r--r--tools/ahat/README.txt2
-rw-r--r--tools/ahat/src/AhatSnapshot.java36
-rw-r--r--tools/ahat/src/DominatedList.java2
-rw-r--r--tools/ahat/src/ObjectHandler.java57
-rw-r--r--tools/ahat/src/ObjectsHandler.java2
-rw-r--r--tools/ahat/src/SiteHandler.java2
-rw-r--r--tools/ahat/src/Value.java24
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);
}