Limit summary string lengths to 200 characters.
Strings longer than 200 characters are now truncated and are shown
with a trailing "..." instead of an end quote. The number 200 was
chosen arbitrarily.
Bug: 23223379
Change-Id: I96d7c9d563026233ff5f4962245b4c276d776a58
diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt
index 5615f8f..d6f55aa 100644
--- a/tools/ahat/README.txt
+++ b/tools/ahat/README.txt
@@ -28,8 +28,6 @@
- Use consistent order for heap columns.
Sometimes I see "app" first, sometimes last (from one heap dump to
another) How about, always sort by name?
- * For long strings, limit the string length shown in the summary view to
- something reasonable. Say 50 chars, then add a "..." at the end.
* For HeapTable with single heap shown, the heap name isn't centered?
* Consistently document functions.
* Should help be part of an AhatHandler, that automatically gets the menu and
@@ -70,6 +68,7 @@
showing all the instances.
* That InstanceUtils.asString properly takes into account "offset" and
"count" fields, if they are present.
+ * InstanceUtils.getDexCacheLocation
Reported Issues:
* Request to be able to sort tables by size.
diff --git a/tools/ahat/src/InstanceUtils.java b/tools/ahat/src/InstanceUtils.java
index eb9e363..c2d75c4 100644
--- a/tools/ahat/src/InstanceUtils.java
+++ b/tools/ahat/src/InstanceUtils.java
@@ -60,9 +60,21 @@
}
- // Read the string value from an hprof Instance.
- // Returns null if the object can't be interpreted as a string.
+ /**
+ * Read the string value from an hprof Instance.
+ * Returns null if the object can't be interpreted as a string.
+ */
public static String asString(Instance inst) {
+ return asString(inst, -1);
+ }
+
+ /**
+ * Read the string value from an hprof Instance.
+ * Returns null if the object can't be interpreted as a string.
+ * The returned string is truncated to maxChars characters.
+ * If maxChars is negative, the returned string is not truncated.
+ */
+ public static String asString(Instance inst, int maxChars) {
if (!isInstanceOfClass(inst, "java.lang.String")) {
return null;
}
@@ -81,13 +93,15 @@
// array, we should use that here.
int numChars = chars.getValues().length;
int count = getIntField(inst, "count", numChars);
- int offset = getIntField(inst, "offset", 0);
- int end = offset + count - 1;
-
if (count == 0) {
return "";
}
+ if (0 <= maxChars && maxChars < count) {
+ count = maxChars;
+ }
+ int offset = getIntField(inst, "offset", 0);
+ int end = offset + count - 1;
if (offset >= 0 && offset < numChars && end >= 0 && end < numChars) {
return new String(chars.asCharArray(offset, count));
}
@@ -234,12 +248,14 @@
* Assuming inst represents a DexCache object, return the dex location for
* that dex cache. Returns null if the given instance doesn't represent a
* DexCache object or the location could not be found.
+ * If maxChars is non-negative, the returned location is truncated to
+ * maxChars in length.
*/
- public static String getDexCacheLocation(Instance inst) {
+ public static String getDexCacheLocation(Instance inst, int maxChars) {
if (isInstanceOfClass(inst, "java.lang.DexCache")) {
Instance location = getRefField(inst, "location");
if (location != null) {
- return asString(location);
+ return asString(location, maxChars);
}
}
return null;
diff --git a/tools/ahat/src/Value.java b/tools/ahat/src/Value.java
index 9b483fa..4eb27b1 100644
--- a/tools/ahat/src/Value.java
+++ b/tools/ahat/src/Value.java
@@ -25,6 +25,10 @@
*/
class Value {
+ // For string literals, we limit the number of characters we show to
+ // kMaxChars in case the string is really long.
+ private static int kMaxChars = 200;
+
/**
* Create a DocString representing a summary of the given instance.
*/
@@ -43,15 +47,19 @@
link.append(inst.toString());
// Annotate Strings with their values.
- String stringValue = InstanceUtils.asString(inst);
+ String stringValue = InstanceUtils.asString(inst, kMaxChars);
if (stringValue != null) {
- link.appendFormat("\"%s\"", stringValue);
+ link.appendFormat("\"%s", stringValue);
+ link.append(kMaxChars == stringValue.length() ? "..." : "\"");
}
// Annotate DexCache with its location.
- String dexCacheLocation = InstanceUtils.getDexCacheLocation(inst);
+ String dexCacheLocation = InstanceUtils.getDexCacheLocation(inst, kMaxChars);
if (dexCacheLocation != null) {
- link.append(" for " + dexCacheLocation);
+ link.appendFormat(" for %s", dexCacheLocation);
+ if (kMaxChars == dexCacheLocation.length()) {
+ link.append("...");
+ }
}
URI objTarget = DocString.formattedUri("object?id=%d", inst.getId());
diff --git a/tools/ahat/src/manifest.txt b/tools/ahat/src/manifest.txt
index 7efb1a7..421de17 100644
--- a/tools/ahat/src/manifest.txt
+++ b/tools/ahat/src/manifest.txt
@@ -1,4 +1,4 @@
Name: ahat/
Implementation-Title: ahat
-Implementation-Version: 0.2
+Implementation-Version: 0.3
Main-Class: com.android.ahat.Main
diff --git a/tools/ahat/test/InstanceUtilsTest.java b/tools/ahat/test/InstanceUtilsTest.java
index 7613df4..11f82a2 100644
--- a/tools/ahat/test/InstanceUtilsTest.java
+++ b/tools/ahat/test/InstanceUtilsTest.java
@@ -25,21 +25,49 @@
public class InstanceUtilsTest {
@Test
- public void basicString() throws IOException {
+ public void asStringBasic() throws IOException {
TestDump dump = TestDump.getTestDump();
Instance str = (Instance)dump.getDumpedThing("basicString");
assertEquals("hello, world", InstanceUtils.asString(str));
}
@Test
- public void nullString() throws IOException {
+ public void asStringTruncated() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance str = (Instance)dump.getDumpedThing("basicString");
+ assertEquals("hello", InstanceUtils.asString(str, 5));
+ }
+
+ @Test
+ public void asStringExactMax() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance str = (Instance)dump.getDumpedThing("basicString");
+ assertEquals("hello, world", InstanceUtils.asString(str, 12));
+ }
+
+ @Test
+ public void asStringNotTruncated() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance str = (Instance)dump.getDumpedThing("basicString");
+ assertEquals("hello, world", InstanceUtils.asString(str, 50));
+ }
+
+ @Test
+ public void asStringNegativeMax() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance str = (Instance)dump.getDumpedThing("basicString");
+ assertEquals("hello, world", InstanceUtils.asString(str, -3));
+ }
+
+ @Test
+ public void asStringNull() throws IOException {
TestDump dump = TestDump.getTestDump();
Instance obj = (Instance)dump.getDumpedThing("nullString");
assertNull(InstanceUtils.asString(obj));
}
@Test
- public void notString() throws IOException {
+ public void asStringNotString() throws IOException {
TestDump dump = TestDump.getTestDump();
Instance obj = (Instance)dump.getDumpedThing("anObject");
assertNotNull(obj);