Use PrintableString in oatdump.

And enhance PrintableString to assume modified UTF-8, which is all we ever give
it. \u0000 is more readable than \xc0\x80 to most people.

Change-Id: I45bd8d65694eda0ef4ef03abc40f41a76f07a671
diff --git a/src/class_linker.cc b/src/class_linker.cc
index ae13908..4a38493 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1216,7 +1216,7 @@
     }
   }
 
-  ThrowNoClassDefFoundError("Class %s not found", PrintableString(StringPiece(descriptor)).c_str());
+  ThrowNoClassDefFoundError("Class %s not found", PrintableString(descriptor).c_str());
   return NULL;
 }
 
diff --git a/src/jdwp/jdwp_handler.cc b/src/jdwp/jdwp_handler.cc
index e8f9e61..355fc5e 100644
--- a/src/jdwp/jdwp_handler.cc
+++ b/src/jdwp/jdwp_handler.cc
@@ -849,7 +849,7 @@
   ObjectId stringObject = ReadObjectId(&buf);
   std::string str(Dbg::StringToUtf8(stringObject));
 
-  VLOG(jdwp) << StringPrintf("  Req for str %#llx --> '%s'", stringObject, PrintableString(str).c_str());
+  VLOG(jdwp) << StringPrintf("  Req for str %#llx --> %s", stringObject, PrintableString(str).c_str());
 
   expandBufAddUtf8String(pReply, str);
 
diff --git a/src/oatdump.cc b/src/oatdump.cc
index d2a78af..af084d5 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -840,8 +840,8 @@
       StringAppendF(&summary, "%p: java.lang.reflect.Method %s\n", obj,
                     PrettyMethod(obj->AsMethod()).c_str());
     } else if (obj_class->IsStringClass()) {
-      StringAppendF(&summary, "%p: java.lang.String \"%s\"\n", obj,
-                    obj->AsString()->ToModifiedUtf8().c_str());
+      StringAppendF(&summary, "%p: java.lang.String %s\n", obj,
+                    PrintableString(obj->AsString()->ToModifiedUtf8()).c_str());
     } else {
       StringAppendF(&summary, "%p: %s\n", obj, PrettyDescriptor(obj_class).c_str());
     }
diff --git a/src/utils.cc b/src/utils.cc
index 127c18f..e80de5b 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -404,6 +404,31 @@
   }
 }
 
+std::string PrintableString(const std::string& utf) {
+  std::string result;
+  result += '"';
+  const char* p = utf.c_str();
+  size_t char_count = CountModifiedUtf8Chars(p);
+  for (size_t i = 0; i < char_count; ++i) {
+    uint16_t ch = GetUtf16FromUtf8(&p);
+    if (ch == '\\') {
+      result += "\\\\";
+    } else if (ch == '\n') {
+      result += "\\n";
+    } else if (ch == '\r') {
+      result += "\\r";
+    } else if (ch == '\t') {
+      result += "\\t";
+    } else if (NeedsEscaping(ch)) {
+      StringAppendF(&result, "\\u%04x", ch);
+    } else {
+      result += ch;
+    }
+  }
+  result += '"';
+  return result;
+}
+
 // See http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/design.html#wp615 for the full rules.
 std::string MangleForJni(const std::string& s) {
   std::string result;
diff --git a/src/utils.h b/src/utils.h
index 4bfd885..c709f9a 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -148,22 +148,9 @@
   return result;
 }
 
-// TODO: assume the content is UTF-8, and show code point escapes?
-template<typename StringT>
-static inline std::string PrintableString(const StringT& s) {
-  std::string result;
-  result += '"';
-  for (typename StringT::const_iterator it = s.begin(); it != s.end(); ++it) {
-    char ch = *it;
-    if (NeedsEscaping(ch)) {
-      StringAppendF(&result, "\\x%02x", ch & 0xff);
-    } else {
-      result += ch;
-    }
-  }
-  result += '"';
-  return result;
-}
+// Returns an ASCII string corresponding to the given UTF-8 string.
+// Java escapes are used for non-ASCII characters.
+std::string PrintableString(const std::string& utf8);
 
 // Tests whether 's' starts with 'prefix'.
 bool StartsWith(const std::string& s, const char* prefix);