Merge "Fix multi-line hex dumping and add unit test."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index bed48ba..acaa0f8 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -30,6 +30,7 @@
 	compiler/utils/x86/managed_register_x86_test.cc \
 	runtime/barrier_test.cc \
 	runtime/base/bit_vector_test.cc \
+	runtime/base/hex_dump_test.cc \
 	runtime/base/histogram_test.cc \
 	runtime/base/mutex_test.cc \
 	runtime/base/timing_logger_test.cc \
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 9e6d5f9..c180b17 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -23,6 +23,7 @@
 	barrier.cc \
 	base/allocator.cc \
 	base/bit_vector.cc \
+	base/hex_dump.cc \
 	base/logging.cc \
 	base/mutex.cc \
 	base/stringpiece.cc \
diff --git a/runtime/base/hex_dump.cc b/runtime/base/hex_dump.cc
new file mode 100644
index 0000000..936c52b
--- /dev/null
+++ b/runtime/base/hex_dump.cc
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hex_dump.h"
+
+#include "globals.h"
+
+#include <string.h>
+
+namespace art {
+
+void HexDump::Dump(std::ostream& os) const {
+  if (byte_count_ == 0) {
+    return;
+  }
+
+  if (address_ == NULL) {
+    os << "00000000:";
+    return;
+  }
+
+  static const char gHexDigit[] = "0123456789abcdef";
+  const unsigned char* addr = reinterpret_cast<const unsigned char*>(address_);
+  // 01234560: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff  0123456789abcdef
+  char out[(kBitsPerWord / 4) + /* offset */
+           1 + /* colon */
+           (16 * 3) + /* 16 hex digits and space */
+           2 + /* white space */
+           16 + /* 16 characters*/
+           1 /* \0 */ ];
+  size_t offset;    /* offset to show while printing */
+
+  if (show_actual_addresses_) {
+    offset = reinterpret_cast<size_t>(addr);
+  } else {
+    offset = 0;
+  }
+  memset(out, ' ', sizeof(out)-1);
+  out[kBitsPerWord / 4] = ':';
+  out[sizeof(out)-1] = '\0';
+
+  size_t byte_count = byte_count_;
+  size_t gap = offset & 0x0f;
+  while (byte_count > 0) {
+    size_t line_offset = offset & ~0x0f;
+
+    char* hex = out;
+    char* asc = out + (kBitsPerWord / 4) + /* offset */ 1 + /* colon */
+        (16 * 3) + /* 16 hex digits and space */ 2 /* white space */;
+
+    for (int i = 0; i < (kBitsPerWord / 4); i++) {
+      *hex++ = gHexDigit[line_offset >> (kBitsPerWord - 4)];
+      line_offset <<= 4;
+    }
+    hex++;
+    hex++;
+
+    size_t count = std::min(byte_count, 16 - gap);
+    // CHECK_NE(count, 0U);
+    // CHECK_LE(count + gap, 16U);
+
+    if (gap) {
+      /* only on first line */
+      hex += gap * 3;
+      asc += gap;
+    }
+
+    size_t i;
+    for (i = gap ; i < count + gap; i++) {
+      *hex++ = gHexDigit[*addr >> 4];
+      *hex++ = gHexDigit[*addr & 0x0f];
+      hex++;
+      if (*addr >= 0x20 && *addr < 0x7f /*isprint(*addr)*/) {
+        *asc++ = *addr;
+      } else {
+        *asc++ = '.';
+      }
+      addr++;
+    }
+    for (; i < 16; i++) {
+      /* erase extra stuff; only happens on last line */
+      *hex++ = ' ';
+      *hex++ = ' ';
+      hex++;
+      *asc++ = ' ';
+    }
+
+    os << prefix_ << out;
+
+    gap = 0;
+    byte_count -= count;
+    offset += count;
+    if (byte_count > 0) {
+      os << "\n";
+    }
+  }
+}
+
+}  // namespace art
diff --git a/runtime/base/hex_dump.h b/runtime/base/hex_dump.h
new file mode 100644
index 0000000..8769ece
--- /dev/null
+++ b/runtime/base/hex_dump.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_HEX_DUMP_H_
+#define ART_RUNTIME_BASE_HEX_DUMP_H_
+
+#include "macros.h"
+
+#include <ostream>
+
+namespace art {
+
+// Prints a hex dump in this format:
+//
+// 01234560: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff  0123456789abcdef
+// 01234568: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff  0123456789abcdef
+class HexDump {
+ public:
+  HexDump(const void* address, size_t byte_count, bool show_actual_addresses, const char* prefix)
+      : address_(address), byte_count_(byte_count), show_actual_addresses_(show_actual_addresses),
+        prefix_(prefix) {
+  }
+
+  void Dump(std::ostream& os) const;
+
+ private:
+  const void* const address_;
+  const size_t byte_count_;
+  const bool show_actual_addresses_;
+  const char* const prefix_;
+
+  DISALLOW_COPY_AND_ASSIGN(HexDump);
+};
+
+inline std::ostream& operator<<(std::ostream& os, const HexDump& rhs) {
+  rhs.Dump(os);
+  return os;
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_BASE_HEX_DUMP_H_
diff --git a/runtime/base/hex_dump_test.cc b/runtime/base/hex_dump_test.cc
new file mode 100644
index 0000000..d950961
--- /dev/null
+++ b/runtime/base/hex_dump_test.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hex_dump.h"
+
+#include "globals.h"
+
+#include "gtest/gtest.h"
+
+#include <stdint.h>
+
+namespace art {
+
+TEST(HexDump, OneLine) {
+  const char* test_text = "0123456789abcdef";
+  std::ostringstream oss;
+  oss << HexDump(test_text, strlen(test_text), false, "");
+  EXPECT_STREQ(oss.str().c_str(),
+               "00000000: 30 31 32 33 34 35 36 37 38 39 61 62 63 64 65 66  0123456789abcdef");
+}
+
+TEST(HexDump, MultiLine) {
+  const char* test_text = "0123456789abcdef0123456789ABCDEF";
+  std::ostringstream oss;
+  oss << HexDump(test_text, strlen(test_text), false, "");
+  EXPECT_STREQ(oss.str().c_str(),
+               "00000000: 30 31 32 33 34 35 36 37 38 39 61 62 63 64 65 66  0123456789abcdef\n"
+               "00000010: 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 46  0123456789ABCDEF");
+}
+
+uint64_t g16byte_aligned_number __attribute__ ((aligned(16)));  // NOLINT(whitespace/parens)
+TEST(HexDump, ShowActualAddresses) {
+  g16byte_aligned_number = 0x6162636465666768;
+  std::ostringstream oss;
+  oss << HexDump(&g16byte_aligned_number, 8, true, "");
+  // Compare ignoring pointer.
+  EXPECT_STREQ(oss.str().c_str() + (kBitsPerWord / 4),
+               ": 68 67 66 65 64 63 62 61                          hgfedcba        ");
+}
+
+TEST(HexDump, Prefix) {
+  const char* test_text = "0123456789abcdef";
+  std::ostringstream oss;
+  oss << HexDump(test_text, strlen(test_text), false, "test prefix: ");
+  EXPECT_STREQ(oss.str().c_str(),
+               "test prefix: 00000000: 30 31 32 33 34 35 36 37 38 39 61 62 63 64 65 66  "
+               "0123456789abcdef");
+}
+
+}  // namespace art
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index 15554ac..46b8ff2 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -161,97 +161,4 @@
   }
 }
 
