Annotate $classOverhead arrays.
ART adds a fake byte[] $classOverhead static field to classes to show
the overheads associated with the class. This change causes ahat to
annotate those $classOverhead arrays with the class that they are
overheads for.
Test: m ahat-test, with new test added.
Test: Open an Android heap dump and visually inspect:
http://localhost:7100/objects?id=0&heap=app&class=byte[]
Bug: 64832332
Change-Id: Ief6ed7ce6c8c1196bc644df36f03c8e5158bf658
diff --git a/tools/ahat/etc/ahat_api.txt b/tools/ahat/etc/ahat_api.txt
index 6fc62e7..84266d9 100644
--- a/tools/ahat/etc/ahat_api.txt
+++ b/tools/ahat/etc/ahat_api.txt
@@ -58,6 +58,7 @@
method public java.lang.String asString(int);
method public java.lang.String asString();
method public com.android.ahat.heapdump.AhatInstance getAssociatedBitmapInstance();
+ method public com.android.ahat.heapdump.AhatClassObj getAssociatedClassForOverhead();
method public com.android.ahat.heapdump.AhatInstance getBaseline();
method public java.lang.String getClassName();
method public com.android.ahat.heapdump.AhatClassObj getClassObj();
diff --git a/tools/ahat/src/main/com/android/ahat/Summarizer.java b/tools/ahat/src/main/com/android/ahat/Summarizer.java
index ae0776a..127ff37 100644
--- a/tools/ahat/src/main/com/android/ahat/Summarizer.java
+++ b/tools/ahat/src/main/com/android/ahat/Summarizer.java
@@ -16,6 +16,7 @@
package com.android.ahat;
+import com.android.ahat.heapdump.AhatClassObj;
import com.android.ahat.heapdump.AhatInstance;
import com.android.ahat.heapdump.Site;
import com.android.ahat.heapdump.Value;
@@ -100,11 +101,17 @@
// Annotate bitmaps with a thumbnail.
AhatInstance bitmap = inst.getAssociatedBitmapInstance();
- String thumbnail = "";
if (bitmap != null) {
URI uri = DocString.formattedUri("bitmap?id=0x%x", bitmap.getId());
formatted.appendThumbnail(uri, "bitmap image");
}
+
+ // Annotate $classOverhead arrays
+ AhatClassObj cls = inst.getAssociatedClassForOverhead();
+ if (cls != null) {
+ formatted.append(" overhead for ");
+ formatted.append(summarize(cls));
+ }
return formatted;
}
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java
index 9c80802..c574e98 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java
@@ -333,6 +333,26 @@
return null;
}
+ @Override public AhatClassObj getAssociatedClassForOverhead() {
+ if (mByteArray != null) {
+ List<AhatInstance> refs = getHardReverseReferences();
+ if (refs.size() == 1) {
+ AhatClassObj ref = refs.get(0).asClassObj();
+ if (ref != null) {
+ for (FieldValue field : ref.getStaticFieldValues()) {
+ if (field.name.equals("$classOverhead")) {
+ if (field.value.asAhatInstance() == this) {
+ return ref;
+ }
+ return null;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
@Override public String toString() {
String className = getClassName();
if (className.endsWith("[]")) {
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java
index 95553a2..a91da82 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java
@@ -453,6 +453,18 @@
}
/**
+ * Returns the class object that this object represents the overhead for.
+ * ART adds a fake byte[] $classOverhead static field to classes to show the
+ * overheads associated with the class. If this is one such byte[] instance,
+ * returns the class it is associated with. Otherwise null is returned.
+ *
+ * @return the class instance that this is the overhead for
+ */
+ public AhatClassObj getAssociatedClassForOverhead() {
+ return null;
+ }
+
+ /**
* Returns the (bounded-length) string associated with this instance.
* Applies to instances of java.lang.String, char[], and in some cases
* byte[]. Returns null if this object cannot be interpreted as a string.
diff --git a/tools/ahat/src/test/com/android/ahat/InstanceTest.java b/tools/ahat/src/test/com/android/ahat/InstanceTest.java
index 8fbb884..65a3fb8 100644
--- a/tools/ahat/src/test/com/android/ahat/InstanceTest.java
+++ b/tools/ahat/src/test/com/android/ahat/InstanceTest.java
@@ -481,4 +481,19 @@
assertEquals("java.lang.String", str.getClassName());
assertNull(str.asString());
}
+
+ @Test
+ public void classOverhead() throws IOException {
+ TestDump dump = TestDump.getTestDump("O.hprof", null, null);
+ AhatSnapshot snapshot = dump.getAhatSnapshot();
+
+ // class libore.io.IoTracker has byte[124]@12c028d1 as its class overhead.
+ AhatInstance overhead = snapshot.findInstance(0x12c028d1);
+ AhatClassObj cls = overhead.getAssociatedClassForOverhead();
+ assertEquals(0x12c028d0, cls.getId());
+ assertEquals("libcore.io.IoTracker", cls.getName());
+
+ // Other kinds of objects should not have associated classes for overhead.
+ assertNull(cls.getAssociatedClassForOverhead());
+ }
}