Pay attention to string offset and count fields.

Strings can be represented as an offset and count inside a larger
character array. Pay attention to the offset and count fields when
getting the string value instead of returning the entire underlying
character array.

Bug: 23223379
Change-Id: Idc95870e33ede90b23deed9489c8c4796107f585
diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt
index a8e3884..1083c2f 100644
--- a/tools/ahat/README.txt
+++ b/tools/ahat/README.txt
@@ -31,9 +31,6 @@
       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 string summaries, if the string is an offset into a bigger byte array,
-   make sure to show just the part that's in the bigger byte array, not the
-   entire byte array.
  * 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
@@ -72,6 +69,8 @@
    time.
  * That we don't show the 'extra' column in the DominatedList if we are
    showing all the instances.
+ * That InstanceUtils.asString properly takes into account "offset" and
+   "count" fields, if they are present.
 
 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 7ee3ff2..10c1092 100644
--- a/tools/ahat/src/InstanceUtils.java
+++ b/tools/ahat/src/InstanceUtils.java
@@ -37,22 +37,6 @@
   }
 
   /**
-   * Read the char[] value from an hprof Instance.
-   * Returns null if the object can't be interpreted as a char[].
-   */
-  private static char[] asCharArray(Instance inst) {
-    if (! (inst instanceof ArrayInstance)) {
-      return null;
-    }
-
-    ArrayInstance array = (ArrayInstance) inst;
-    if (array.getArrayType() != Type.CHAR) {
-      return null;
-    }
-    return array.asCharArray(0, array.getValues().length);
-  }
-
-  /**
    * Read the byte[] value from an hprof Instance.
    * Returns null if the instance is not a byte array.
    */
@@ -82,8 +66,32 @@
     if (!isInstanceOfClass(inst, "java.lang.String")) {
       return null;
     }
-    char[] value = getCharArrayField(inst, "value");
-    return (value == null) ? null : new String(value);
+
+    Object value = getField(inst, "value");
+    if (!(value instanceof ArrayInstance)) {
+      return null;
+    }
+
+    ArrayInstance chars = (ArrayInstance) value;
+    if (chars.getArrayType() != Type.CHAR) {
+      return null;
+    }
+
+    // TODO: When perflib provides a better way to get the length of the
+    // 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 (offset >= 0 && offset < numChars && end >= 0 && end < numChars) {
+      return new String(chars.asCharArray(offset, count));
+    }
+    return null;
   }
 
   /**
@@ -175,6 +183,15 @@
   }
 
   /**
+   * Read an int field of an instance, returning a default value if the field
+   * was not an int or could not be read.
+   */
+  private static int getIntField(Instance inst, String fieldName, int def) {
+    Integer value = getIntField(inst, fieldName);
+    return value == null ? def : value;
+  }
+
+  /**
    * Read the given field from the given instance.
    * The field is assumed to be a byte[] field.
    * Returns null if the field value is null, not a byte[] or could not be read.
@@ -187,14 +204,6 @@
     return asByteArray((Instance)value);
   }
 
-  private static char[] getCharArrayField(Instance inst, String fieldName) {
-    Object value = getField(inst, fieldName);
-    if (!(value instanceof Instance)) {
-      return null;
-    }
-    return asCharArray((Instance)value);
-  }
-
   // Return the bitmap instance associated with this object, or null if there
   // is none. This works for android.graphics.Bitmap instances and their
   // underlying Byte[] instances.