ahat: Show GC Root Paths.
The Dominator Path in the objects view is replaced by an augmented
Sample Path from GC Root, which includes non-dominator objects
along a sample path and field names.
Also, use blanks instead of "0" in heap tables when the size is 0.
This cleans up the pages a little, and conveniently lets us
distinguish between dominator and non-dominator objects in the Sample
Path from GC Root.
Test: m ahat-test, with new InstanceUtils.gcRootPath test added.
Bug: 27299030
Change-Id: I53d75f9dcb3157c2b5b3afc74958711536cd67b6
diff --git a/tools/ahat/src/ObjectHandler.java b/tools/ahat/src/ObjectHandler.java
index 4df1be5..78aac17 100644
--- a/tools/ahat/src/ObjectHandler.java
+++ b/tools/ahat/src/ObjectHandler.java
@@ -22,7 +22,6 @@
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;
@@ -32,6 +31,8 @@
import java.util.List;
import java.util.Map;
+import static com.android.ahat.InstanceUtils.PathElement;
+
class ObjectHandler implements AhatHandler {
private static final String ARRAY_ELEMENTS_ID = "elements";
@@ -62,7 +63,7 @@
doc.big(Value.render(mSnapshot, inst));
printAllocationSite(doc, query, inst);
- printDominatorPath(doc, query, inst);
+ printGcRootPath(doc, query, inst);
doc.section("Object Info");
ClassObj cls = inst.getClassObj();
@@ -202,43 +203,43 @@
}
}
- private void printDominatorPath(Doc doc, Query query, Instance inst) {
- doc.section("Dominator Path from Root");
- List<Instance> path = new ArrayList<Instance>();
- for (Instance parent = inst;
- parent != null && !(parent instanceof RootObj);
- parent = parent.getImmediateDominator()) {
- path.add(parent);
- }
+ private void printGcRootPath(Doc doc, Query query, Instance inst) {
+ doc.section("Sample Path from GC Root");
+ List<PathElement> path = InstanceUtils.getPathFromGcRoot(inst);
// Add 'null' as a marker for the root.
- path.add(null);
- Collections.reverse(path);
+ path.add(0, null);
- HeapTable.TableConfig<Instance> table = new HeapTable.TableConfig<Instance>() {
+ HeapTable.TableConfig<PathElement> table = new HeapTable.TableConfig<PathElement>() {
public String getHeapsDescription() {
- return "Bytes Retained by Heap";
+ return "Bytes Retained by Heap (Dominators Only)";
}
- public long getSize(Instance element, Heap heap) {
+ public long getSize(PathElement element, Heap heap) {
if (element == null) {
return mSnapshot.getHeapSize(heap);
}
- int index = mSnapshot.getHeapIndex(heap);
- return element.getRetainedSize(index);
+ if (element.isDominator) {
+ int index = mSnapshot.getHeapIndex(heap);
+ return element.instance.getRetainedSize(index);
+ }
+ return 0;
}
- public List<HeapTable.ValueConfig<Instance>> getValueConfigs() {
- HeapTable.ValueConfig<Instance> value = new HeapTable.ValueConfig<Instance>() {
+ public List<HeapTable.ValueConfig<PathElement>> getValueConfigs() {
+ HeapTable.ValueConfig<PathElement> value = new HeapTable.ValueConfig<PathElement>() {
public String getDescription() {
- return "Object";
+ return "Path Element";
}
- public DocString render(Instance element) {
+ public DocString render(PathElement element) {
if (element == null) {
return DocString.link(DocString.uri("rooted"), DocString.text("ROOT"));
} else {
- return DocString.text("→ ").append(Value.render(mSnapshot, element));
+ DocString label = DocString.text(" → ");
+ label.append(Value.render(mSnapshot, element.instance));
+ label.append(element.field);
+ return label;
}
}
};