summaryrefslogtreecommitdiff
path: root/tools/ahat/src/ObjectHandler.java
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ahat/src/ObjectHandler.java')
-rw-r--r--tools/ahat/src/ObjectHandler.java227
1 files changed, 135 insertions, 92 deletions
diff --git a/tools/ahat/src/ObjectHandler.java b/tools/ahat/src/ObjectHandler.java
index 78aac178e9..2e0ae6ed2d 100644
--- a/tools/ahat/src/ObjectHandler.java
+++ b/tools/ahat/src/ObjectHandler.java
@@ -16,22 +16,23 @@
package com.android.ahat;
-import com.android.tools.perflib.heap.ArrayInstance;
-import com.android.tools.perflib.heap.ClassInstance;
-import com.android.tools.perflib.heap.ClassObj;
-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.RootType;
+import com.android.ahat.heapdump.AhatArrayInstance;
+import com.android.ahat.heapdump.AhatClassInstance;
+import com.android.ahat.heapdump.AhatClassObj;
+import com.android.ahat.heapdump.AhatHeap;
+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.PathElement;
+import com.android.ahat.heapdump.Site;
+import com.android.ahat.heapdump.Value;
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;
+import java.util.Objects;
-import static com.android.ahat.InstanceUtils.PathElement;
class ObjectHandler implements AhatHandler {
@@ -53,35 +54,43 @@ class ObjectHandler implements AhatHandler {
@Override
public void handle(Doc doc, Query query) throws IOException {
long id = query.getLong("id", 0);
- Instance inst = mSnapshot.findInstance(id);
+ AhatInstance inst = mSnapshot.findInstance(id);
if (inst == null) {
doc.println(DocString.format("No object with id %08xl", id));
return;
}
+ AhatInstance base = inst.getBaseline();
- doc.title("Object %08x", inst.getUniqueId());
- doc.big(Value.render(mSnapshot, inst));
+ doc.title("Object %08x", inst.getId());
+ doc.big(Summarizer.summarize(inst));
printAllocationSite(doc, query, inst);
printGcRootPath(doc, query, inst);
doc.section("Object Info");
- ClassObj cls = inst.getClassObj();
+ AhatClassObj cls = inst.getClassObj();
doc.descriptions();
- 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("Class"), Summarizer.summarize(cls));
+
+ DocString sizeDescription = DocString.format("%,14d ", inst.getSize());
+ sizeDescription.appendDelta(false, base.isPlaceHolder(),
+ inst.getSize(), base.getSize());
+ doc.description(DocString.text("Size"), sizeDescription);
+
+ DocString rsizeDescription = DocString.format("%,14d ", inst.getTotalRetainedSize());
+ rsizeDescription.appendDelta(false, base.isPlaceHolder(),
+ inst.getTotalRetainedSize(), base.getTotalRetainedSize());
+ doc.description(DocString.text("Retained Size"), rsizeDescription);
+
doc.description(DocString.text("Heap"), DocString.text(inst.getHeap().getName()));
- Collection<RootType> rootTypes = mSnapshot.getRootTypes(inst);
+ Collection<String> rootTypes = inst.getRootTypes();
if (rootTypes != null) {
DocString types = new DocString();
String comma = "";
- for (RootType type : rootTypes) {
+ for (String type : rootTypes) {
types.append(comma);
- types.append(type.getName());
+ types.append(type);
comma = ", ";
}
doc.description(DocString.text("Root Types"), types);
@@ -90,112 +99,146 @@ class ObjectHandler implements AhatHandler {
doc.end();
printBitmap(doc, inst);
- if (inst instanceof ClassInstance) {
- printClassInstanceFields(doc, query, mSnapshot, (ClassInstance)inst);
- } else if (inst instanceof ArrayInstance) {
- printArrayElements(doc, query, mSnapshot, (ArrayInstance)inst);
- } else if (inst instanceof ClassObj) {
- printClassInfo(doc, query, mSnapshot, (ClassObj)inst);
+ if (inst.isClassInstance()) {
+ printClassInstanceFields(doc, query, inst.asClassInstance());
+ } else if (inst.isArrayInstance()) {
+ printArrayElements(doc, query, inst.asArrayInstance());
+ } else if (inst.isClassObj()) {
+ printClassInfo(doc, query, inst.asClassObj());
}
- printReferences(doc, query, mSnapshot, inst);
+ printReferences(doc, query, inst);
printDominatedObjects(doc, query, inst);
}
- private static void printClassInstanceFields(
- Doc doc, Query query, AhatSnapshot snapshot, ClassInstance inst) {
+ private static void printClassInstanceFields(Doc doc, Query query, AhatClassInstance inst) {
doc.section("Fields");
- doc.table(new Column("Type"), new Column("Name"), new Column("Value"));
- SubsetSelector<ClassInstance.FieldValue> selector
- = new SubsetSelector(query, INSTANCE_FIELDS_ID, inst.getValues());
- for (ClassInstance.FieldValue field : selector.selected()) {
- doc.row(
- DocString.text(field.getField().getType().toString()),
- DocString.text(field.getField().getName()),
- Value.render(snapshot, field.getValue()));
+ AhatInstance base = inst.getBaseline();
+ List<FieldValue> fields = inst.getInstanceFields();
+ if (!base.isPlaceHolder()) {
+ Diff.fields(fields, base.asClassInstance().getInstanceFields());
}
- doc.end();
+ SubsetSelector<FieldValue> selector = new SubsetSelector(query, INSTANCE_FIELDS_ID, fields);
+ printFields(doc, inst != base && !base.isPlaceHolder(), selector.selected());
selector.render(doc);
}
- private static void printArrayElements(
- Doc doc, Query query, AhatSnapshot snapshot, ArrayInstance array) {
+ private static void printArrayElements(Doc doc, Query query, AhatArrayInstance 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);
+ AhatInstance base = array.getBaseline();
+ boolean diff = array.getBaseline() != array && !base.isPlaceHolder();
+ doc.table(
+ new Column("Index", Column.Align.RIGHT),
+ new Column("Value"),
+ new Column("Δ", Column.Align.LEFT, diff));
+
+ List<Value> elements = array.getValues();
+ SubsetSelector<Value> selector = new SubsetSelector(query, ARRAY_ELEMENTS_ID, elements);
int i = 0;
- for (Object elem : selector.selected()) {
- doc.row(DocString.format("%d", i), Value.render(snapshot, elem));
+ for (Value current : selector.selected()) {
+ DocString delta = new DocString();
+ if (diff) {
+ Value previous = Value.getBaseline(base.asArrayInstance().getValue(i));
+ if (!Objects.equals(current, previous)) {
+ delta.append("was ");
+ delta.append(Summarizer.summarize(previous));
+ }
+ }
+ doc.row(DocString.format("%d", i), Summarizer.summarize(current), delta);
i++;
}
doc.end();
selector.render(doc);
}
- private static void printClassInfo(
- Doc doc, Query query, AhatSnapshot snapshot, ClassObj clsobj) {
+ private static void printFields(Doc doc, boolean diff, List<FieldValue> fields) {
+ doc.table(
+ new Column("Type"),
+ new Column("Name"),
+ new Column("Value"),
+ new Column("Δ", Column.Align.LEFT, diff));
+
+ for (FieldValue field : fields) {
+ Value current = field.getValue();
+ DocString value;
+ if (field.isPlaceHolder()) {
+ value = DocString.removed("del");
+ } else {
+ value = Summarizer.summarize(current);
+ }
+
+ DocString delta = new DocString();
+ FieldValue basefield = field.getBaseline();
+ if (basefield.isPlaceHolder()) {
+ delta.append(DocString.added("new"));
+ } else {
+ Value previous = Value.getBaseline(basefield.getValue());
+ if (!Objects.equals(current, previous)) {
+ delta.append("was ");
+ delta.append(Summarizer.summarize(previous));
+ }
+ }
+ doc.row(DocString.text(field.getType()), DocString.text(field.getName()), value, delta);
+ }
+ doc.end();
+ }
+
+ private static void printClassInfo(Doc doc, Query query, AhatClassObj clsobj) {
doc.section("Class Info");
doc.descriptions();
doc.description(DocString.text("Super Class"),
- Value.render(snapshot, clsobj.getSuperClassObj()));
+ Summarizer.summarize(clsobj.getSuperClassObj()));
doc.description(DocString.text("Class Loader"),
- Value.render(snapshot, clsobj.getClassLoader()));
+ Summarizer.summarize(clsobj.getClassLoader()));
doc.end();
doc.section("Static Fields");
- doc.table(new Column("Type"), new Column("Name"), new Column("Value"));
- List<Map.Entry<Field, Object>> fields
- = new ArrayList<Map.Entry<Field, Object>>(clsobj.getStaticFieldValues().entrySet());
- SubsetSelector<Map.Entry<Field, Object>> selector
- = new SubsetSelector(query, STATIC_FIELDS_ID, fields);
- for (Map.Entry<Field, Object> field : selector.selected()) {
- doc.row(
- DocString.text(field.getKey().getType().toString()),
- DocString.text(field.getKey().getName()),
- Value.render(snapshot, field.getValue()));
+ AhatInstance base = clsobj.getBaseline();
+ List<FieldValue> fields = clsobj.getStaticFieldValues();
+ if (!base.isPlaceHolder()) {
+ Diff.fields(fields, base.asClassObj().getStaticFieldValues());
}
- doc.end();
+ SubsetSelector<FieldValue> selector = new SubsetSelector(query, STATIC_FIELDS_ID, fields);
+ printFields(doc, clsobj != base && !base.isPlaceHolder(), selector.selected());
selector.render(doc);
}
- private static void printReferences(
- Doc doc, Query query, AhatSnapshot snapshot, Instance inst) {
+ private static void printReferences(Doc doc, Query query, AhatInstance inst) {
doc.section("Objects with References to this Object");
if (inst.getHardReverseReferences().isEmpty()) {
doc.println(DocString.text("(none)"));
} else {
doc.table(new Column("Object"));
- List<Instance> references = inst.getHardReverseReferences();
- SubsetSelector<Instance> selector = new SubsetSelector(query, HARD_REFS_ID, references);
- for (Instance ref : selector.selected()) {
- doc.row(Value.render(snapshot, ref));
+ List<AhatInstance> references = inst.getHardReverseReferences();
+ SubsetSelector<AhatInstance> selector = new SubsetSelector(query, HARD_REFS_ID, references);
+ for (AhatInstance ref : selector.selected()) {
+ doc.row(Summarizer.summarize(ref));
}
doc.end();
selector.render(doc);
}
- if (inst.getSoftReverseReferences() != null) {
+ if (!inst.getSoftReverseReferences().isEmpty()) {
doc.section("Objects with Soft References to this Object");
doc.table(new Column("Object"));
- List<Instance> references = inst.getSoftReverseReferences();
- SubsetSelector<Instance> selector = new SubsetSelector(query, SOFT_REFS_ID, references);
- for (Instance ref : selector.selected()) {
- doc.row(Value.render(snapshot, ref));
+ List<AhatInstance> references = inst.getSoftReverseReferences();
+ SubsetSelector<AhatInstance> selector = new SubsetSelector(query, SOFT_REFS_ID, references);
+ for (AhatInstance ref : selector.selected()) {
+ doc.row(Summarizer.summarize(ref));
}
doc.end();
selector.render(doc);
}
}
- private void printAllocationSite(Doc doc, Query query, Instance inst) {
+ private void printAllocationSite(Doc doc, Query query, AhatInstance inst) {
doc.section("Allocation Site");
- Site site = mSnapshot.getSiteForInstance(inst);
+ Site site = inst.getSite();
SitePrinter.printSite(mSnapshot, doc, query, ALLOCATION_SITE_ID, site);
}
// Draw the bitmap corresponding to this instance if there is one.
- private static void printBitmap(Doc doc, Instance inst) {
- Instance bitmap = InstanceUtils.getAssociatedBitmapInstance(inst);
+ private static void printBitmap(Doc doc, AhatInstance inst) {
+ AhatInstance bitmap = inst.getAssociatedBitmapInstance();
if (bitmap != null) {
doc.section("Bitmap Image");
doc.println(DocString.image(
@@ -203,25 +246,25 @@ class ObjectHandler implements AhatHandler {
}
}
- private void printGcRootPath(Doc doc, Query query, Instance inst) {
+ private void printGcRootPath(Doc doc, Query query, AhatInstance inst) {
doc.section("Sample Path from GC Root");
- List<PathElement> path = InstanceUtils.getPathFromGcRoot(inst);
+ List<PathElement> path = inst.getPathFromGcRoot();
- // Add 'null' as a marker for the root.
- path.add(0, null);
+ // Add a dummy PathElement as a marker for the root.
+ final PathElement root = new PathElement(null, null);
+ path.add(0, root);
HeapTable.TableConfig<PathElement> table = new HeapTable.TableConfig<PathElement>() {
public String getHeapsDescription() {
return "Bytes Retained by Heap (Dominators Only)";
}
- public long getSize(PathElement element, Heap heap) {
- if (element == null) {
- return mSnapshot.getHeapSize(heap);
+ public long getSize(PathElement element, AhatHeap heap) {
+ if (element == root) {
+ return heap.getSize();
}
if (element.isDominator) {
- int index = mSnapshot.getHeapIndex(heap);
- return element.instance.getRetainedSize(index);
+ return element.instance.getRetainedSize(heap);
}
return 0;
}
@@ -233,11 +276,11 @@ class ObjectHandler implements AhatHandler {
}
public DocString render(PathElement element) {
- if (element == null) {
+ if (element == root) {
return DocString.link(DocString.uri("rooted"), DocString.text("ROOT"));
} else {
- DocString label = DocString.text(" → ");
- label.append(Value.render(mSnapshot, element.instance));
+ DocString label = DocString.text("→ ");
+ label.append(Summarizer.summarize(element.instance));
label.append(element.field);
return label;
}
@@ -249,9 +292,9 @@ class ObjectHandler implements AhatHandler {
HeapTable.render(doc, query, DOMINATOR_PATH_ID, table, mSnapshot, path);
}
- public void printDominatedObjects(Doc doc, Query query, Instance inst) {
+ public void printDominatedObjects(Doc doc, Query query, AhatInstance inst) {
doc.section("Immediately Dominated Objects");
- List<Instance> instances = mSnapshot.getDominated(inst);
+ List<AhatInstance> instances = inst.getDominated();
if (instances != null) {
DominatedList.render(mSnapshot, doc, query, DOMINATED_OBJECTS_ID, instances);
} else {