-HexDump::HexDump(const void* address, size_t byte_count, bool show_actual_addresses)
-    : address_(address), byte_count_(byte_count), show_actual_addresses_(show_actual_addresses) {
-}
-
-void HexDump::Dump(std::ostream& os) const {
-  if (byte_count_ == 0) {
-    return;
-  }
-
-  if (address_ == NULL) {
-    os << "00000000:";
-    return;
-  }
-
-  static const char gHexDigit[] = "0123456789abcdef";
-  const unsigned char* addr = reinterpret_cast<const unsigned char*>(address_);
-  // 01234560: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff  0123456789abcdef
-  char out[(kBitsPerWord / 4) + /* offset */
-           1 + /* colon */
-           (16 * 3) + /* 16 hex digits and space */
-           2 + /* white space */
-           16 + /* 16 characters*/
-           1 /* \0 */ ];
-  size_t offset;    /* offset to show while printing */
-
-  if (show_actual_addresses_) {
-    offset = reinterpret_cast<size_t>(addr);
-  } else {
-    offset = 0;
-  }
-  memset(out, ' ', sizeof(out)-1);
-  out[kBitsPerWord / 4] = ':';
-  out[sizeof(out)-1] = '\0';
-
-  size_t byte_count = byte_count_;
-  size_t gap = offset & 0x0f;
-  while (byte_count) {
-    size_t line_offset = offset & ~0x0f;
-
-    char* hex = out;
-    char* asc = out + (kBitsPerWord / 4) + /* offset */ 1 + /* colon */
-        (16 * 3) + /* 16 hex digits and space */ 2 /* white space */;
-
-    for (int i = 0; i < (kBitsPerWord / 4); i++) {
-      *hex++ = gHexDigit[line_offset >> (kBitsPerWord - 4)];
-      line_offset <<= 4;
-    }
-    hex++;
-    hex++;
-
-    size_t count = std::min(byte_count, 16 - gap);
-    CHECK_NE(count, 0U);
-    CHECK_LE(count + gap, 16U);
-
-    if (gap) {
-      /* only on first line */
-      hex += gap * 3;
-      asc += gap;
-    }
-
-    size_t i;
-    for (i = gap ; i < count + gap; i++) {
-      *hex++ = gHexDigit[*addr >> 4];
-      *hex++ = gHexDigit[*addr & 0x0f];
-      hex++;
-      if (*addr >= 0x20 && *addr < 0x7f /*isprint(*addr)*/) {
-        *asc++ = *addr;
-      } else {
-        *asc++ = '.';
-      }
-      addr++;
-    }
-    for (; i < 16; i++) {
-      /* erase extra stuff; only happens on last line */
-      *hex++ = ' ';
-      *hex++ = ' ';
-      hex++;
-      *asc++ = ' ';
-    }
-
-    os << out;
-
-    gap = 0;
-    byte_count -= count;
-    offset += count;
-  }
-}
-
-std::ostream& operator<<(std::ostream& os, const HexDump& rhs) {
-  rhs.Dump(os);
-  return os;
-}
-
 }  // namespace art
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index 8e40da0..075d571 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -208,24 +208,6 @@
   DISALLOW_COPY_AND_ASSIGN(LogMessage);
 };
 
-// Prints a hex dump in this format:
-//
-// 01234560: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff  0123456789abcdef
-// 01234568: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff  0123456789abcdef
-class HexDump {
- public:
-  HexDump(const void* address, size_t byte_count, bool show_actual_addresses = false);
-  void Dump(std::ostream& os) const;
-
- private:
-  const void* address_;
-  size_t byte_count_;
-  bool show_actual_addresses_;
-
-  DISALLOW_COPY_AND_ASSIGN(HexDump);
-};
-std::ostream& operator<<(std::ostream& os, const HexDump& rhs);
-
 // A convenience to allow any class with a "Dump(std::ostream& os)" member function
 // but without an operator<< to be used as if it had an operator<<. Use like this:
 //
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 6522a62..a514e69 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -21,6 +21,7 @@
 #include <string>
 
 #include "atomic.h"
+#include "base/hex_dump.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/stringprintf.h"
@@ -1705,7 +1706,7 @@
   }
   if (i == arraysize(gHandlers)) {
     LOG(ERROR) << "Command not implemented: " << DescribeCommand(request);
-    LOG(ERROR) << HexDump(request.data(), request.size());
+    LOG(ERROR) << HexDump(request.data(), request.size(), false, "");
     result = ERR_NOT_IMPLEMENTED;
   }
 
@@ -1729,7 +1730,7 @@
   size_t respLen = expandBufGetLength(pReply) - kJDWPHeaderLen;
   VLOG(jdwp) << "REPLY: " << GetCommandName(request) << " " << result << " (length=" << respLen << ")";
   if (false) {
-    VLOG(jdwp) << HexDump(expandBufGetBuffer(pReply) + kJDWPHeaderLen, respLen);
+    VLOG(jdwp) << HexDump(expandBufGetBuffer(pReply) + kJDWPHeaderLen, respLen, false, "");
   }
 
   VLOG(jdwp) << "----------";