New version of unwinder.

Bug: 23762183

Test: All unit tests pass.
Change-Id: I0ac69e55af56e1142c0a1ee3715cdc48f2ed3ec3
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
new file mode 100644
index 0000000..9ea917a
--- /dev/null
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -0,0 +1,992 @@
+/*
+ * Copyright (C) 2016 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 <stdint.h>
+
+#include <deque>
+#include <ios>
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "ArmExidx.h"
+#include "Log.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> {
+ protected:
+  void Init(Memory* process_memory = nullptr) {
+    TearDown();
+
+    if (process_memory == nullptr) {
+      process_memory = &process_memory_;
+    }
+
+    regs32_.reset(new Regs32(0, 1, 32));
+    for (size_t i = 0; i < 32; i++) {
+      (*regs32_)[i] = 0;
+    }
+
+    exidx_.reset(new ArmExidx(regs32_.get(), &elf_memory_, process_memory));
+    if (log_) {
+      exidx_->set_log(true);
+      exidx_->set_log_indent(0);
+      exidx_->set_log_skip_execution(false);
+    }
+    data_ = exidx_->data();
+    exidx_->set_cfa(0x10000);
+  }
+
+  void SetUp() override {
+    if (GetParam() != "no_logging") {
+      log_ = false;
+    } else {
+      log_ = true;
+    }
+    ResetLogs();
+    elf_memory_.Clear();
+    process_memory_.Clear();
+    Init();
+  }
+
+  std::unique_ptr<ArmExidx> exidx_;
+  std::unique_ptr<Regs32> regs32_;
+  std::deque<uint8_t>* data_;
+
+  MemoryFake elf_memory_;
+  MemoryFake process_memory_;
+  bool log_;
+};
+
+TEST_P(ArmExidxDecodeTest, vsp_incr) {
+  // 00xxxxxx: vsp = vsp + (xxxxxx << 2) + 4
+  data_->push_back(0x00);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10004U, exidx_->cfa());
+
+  ResetLogs();
+  data_->clear();
+  data_->push_back(0x01);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1000cU, exidx_->cfa());
+
+  ResetLogs();
+  data_->clear();
+  data_->push_back(0x3f);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1010cU, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, vsp_decr) {
+  // 01xxxxxx: vsp = vsp - (xxxxxx << 2) + 4
+  data_->push_back(0x40);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0xfffcU, exidx_->cfa());
+
+  ResetLogs();
+  data_->clear();
+  data_->push_back(0x41);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0xfff4U, exidx_->cfa());
+
+  ResetLogs();
+  data_->clear();
+  data_->push_back(0x7f);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0xfef4U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, refuse_unwind) {
+  // 10000000 00000000: Refuse to unwind
+  data_->push_back(0x80);
+  data_->push_back(0x00);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind Refuse to unwind\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_up_to_12) {
+  // 1000iiii iiiiiiii: Pop up to 12 integer registers
+  data_->push_back(0x80);
+  data_->push_back(0x01);
+  process_memory_.SetData(0x10000, 0x10);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10004U, exidx_->cfa());
+  ASSERT_EQ(0x10U, (*exidx_->regs())[4]);
+
+  ResetLogs();
+  data_->push_back(0x8f);
+  data_->push_back(0xff);
+  for (size_t i = 0; i < 12; i++) {
+    process_memory_.SetData(0x10004 + i * 4, i + 0x20);
+  }
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n",
+              GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  // Popping r13 results in a modified cfa.
+  ASSERT_EQ(0x29U, exidx_->cfa());
+
+  ASSERT_EQ(0x20U, (*exidx_->regs())[4]);
+  ASSERT_EQ(0x21U, (*exidx_->regs())[5]);
+  ASSERT_EQ(0x22U, (*exidx_->regs())[6]);
+  ASSERT_EQ(0x23U, (*exidx_->regs())[7]);
+  ASSERT_EQ(0x24U, (*exidx_->regs())[8]);
+  ASSERT_EQ(0x25U, (*exidx_->regs())[9]);
+  ASSERT_EQ(0x26U, (*exidx_->regs())[10]);
+  ASSERT_EQ(0x27U, (*exidx_->regs())[11]);
+  ASSERT_EQ(0x28U, (*exidx_->regs())[12]);
+  ASSERT_EQ(0x29U, (*exidx_->regs())[13]);
+  ASSERT_EQ(0x2aU, (*exidx_->regs())[14]);
+  ASSERT_EQ(0x2bU, (*exidx_->regs())[15]);
+
+  ResetLogs();
+  exidx_->set_cfa(0x10034);
+  data_->push_back(0x81);
+  data_->push_back(0x28);
+  process_memory_.SetData(0x10034, 0x11);
+  process_memory_.SetData(0x10038, 0x22);
+  process_memory_.SetData(0x1003c, 0x33);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10040U, exidx_->cfa());
+  ASSERT_EQ(0x11U, (*exidx_->regs())[7]);
+  ASSERT_EQ(0x22U, (*exidx_->regs())[9]);
+  ASSERT_EQ(0x33U, (*exidx_->regs())[12]);
+}
+
+TEST_P(ArmExidxDecodeTest, set_vsp_from_register) {
+  // 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
+  exidx_->set_cfa(0x100);
+  for (size_t i = 0; i < 15; i++) {
+    (*regs32_)[i] = i + 1;
+  }
+
+  data_->push_back(0x90);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(1U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0x93);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(4U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0x9e);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(15U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, reserved_prefix) {
+  // 10011101: Reserved as prefix for ARM register to register moves
+  data_->push_back(0x9d);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
+
+  // 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
+  ResetLogs();
+  data_->push_back(0x9f);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_registers) {
+  // 10100nnn: Pop r4-r[4+nnn]
+  data_->push_back(0xa0);
+  process_memory_.SetData(0x10000, 0x14);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10004U, exidx_->cfa());
+  ASSERT_EQ(0x14U, (*exidx_->regs())[4]);
+
+  ResetLogs();
+  data_->push_back(0xa3);
+  process_memory_.SetData(0x10004, 0x20);
+  process_memory_.SetData(0x10008, 0x30);
+  process_memory_.SetData(0x1000c, 0x40);
+  process_memory_.SetData(0x10010, 0x50);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10014U, exidx_->cfa());
+  ASSERT_EQ(0x20U, (*exidx_->regs())[4]);
+  ASSERT_EQ(0x30U, (*exidx_->regs())[5]);
+  ASSERT_EQ(0x40U, (*exidx_->regs())[6]);
+  ASSERT_EQ(0x50U, (*exidx_->regs())[7]);
+
+  ResetLogs();
+  data_->push_back(0xa7);
+  process_memory_.SetData(0x10014, 0x41);
+  process_memory_.SetData(0x10018, 0x51);
+  process_memory_.SetData(0x1001c, 0x61);
+  process_memory_.SetData(0x10020, 0x71);
+  process_memory_.SetData(0x10024, 0x81);
+  process_memory_.SetData(0x10028, 0x91);
+  process_memory_.SetData(0x1002c, 0xa1);
+  process_memory_.SetData(0x10030, 0xb1);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10034U, exidx_->cfa());
+  ASSERT_EQ(0x41U, (*exidx_->regs())[4]);
+  ASSERT_EQ(0x51U, (*exidx_->regs())[5]);
+  ASSERT_EQ(0x61U, (*exidx_->regs())[6]);
+  ASSERT_EQ(0x71U, (*exidx_->regs())[7]);
+  ASSERT_EQ(0x81U, (*exidx_->regs())[8]);
+  ASSERT_EQ(0x91U, (*exidx_->regs())[9]);
+  ASSERT_EQ(0xa1U, (*exidx_->regs())[10]);
+  ASSERT_EQ(0xb1U, (*exidx_->regs())[11]);
+}
+
+TEST_P(ArmExidxDecodeTest, pop_registers_with_r14) {
+  // 10101nnn: Pop r4-r[4+nnn], r14
+  data_->push_back(0xa8);
+  process_memory_.SetData(0x10000, 0x12);
+  process_memory_.SetData(0x10004, 0x22);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10008U, exidx_->cfa());
+  ASSERT_EQ(0x12U, (*exidx_->regs())[4]);
+  ASSERT_EQ(0x22U, (*exidx_->regs())[14]);
+
+  ResetLogs();
+  data_->push_back(0xab);
+  process_memory_.SetData(0x10008, 0x1);
+  process_memory_.SetData(0x1000c, 0x2);
+  process_memory_.SetData(0x10010, 0x3);
+  process_memory_.SetData(0x10014, 0x4);
+  process_memory_.SetData(0x10018, 0x5);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1001cU, exidx_->cfa());
+  ASSERT_EQ(0x1U, (*exidx_->regs())[4]);
+  ASSERT_EQ(0x2U, (*exidx_->regs())[5]);
+  ASSERT_EQ(0x3U, (*exidx_->regs())[6]);
+  ASSERT_EQ(0x4U, (*exidx_->regs())[7]);
+  ASSERT_EQ(0x5U, (*exidx_->regs())[14]);
+
+  ResetLogs();
+  data_->push_back(0xaf);
+  process_memory_.SetData(0x1001c, 0x1a);
+  process_memory_.SetData(0x10020, 0x2a);
+  process_memory_.SetData(0x10024, 0x3a);
+  process_memory_.SetData(0x10028, 0x4a);
+  process_memory_.SetData(0x1002c, 0x5a);
+  process_memory_.SetData(0x10030, 0x6a);
+  process_memory_.SetData(0x10034, 0x7a);
+  process_memory_.SetData(0x10038, 0x8a);
+  process_memory_.SetData(0x1003c, 0x9a);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10040U, exidx_->cfa());
+  ASSERT_EQ(0x1aU, (*exidx_->regs())[4]);
+  ASSERT_EQ(0x2aU, (*exidx_->regs())[5]);
+  ASSERT_EQ(0x3aU, (*exidx_->regs())[6]);
+  ASSERT_EQ(0x4aU, (*exidx_->regs())[7]);
+  ASSERT_EQ(0x5aU, (*exidx_->regs())[8]);
+  ASSERT_EQ(0x6aU, (*exidx_->regs())[9]);
+  ASSERT_EQ(0x7aU, (*exidx_->regs())[10]);
+  ASSERT_EQ(0x8aU, (*exidx_->regs())[11]);
+  ASSERT_EQ(0x9aU, (*exidx_->regs())[14]);
+}
+
+TEST_P(ArmExidxDecodeTest, finish) {
+  // 10110000: Finish
+  data_->push_back(0xb0);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind finish\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10000U, exidx_->cfa());
+  ASSERT_EQ(ARM_STATUS_FINISH, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, spare) {
+  // 10110001 00000000: Spare
+  data_->push_back(0xb1);
+  data_->push_back(0x00);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10000U, exidx_->cfa());
+  ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+
+  // 10110001 xxxxyyyy: Spare (xxxx != 0000)
+  for (size_t x = 1; x < 16; x++) {
+    for (size_t y = 0; y < 16; y++) {
+      ResetLogs();
+      data_->push_back(0xb1);
+      data_->push_back((x << 4) | y);
+      ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
+      ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
+      if (log_) {
+        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+      } else {
+        ASSERT_EQ("", GetFakeLogPrint());
+      }
+      ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
+      ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+    }
+  }
+
+  // 101101nn: Spare
+  for (size_t n = 0; n < 4; n++) {
+    ResetLogs();
+    data_->push_back(0xb4 | n);
+    ASSERT_FALSE(exidx_->Decode()) << "n = " << n;
+    ASSERT_EQ("", GetFakeLogBuf()) << "n = " << n;
+    if (log_) {
+      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "n = " << n;
+    } else {
+      ASSERT_EQ("", GetFakeLogPrint());
+    }
+    ASSERT_EQ(0x10000U, exidx_->cfa()) << "n = " << n;
+    ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+  }
+
+  // 11000111 00000000: Spare
+  ResetLogs();
+  data_->push_back(0xc7);
+  data_->push_back(0x00);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10000U, exidx_->cfa());
+  ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+
+  // 11000111 xxxxyyyy: Spare (xxxx != 0000)
+  for (size_t x = 1; x < 16; x++) {
+    for (size_t y = 0; y < 16; y++) {
+      ResetLogs();
+      data_->push_back(0xc7);
+      data_->push_back(0x10);
+      ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
+      ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
+      if (log_) {
+        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+      } else {
+        ASSERT_EQ("", GetFakeLogPrint());
+      }
+      ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
+      ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+    }
+  }
+
+  // 11001yyy: Spare (yyy != 000, 001)
+  for (size_t y = 2; y < 8; y++) {
+    ResetLogs();
+    data_->push_back(0xc8 | y);
+    ASSERT_FALSE(exidx_->Decode()) << "y = " << y;
+    ASSERT_EQ("", GetFakeLogBuf()) << "y = " << y;
+    if (log_) {
+      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "y = " << y;
+    } else {
+      ASSERT_EQ("", GetFakeLogPrint());
+    }
+    ASSERT_EQ(0x10000U, exidx_->cfa()) << "y = " << y;
+    ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+  }
+
+  // 11xxxyyy: Spare (xxx != 000, 001, 010)
+  for (size_t x = 3; x < 8; x++) {
+    for (size_t y = 0; y < 8; y++) {
+      ResetLogs();
+      data_->push_back(0xc0 | (x << 3) | y);
+      ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
+      ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
+      if (log_) {
+        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+      } else {
+        ASSERT_EQ("", GetFakeLogPrint());
+      }
+      ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
+      ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+    }
+  }
+}
+
+TEST_P(ArmExidxDecodeTest, pop_registers_under_mask) {
+  // 10110001 0000iiii: Pop integer registers {r0, r1, r2, r3}
+  data_->push_back(0xb1);
+  data_->push_back(0x01);
+  process_memory_.SetData(0x10000, 0x45);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10004U, exidx_->cfa());
+  ASSERT_EQ(0x45U, (*exidx_->regs())[0]);
+
+  ResetLogs();
+  data_->push_back(0xb1);
+  data_->push_back(0x0a);
+  process_memory_.SetData(0x10004, 0x23);
+  process_memory_.SetData(0x10008, 0x24);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1000cU, exidx_->cfa());
+  ASSERT_EQ(0x23U, (*exidx_->regs())[1]);
+  ASSERT_EQ(0x24U, (*exidx_->regs())[3]);
+
+  ResetLogs();
+  data_->push_back(0xb1);
+  data_->push_back(0x0f);
+  process_memory_.SetData(0x1000c, 0x65);
+  process_memory_.SetData(0x10010, 0x54);
+  process_memory_.SetData(0x10014, 0x43);
+  process_memory_.SetData(0x10018, 0x32);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1001cU, exidx_->cfa());
+  ASSERT_EQ(0x65U, (*exidx_->regs())[0]);
+  ASSERT_EQ(0x54U, (*exidx_->regs())[1]);
+  ASSERT_EQ(0x43U, (*exidx_->regs())[2]);
+  ASSERT_EQ(0x32U, (*exidx_->regs())[3]);
+}
+
+TEST_P(ArmExidxDecodeTest, vsp_large_incr) {
+  // 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
+  data_->push_back(0xb2);
+  data_->push_back(0x7f);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10400U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xb2);
+  data_->push_back(0xff);
+  data_->push_back(0x02);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10c00U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xb2);
+  data_->push_back(0xff);
+  data_->push_back(0x82);
+  data_->push_back(0x30);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x311400U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp_fstmfdx) {
+  // 10110011 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by FSTMFDX
+  data_->push_back(0xb3);
+  data_->push_back(0x00);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1000cU, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xb3);
+  data_->push_back(0x48);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10058U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp8_fstmfdx) {
+  // 10111nnn: Pop VFP double precision registers D[8]-D[8+nnn] by FSTMFDX
+  data_->push_back(0xb8);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1000cU, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xbb);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10030U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xbf);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10074U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_mmx_wr10) {
+  // 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7)
+  data_->push_back(0xc0);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10008U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc2);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10020U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc5);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10050U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_mmx_wr) {
+  // 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]
+  data_->push_back(0xc6);
+  data_->push_back(0x00);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10008U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc6);
+  data_->push_back(0x25);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10038U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc6);
+  data_->push_back(0xff);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x100b8U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_mmx_wcgr) {
+  // 11000111 0000iiii: Intel Wireless MMX pop wCGR registes {wCGR0,1,2,3}
+  data_->push_back(0xc7);
+  data_->push_back(0x01);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10004U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc7);
+  data_->push_back(0x0a);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1000cU, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc7);
+  data_->push_back(0x0f);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1001cU, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp16_vpush) {
+  // 11001000 sssscccc: Pop VFP double precision registers d[16+ssss]-D[16+ssss+cccc] by VPUSH
+  data_->push_back(0xc8);
+  data_->push_back(0x00);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10008U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc8);
+  data_->push_back(0x14);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10030U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc8);
+  data_->push_back(0xff);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x100b0U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp_vpush) {
+  // 11001001 sssscccc: Pop VFP double precision registers d[ssss]-D[ssss+cccc] by VPUSH
+  data_->push_back(0xc9);
+  data_->push_back(0x00);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10008U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc9);
+  data_->push_back(0x23);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10028U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc9);
+  data_->push_back(0xff);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x100a8U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp8_vpush) {
+  // 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
+  data_->push_back(0xd0);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10008U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xd2);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10020U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xd7);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10060U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, expect_truncated) {
+  // This test verifies that any op that requires extra ops will
+  // fail if the data is not present.
+  data_->push_back(0x80);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+  data_->clear();
+  data_->push_back(0xb1);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+  data_->clear();
+  data_->push_back(0xb2);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+  data_->clear();
+  data_->push_back(0xb3);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+  data_->clear();
+  data_->push_back(0xc6);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+  data_->clear();
+  data_->push_back(0xc7);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+  data_->clear();
+  data_->push_back(0xc8);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+  data_->clear();
+  data_->push_back(0xc9);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, verify_no_truncated) {
+  // This test verifies that no pattern results in a crash or truncation.
+  MemoryFakeAlwaysReadZero memory_zero;
+  Init(&memory_zero);
+
+  for (size_t x = 0; x < 256; x++) {
+    if (x == 0xb2) {
+      // This opcode is followed by an uleb128, so just skip this one.
+      continue;
+    }
+    for (size_t y = 0; y < 256; y++) {
+      data_->clear();
+      data_->push_back(x);
+      data_->push_back(y);
+      if (!exidx_->Decode()) {
+        ASSERT_NE(ARM_STATUS_TRUNCATED, exidx_->status())
+            << "x y = 0x" << std::hex << x << " 0x" << y;
+        ASSERT_NE(ARM_STATUS_READ_FAILED, exidx_->status())
+            << "x y = 0x" << std::hex << x << " 0x" << y;
+      }
+    }
+  }
+}
+
+INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest, ::testing::Values("logging", "no_logging"));
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
new file mode 100644
index 0000000..021765a
--- /dev/null
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2016 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 <stdint.h>
+
+#include <deque>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "ArmExidx.h"
+#include "Log.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+class ArmExidxExtractTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+    elf_memory_.Clear();
+    exidx_ = new ArmExidx(nullptr, &elf_memory_, nullptr);
+    data_ = exidx_->data();
+    data_->clear();
+  }
+
+  void TearDown() override {
+    delete exidx_;
+  }
+
+  ArmExidx* exidx_ = nullptr;
+  std::deque<uint8_t>* data_;
+  MemoryFake elf_memory_;
+};
+
+TEST_F(ArmExidxExtractTest, bad_alignment) {
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x1001));
+  ASSERT_EQ(ARM_STATUS_INVALID_ALIGNMENT, exidx_->status());
+  ASSERT_TRUE(data_->empty());
+}
+
+TEST_F(ArmExidxExtractTest, cant_unwind) {
+  elf_memory_.SetData(0x1000, 0x7fff2340);
+  elf_memory_.SetData(0x1004, 1);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x1000));
+  ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
+  ASSERT_TRUE(data_->empty());
+}
+
+TEST_F(ArmExidxExtractTest, compact) {
+  elf_memory_.SetData(0x4000, 0x7ffa3000);
+  elf_memory_.SetData(0x4004, 0x80a8b0b0);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x4000));
+  ASSERT_EQ(3U, data_->size());
+  ASSERT_EQ(0xa8, data_->at(0));
+  ASSERT_EQ(0xb0, data_->at(1));
+  ASSERT_EQ(0xb0, data_->at(2));
+
+  // Missing finish gets added.
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x534, 0x7ffa3000);
+  elf_memory_.SetData(0x538, 0x80a1a2a3);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x534));
+  ASSERT_EQ(4U, data_->size());
+  ASSERT_EQ(0xa1, data_->at(0));
+  ASSERT_EQ(0xa2, data_->at(1));
+  ASSERT_EQ(0xa3, data_->at(2));
+  ASSERT_EQ(0xb0, data_->at(3));
+}
+
+TEST_F(ArmExidxExtractTest, compact_non_zero_personality) {
+  elf_memory_.SetData(0x4000, 0x7ffa3000);
+
+  uint32_t compact_value = 0x80a8b0b0;
+  for (size_t i = 1; i < 16; i++) {
+    elf_memory_.SetData(0x4004, compact_value | (i << 24));
+    ASSERT_FALSE(exidx_->ExtractEntryData(0x4000));
+    ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+  }
+}
+
+TEST_F(ArmExidxExtractTest, second_read_compact_personality_1_2) {
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x8100f3b0);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(2U, data_->size());
+  ASSERT_EQ(0xf3, data_->at(0));
+  ASSERT_EQ(0xb0, data_->at(1));
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x8200f3f4);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(3U, data_->size());
+  ASSERT_EQ(0xf3, data_->at(0));
+  ASSERT_EQ(0xf4, data_->at(1));
+  ASSERT_EQ(0xb0, data_->at(2));
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x8201f3f4);
+  elf_memory_.SetData(0x6238, 0x102030b0);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(6U, data_->size());
+  ASSERT_EQ(0xf3, data_->at(0));
+  ASSERT_EQ(0xf4, data_->at(1));
+  ASSERT_EQ(0x10, data_->at(2));
+  ASSERT_EQ(0x20, data_->at(3));
+  ASSERT_EQ(0x30, data_->at(4));
+  ASSERT_EQ(0xb0, data_->at(5));
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x8103f3f4);
+  elf_memory_.SetData(0x6238, 0x10203040);
+  elf_memory_.SetData(0x623c, 0x50607080);
+  elf_memory_.SetData(0x6240, 0x90a0c0d0);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(15U, data_->size());
+  ASSERT_EQ(0xf3, data_->at(0));
+  ASSERT_EQ(0xf4, data_->at(1));
+  ASSERT_EQ(0x10, data_->at(2));
+  ASSERT_EQ(0x20, data_->at(3));
+  ASSERT_EQ(0x30, data_->at(4));
+  ASSERT_EQ(0x40, data_->at(5));
+  ASSERT_EQ(0x50, data_->at(6));
+  ASSERT_EQ(0x60, data_->at(7));
+  ASSERT_EQ(0x70, data_->at(8));
+  ASSERT_EQ(0x80, data_->at(9));
+  ASSERT_EQ(0x90, data_->at(10));
+  ASSERT_EQ(0xa0, data_->at(11));
+  ASSERT_EQ(0xc0, data_->at(12));
+  ASSERT_EQ(0xd0, data_->at(13));
+  ASSERT_EQ(0xb0, data_->at(14));
+}
+
+TEST_F(ArmExidxExtractTest, second_read_compact_personality_illegal) {
+  elf_memory_.SetData(0x5000, 0x7ffa1e48);
+  elf_memory_.SetData(0x5004, 0x1230);
+  elf_memory_.SetData(0x6234, 0x832132b0);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x7ffa1e48);
+  elf_memory_.SetData(0x5004, 0x1230);
+  elf_memory_.SetData(0x6234, 0x842132b0);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+}
+
+TEST_F(ArmExidxExtractTest, second_read_offset_is_negative) {
+  elf_memory_.SetData(0x5000, 0x7ffa1e48);
+  elf_memory_.SetData(0x5004, 0x7fffb1e0);
+  elf_memory_.SetData(0x1e4, 0x842132b0);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+}
+
+TEST_F(ArmExidxExtractTest, second_read_not_compact) {
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x1);
+  elf_memory_.SetData(0x6238, 0x001122b0);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(3U, data_->size());
+  ASSERT_EQ(0x11, data_->at(0));
+  ASSERT_EQ(0x22, data_->at(1));
+  ASSERT_EQ(0xb0, data_->at(2));
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x2);
+  elf_memory_.SetData(0x6238, 0x00112233);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(4U, data_->size());
+  ASSERT_EQ(0x11, data_->at(0));
+  ASSERT_EQ(0x22, data_->at(1));
+  ASSERT_EQ(0x33, data_->at(2));
+  ASSERT_EQ(0xb0, data_->at(3));
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x3);
+  elf_memory_.SetData(0x6238, 0x01112233);
+  elf_memory_.SetData(0x623c, 0x445566b0);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(7U, data_->size());
+  ASSERT_EQ(0x11, data_->at(0));
+  ASSERT_EQ(0x22, data_->at(1));
+  ASSERT_EQ(0x33, data_->at(2));
+  ASSERT_EQ(0x44, data_->at(3));
+  ASSERT_EQ(0x55, data_->at(4));
+  ASSERT_EQ(0x66, data_->at(5));
+  ASSERT_EQ(0xb0, data_->at(6));
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x3);
+  elf_memory_.SetData(0x6238, 0x05112233);
+  elf_memory_.SetData(0x623c, 0x01020304);
+  elf_memory_.SetData(0x6240, 0x05060708);
+  elf_memory_.SetData(0x6244, 0x090a0b0c);
+  elf_memory_.SetData(0x6248, 0x0d0e0f10);
+  elf_memory_.SetData(0x624c, 0x11121314);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(24U, data_->size());
+  ASSERT_EQ(0x11, data_->at(0));
+  ASSERT_EQ(0x22, data_->at(1));
+  ASSERT_EQ(0x33, data_->at(2));
+  ASSERT_EQ(0x01, data_->at(3));
+  ASSERT_EQ(0x02, data_->at(4));
+  ASSERT_EQ(0x03, data_->at(5));
+  ASSERT_EQ(0x04, data_->at(6));
+  ASSERT_EQ(0x05, data_->at(7));
+  ASSERT_EQ(0x06, data_->at(8));
+  ASSERT_EQ(0x07, data_->at(9));
+  ASSERT_EQ(0x08, data_->at(10));
+  ASSERT_EQ(0x09, data_->at(11));
+  ASSERT_EQ(0x0a, data_->at(12));
+  ASSERT_EQ(0x0b, data_->at(13));
+  ASSERT_EQ(0x0c, data_->at(14));
+  ASSERT_EQ(0x0d, data_->at(15));
+  ASSERT_EQ(0x0e, data_->at(16));
+  ASSERT_EQ(0x0f, data_->at(17));
+  ASSERT_EQ(0x10, data_->at(18));
+  ASSERT_EQ(0x11, data_->at(19));
+  ASSERT_EQ(0x12, data_->at(20));
+  ASSERT_EQ(0x13, data_->at(21));
+  ASSERT_EQ(0x14, data_->at(22));
+  ASSERT_EQ(0xb0, data_->at(23));
+}
+
+TEST_F(ArmExidxExtractTest, read_failures) {
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+
+  elf_memory_.SetData(0x5000, 0x100);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+
+  elf_memory_.SetData(0x5004, 0x100);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+
+  elf_memory_.SetData(0x5104, 0x1);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+
+  elf_memory_.SetData(0x5108, 0x01010203);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+}
+
+TEST_F(ArmExidxExtractTest, malformed) {
+  elf_memory_.SetData(0x5000, 0x100);
+  elf_memory_.SetData(0x5004, 0x100);
+  elf_memory_.SetData(0x5104, 0x1);
+  elf_memory_.SetData(0x5108, 0x06010203);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_MALFORMED, exidx_->status());
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x100);
+  elf_memory_.SetData(0x5004, 0x100);
+  elf_memory_.SetData(0x5104, 0x1);
+  elf_memory_.SetData(0x5108, 0x81060203);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_MALFORMED, exidx_->status());
+}
+
+TEST_F(ArmExidxExtractTest, cant_unwind_log) {
+  elf_memory_.SetData(0x1000, 0x7fff2340);
+  elf_memory_.SetData(0x1004, 1);
+
+  exidx_->set_log(true);
+  exidx_->set_log_indent(0);
+  exidx_->set_log_skip_execution(false);
+
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x1000));
+  ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
+
+  ASSERT_EQ("4 unwind Raw Data: 0x00 0x00 0x00 0x01\n"
+            "4 unwind [cantunwind]\n", GetFakeLogPrint());
+}
+
+TEST_F(ArmExidxExtractTest, raw_data_compact) {
+  elf_memory_.SetData(0x4000, 0x7ffa3000);
+  elf_memory_.SetData(0x4004, 0x80a8b0b0);
+
+  exidx_->set_log(true);
+  exidx_->set_log_indent(0);
+  exidx_->set_log_skip_execution(false);
+
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x4000));
+  ASSERT_EQ("4 unwind Raw Data: 0xa8 0xb0 0xb0\n", GetFakeLogPrint());
+}
+
+TEST_F(ArmExidxExtractTest, raw_data_non_compact) {
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x2);
+  elf_memory_.SetData(0x6238, 0x00112233);
+
+  exidx_->set_log(true);
+  exidx_->set_log_indent(0);
+  exidx_->set_log_skip_execution(false);
+
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ("4 unwind Raw Data: 0x11 0x22 0x33 0xb0\n", GetFakeLogPrint());
+}
diff --git a/libunwindstack/tests/LogFake.cpp b/libunwindstack/tests/LogFake.cpp
new file mode 100644
index 0000000..446cda9
--- /dev/null
+++ b/libunwindstack/tests/LogFake.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015 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 <errno.h>
+#include <stdarg.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+#include <log/logger.h>
+
+#include "LogFake.h"
+
+// Forward declarations.
+class Backtrace;
+struct EventTagMap;
+struct AndroidLogEntry;
+
+std::string g_fake_log_buf;
+
+std::string g_fake_log_print;
+
+void ResetLogs() {
+  g_fake_log_buf = "";
+  g_fake_log_print = "";
+}
+
+std::string GetFakeLogBuf() {
+  return g_fake_log_buf;
+}
+
+std::string GetFakeLogPrint() {
+  return g_fake_log_print;
+}
+
+extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
+  g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
+  g_fake_log_buf += tag;
+  g_fake_log_buf += ' ';
+  g_fake_log_buf += msg;
+  return 1;
+}
+
+extern "C" int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  int val = __android_log_vprint(prio, tag, fmt, ap);
+  va_end(ap);
+
+  return val;
+}
+
+extern "C" int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
+  g_fake_log_print += std::to_string(prio) + ' ';
+  g_fake_log_print += tag;
+  g_fake_log_print += ' ';
+
+  android::base::StringAppendV(&g_fake_log_print, fmt, ap);
+
+  g_fake_log_print += '\n';
+
+  return 1;
+}
+
+extern "C" log_id_t android_name_to_log_id(const char*) {
+  return LOG_ID_SYSTEM;
+}
+
+extern "C" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) {
+  errno = EACCES;
+  return nullptr;
+}
+
+extern "C" int android_logger_list_read(struct logger_list*, struct log_msg*) {
+  return 0;
+}
+
+extern "C" EventTagMap* android_openEventTagMap(const char*) {
+  return nullptr;
+}
+
+extern "C" int android_log_processBinaryLogBuffer(
+    struct logger_entry*,
+    AndroidLogEntry*, const EventTagMap*, char*, int) {
+  return 0;
+}
+
+extern "C" void android_logger_list_free(struct logger_list*) {
+}
diff --git a/libunwindstack/tests/LogFake.h b/libunwindstack/tests/LogFake.h
new file mode 100644
index 0000000..006d393
--- /dev/null
+++ b/libunwindstack/tests/LogFake.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 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 _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
+
+#include <string>
+
+void ResetLogs();
+std::string GetFakeLogBuf();
+std::string GetFakeLogPrint();
+
+#endif  // _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
new file mode 100644
index 0000000..216873f
--- /dev/null
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2016 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 <sys/mman.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Maps.h"
+
+#include "LogFake.h"
+
+class MapsTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+  }
+};
+
+TEST_F(MapsTest, parse_permissions) {
+  MapsBuffer maps("1000-2000 ---- 00000000 00:00 0\n"
+                  "2000-3000 r--- 00000000 00:00 0\n"
+                  "3000-4000 -w-- 00000000 00:00 0\n"
+                  "4000-5000 --x- 00000000 00:00 0\n"
+                  "5000-6000 rwx- 00000000 00:00 0\n");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(5U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ(PROT_NONE, it->flags);
+  ASSERT_EQ(0x1000U, it->start);
+  ASSERT_EQ(0x2000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_READ, it->flags);
+  ASSERT_EQ(0x2000U, it->start);
+  ASSERT_EQ(0x3000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_WRITE, it->flags);
+  ASSERT_EQ(0x3000U, it->start);
+  ASSERT_EQ(0x4000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_EXEC, it->flags);
+  ASSERT_EQ(0x4000U, it->start);
+  ASSERT_EQ(0x5000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, it->flags);
+  ASSERT_EQ(0x5000U, it->start);
+  ASSERT_EQ(0x6000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(it, maps.end());
+}
+
+TEST_F(MapsTest, parse_name) {
+  MapsBuffer maps("720b29b000-720b29e000 rw-p 00000000 00:00 0\n"
+                  "720b29e000-720b29f000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+                  "720b29f000-720b2a0000 rw-p 00000000 00:00 0");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(3U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ("", it->name);
+  ASSERT_EQ(0x720b29b000U, it->start);
+  ASSERT_EQ(0x720b29e000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ++it;
+  ASSERT_EQ("/system/lib/fake.so", it->name);
+  ASSERT_EQ(0x720b29e000U, it->start);
+  ASSERT_EQ(0x720b29f000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ++it;
+  ASSERT_EQ("", it->name);
+  ASSERT_EQ(0x720b29f000U, it->start);
+  ASSERT_EQ(0x720b2a0000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ++it;
+  ASSERT_EQ(it, maps.end());
+}
+
+TEST_F(MapsTest, parse_offset) {
+  MapsBuffer maps("a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+                  "e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(2U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(0xa000U, it->start);
+  ASSERT_EQ(0xe000U, it->end);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ASSERT_EQ("/system/lib/fake.so", it->name);
+  ++it;
+  ASSERT_EQ(0xa12345U, it->offset);
+  ASSERT_EQ(0xe000U, it->start);
+  ASSERT_EQ(0xf000U, it->end);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ASSERT_EQ("/system/lib/fake.so", it->name);
+  ++it;
+  ASSERT_EQ(maps.end(), it);
+}
+
+TEST_F(MapsTest, file_smoke) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_TRUE(android::base::WriteStringToFile(
+      "720b29b000-720b29e000 r-xp a0000000 00:00 0   /fake.so\n"
+      "720b2b0000-720b2e0000 r-xp b0000000 00:00 0   /fake2.so\n"
+      "720b2e0000-720b2f0000 r-xp c0000000 00:00 0   /fake3.so\n",
+      tf.path, 0660, getuid(), getgid()));
+
+  MapsFile maps(tf.path);
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(3U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ(0x720b29b000U, it->start);
+  ASSERT_EQ(0x720b29e000U, it->end);
+  ASSERT_EQ(0xa0000000U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+  ASSERT_EQ("/fake.so", it->name);
+  ++it;
+  ASSERT_EQ(0x720b2b0000U, it->start);
+  ASSERT_EQ(0x720b2e0000U, it->end);
+  ASSERT_EQ(0xb0000000U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+  ASSERT_EQ("/fake2.so", it->name);
+  ++it;
+  ASSERT_EQ(0x720b2e0000U, it->start);
+  ASSERT_EQ(0x720b2f0000U, it->end);
+  ASSERT_EQ(0xc0000000U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+  ASSERT_EQ("/fake3.so", it->name);
+  ++it;
+  ASSERT_EQ(it, maps.end());
+}
+
+TEST_F(MapsTest, find) {
+  MapsBuffer maps("1000-2000 r--p 00000010 00:00 0 /system/lib/fake1.so\n"
+                  "3000-4000 -w-p 00000020 00:00 0 /system/lib/fake2.so\n"
+                  "6000-8000 --xp 00000030 00:00 0 /system/lib/fake3.so\n"
+                  "a000-b000 rw-p 00000040 00:00 0 /system/lib/fake4.so\n"
+                  "e000-f000 rwxp 00000050 00:00 0 /system/lib/fake5.so\n");
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(5U, maps.Total());
+
+  ASSERT_TRUE(maps.Find(0x500) == nullptr);
+  ASSERT_TRUE(maps.Find(0x2000) == nullptr);
+  ASSERT_TRUE(maps.Find(0x5010) == nullptr);
+  ASSERT_TRUE(maps.Find(0x9a00) == nullptr);
+  ASSERT_TRUE(maps.Find(0xf000) == nullptr);
+  ASSERT_TRUE(maps.Find(0xf010) == nullptr);
+
+  MapInfo* info = maps.Find(0x1000);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0x1000U, info->start);
+  ASSERT_EQ(0x2000U, info->end);
+  ASSERT_EQ(0x10U, info->offset);
+  ASSERT_EQ(PROT_READ, info->flags);
+  ASSERT_EQ("/system/lib/fake1.so", info->name);
+
+  info = maps.Find(0x3020);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0x3000U, info->start);
+  ASSERT_EQ(0x4000U, info->end);
+  ASSERT_EQ(0x20U, info->offset);
+  ASSERT_EQ(PROT_WRITE, info->flags);
+  ASSERT_EQ("/system/lib/fake2.so", info->name);
+
+  info = maps.Find(0x6020);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0x6000U, info->start);
+  ASSERT_EQ(0x8000U, info->end);
+  ASSERT_EQ(0x30U, info->offset);
+  ASSERT_EQ(PROT_EXEC, info->flags);
+  ASSERT_EQ("/system/lib/fake3.so", info->name);
+
+  info = maps.Find(0xafff);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0xa000U, info->start);
+  ASSERT_EQ(0xb000U, info->end);
+  ASSERT_EQ(0x40U, info->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, info->flags);
+  ASSERT_EQ("/system/lib/fake4.so", info->name);
+
+  info = maps.Find(0xe500);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0xe000U, info->start);
+  ASSERT_EQ(0xf000U, info->end);
+  ASSERT_EQ(0x50U, info->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
+  ASSERT_EQ("/system/lib/fake5.so", info->name);
+}
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
new file mode 100644
index 0000000..afb1029
--- /dev/null
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 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 <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "MemoryFake.h"
+
+void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
+  const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
+  for (size_t i = 0; i < length; i++, addr++) {
+    auto value = data_.find(addr);
+    if (value != data_.end()) {
+      value->second = src[i];
+    } else {
+      data_.insert({ addr, src[i] });
+    }
+  }
+}
+
+bool MemoryFake::Read(uint64_t addr, void* memory, size_t size) {
+  uint8_t* dst = reinterpret_cast<uint8_t*>(memory);
+  for (size_t i = 0; i < size; i++, addr++) {
+    auto value = data_.find(addr);
+    if (value == data_.end()) {
+      return false;
+    }
+    dst[i] = value->second;
+  }
+  return true;
+}
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
new file mode 100644
index 0000000..4f898fa
--- /dev/null
+++ b/libunwindstack/tests/MemoryFake.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+#include "Memory.h"
+
+class MemoryFake : public Memory {
+ public:
+  MemoryFake() = default;
+  virtual ~MemoryFake() = default;
+
+  bool Read(uint64_t addr, void* buffer, size_t size) override;
+
+  void SetMemory(uint64_t addr, const void* memory, size_t length);
+
+  void SetData(uint64_t addr, uint32_t value) {
+    SetMemory(addr, &value, sizeof(value));
+  }
+
+  void SetMemory(uint64_t addr, std::vector<uint8_t> values) {
+    SetMemory(addr, values.data(), values.size());
+  }
+
+  void SetMemory(uint64_t addr, std::string string) {
+    SetMemory(addr, string.c_str(), string.size() + 1);
+  }
+
+  void Clear() { data_.clear(); }
+
+ private:
+  std::unordered_map<uint64_t, uint8_t> data_;
+};
+
+class MemoryFakeAlwaysReadZero : public Memory {
+ public:
+  MemoryFakeAlwaysReadZero() = default;
+  virtual ~MemoryFakeAlwaysReadZero() = default;
+
+  bool Read(uint64_t, void* buffer, size_t size) override {
+    memset(buffer, 0, size);
+    return true;
+  }
+};
+
+#endif  // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
new file mode 100644
index 0000000..ebc6118
--- /dev/null
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2016 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 <android-base/test_utils.h>
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+
+class MemoryFileTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+    tf_ = new TemporaryFile;
+  }
+
+  void TearDown() override {
+    delete tf_;
+  }
+
+  void WriteTestData() {
+    ASSERT_TRUE(android::base::WriteStringToFd("0123456789abcdefghijklmnopqrstuvxyz", tf_->fd));
+  }
+
+  MemoryFileAtOffset memory_;
+
+  TemporaryFile* tf_ = nullptr;
+};
+
+TEST_F(MemoryFileTest, offset_0) {
+  WriteTestData();
+
+  ASSERT_TRUE(memory_.Init(tf_->path, 0));
+  std::vector<char> buffer(11);
+  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  buffer[10] = '\0';
+  ASSERT_STREQ("0123456789", buffer.data());
+}
+
+TEST_F(MemoryFileTest, offset_non_zero) {
+  WriteTestData();
+
+  ASSERT_TRUE(memory_.Init(tf_->path, 10));
+  std::vector<char> buffer(11);
+  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  buffer[10] = '\0';
+  ASSERT_STREQ("abcdefghij", buffer.data());
+}
+
+TEST_F(MemoryFileTest, offset_non_zero_larger_than_pagesize) {
+  size_t pagesize = getpagesize();
+  std::string large_string;
+  for (size_t i = 0; i < pagesize; i++) {
+    large_string += '1';
+  }
+  large_string += "012345678901234abcdefgh";
+  ASSERT_TRUE(android::base::WriteStringToFd(large_string, tf_->fd));
+
+  ASSERT_TRUE(memory_.Init(tf_->path, pagesize + 15));
+  std::vector<char> buffer(9);
+  ASSERT_TRUE(memory_.Read(0, buffer.data(), 8));
+  buffer[8] = '\0';
+  ASSERT_STREQ("abcdefgh", buffer.data());
+}
+
+TEST_F(MemoryFileTest, offset_pagesize_aligned) {
+  size_t pagesize = getpagesize();
+  std::string data;
+  for (size_t i = 0; i < 2 * pagesize; i++) {
+    data += static_cast<char>((i / pagesize) + '0');
+    data += static_cast<char>((i % 10) + '0');
+  }
+  ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+  ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize));
+  std::vector<char> buffer(11);
+  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  buffer[10] = '\0';
+  std::string expected_str;
+  for (size_t i = 0; i < 5; i++) {
+    expected_str += '1';
+    expected_str += static_cast<char>(((i + pagesize) % 10) + '0');
+  }
+  ASSERT_STREQ(expected_str.c_str(), buffer.data());
+}
+
+TEST_F(MemoryFileTest, offset_pagesize_aligned_plus_extra) {
+  size_t pagesize = getpagesize();
+  std::string data;
+  for (size_t i = 0; i < 2 * pagesize; i++) {
+    data += static_cast<char>((i / pagesize) + '0');
+    data += static_cast<char>((i % 10) + '0');
+  }
+  ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+  ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize + 10));
+  std::vector<char> buffer(11);
+  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  buffer[10] = '\0';
+  std::string expected_str;
+  for (size_t i = 0; i < 5; i++) {
+    expected_str += '1';
+    expected_str += static_cast<char>(((i + pagesize + 5) % 10) + '0');
+  }
+  ASSERT_STREQ(expected_str.c_str(), buffer.data());
+}
+
+TEST_F(MemoryFileTest, read_error) {
+  std::string data;
+  for (size_t i = 0; i < 5000; i++) {
+    data += static_cast<char>((i % 10) + '0');
+  }
+  ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+
+  std::vector<char> buffer(100);
+
+  // Read before init.
+  ASSERT_FALSE(memory_.Read(0, buffer.data(), 10));
+
+  ASSERT_TRUE(memory_.Init(tf_->path, 0));
+
+  ASSERT_FALSE(memory_.Read(10000, buffer.data(), 10));
+  ASSERT_FALSE(memory_.Read(5000, buffer.data(), 10));
+  ASSERT_FALSE(memory_.Read(4990, buffer.data(), 11));
+  ASSERT_TRUE(memory_.Read(4990, buffer.data(), 10));
+  ASSERT_FALSE(memory_.Read(4999, buffer.data(), 2));
+  ASSERT_TRUE(memory_.Read(4999, buffer.data(), 1));
+}
+
+TEST_F(MemoryFileTest, read_string) {
+  std::string value("name_in_file");
+  ASSERT_TRUE(android::base::WriteFully(tf_->fd, value.c_str(), value.size() + 1));
+
+  std::string name;
+  ASSERT_TRUE(memory_.Init(tf_->path, 0));
+  ASSERT_TRUE(memory_.ReadString(0, &name));
+  ASSERT_EQ("name_in_file", name);
+  ASSERT_TRUE(memory_.ReadString(5, &name));
+  ASSERT_EQ("in_file", name);
+}
+
+TEST_F(MemoryFileTest, read_string_error) {
+  std::vector<uint8_t> buffer = { 0x23, 0x32, 0x45 };
+  ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size()));
+
+  std::string name;
+  ASSERT_TRUE(memory_.Init(tf_->path, 0));
+
+  // Read from a non-existant address.
+  ASSERT_FALSE(memory_.ReadString(100, &name));
+
+  // This should fail because there is no terminating \0
+  ASSERT_FALSE(memory_.ReadString(0, &name));
+}
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
new file mode 100644
index 0000000..49ece9d
--- /dev/null
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+#include <string.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+
+class MemoryLocalTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+  }
+};
+
+TEST_F(MemoryLocalTest, read) {
+  std::vector<uint8_t> src(1024);
+  memset(src.data(), 0x4c, 1024);
+
+  MemoryLocal local;
+
+  std::vector<uint8_t> dst(1024);
+  ASSERT_TRUE(local.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+  ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024));
+  for (size_t i = 0; i < 1024; i++) {
+    ASSERT_EQ(0x4cU, dst[i]);
+  }
+
+  memset(src.data(), 0x23, 512);
+  ASSERT_TRUE(local.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+  ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024));
+  for (size_t i = 0; i < 512; i++) {
+    ASSERT_EQ(0x23U, dst[i]);
+  }
+  for (size_t i = 512; i < 1024; i++) {
+    ASSERT_EQ(0x4cU, dst[i]);
+  }
+}
+
+TEST_F(MemoryLocalTest, read_string) {
+  std::string name("string_in_memory");
+
+  MemoryLocal local;
+
+  std::vector<uint8_t> dst(1024);
+  std::string dst_name;
+  ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(name.c_str()), &dst_name));
+  ASSERT_EQ("string_in_memory", dst_name);
+
+  ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name));
+  ASSERT_EQ("in_memory", dst_name);
+
+  ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 10));
+  ASSERT_EQ("in_memory", dst_name);
+
+  ASSERT_FALSE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 9));
+}
+
+TEST_F(MemoryLocalTest, read_illegal) {
+  MemoryLocal local;
+
+  std::vector<uint8_t> dst(100);
+  ASSERT_FALSE(local.Read(0, dst.data(), 1));
+  ASSERT_FALSE(local.Read(0, dst.data(), 100));
+}
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
new file mode 100644
index 0000000..fcae3a4
--- /dev/null
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+#include <string.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+class MemoryRangeTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+    memory_ = new MemoryFake;
+  }
+
+  MemoryFake* memory_;
+};
+
+TEST_F(MemoryRangeTest, read) {
+  std::vector<uint8_t> src(1024);
+  memset(src.data(), 0x4c, 1024);
+  memory_->SetMemory(9001, src);
+
+  MemoryRange range(memory_, 9001, 9001 + src.size());
+
+  std::vector<uint8_t> dst(1024);
+  ASSERT_TRUE(range.Read(0, dst.data(), src.size()));
+  for (size_t i = 0; i < 1024; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
+}
+
+TEST_F(MemoryRangeTest, read_near_limit) {
+  std::vector<uint8_t> src(4096);
+  memset(src.data(), 0x4c, 4096);
+  memory_->SetMemory(1000, src);
+
+  MemoryRange range(memory_, 1000, 2024);
+
+  std::vector<uint8_t> dst(1024);
+  ASSERT_TRUE(range.Read(1020, dst.data(), 4));
+  for (size_t i = 0; i < 4; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
+
+  // Verify that reads outside of the range will fail.
+  ASSERT_FALSE(range.Read(1020, dst.data(), 5));
+  ASSERT_FALSE(range.Read(1024, dst.data(), 1));
+  ASSERT_FALSE(range.Read(1024, dst.data(), 1024));
+}
+
+TEST_F(MemoryRangeTest, read_string_past_end) {
+  std::string name("0123456789");
+  memory_->SetMemory(0, name);
+
+  // Verify a read past the range fails.
+  MemoryRange range(memory_, 0, 5);
+  std::string dst_name;
+  ASSERT_FALSE(range.ReadString(0, &dst_name));
+}
+
+TEST_F(MemoryRangeTest, read_string_to_end) {
+  std::string name("0123456789");
+  memory_->SetMemory(30, name);
+
+  // Verify the range going to the end of the string works.
+  MemoryRange range(memory_, 30, 30 + name.size() + 1);
+  std::string dst_name;
+  ASSERT_TRUE(range.ReadString(0, &dst_name));
+  ASSERT_EQ("0123456789", dst_name);
+}
+
+TEST_F(MemoryRangeTest, read_string_fencepost) {
+  std::string name("0123456789");
+  memory_->SetMemory(10, name);
+
+  // Verify the range set to one byte less than the end of the string fails.
+  MemoryRange range(memory_, 10, 10 + name.size());
+  std::string dst_name;
+  ASSERT_FALSE(range.ReadString(0, &dst_name));
+}
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
new file mode 100644
index 0000000..49244a5
--- /dev/null
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 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 <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <android-base/test_utils.h>
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+
+class MemoryRemoteTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+  }
+
+  static uint64_t NanoTime() {
+    struct timespec t = { 0, 0 };
+    clock_gettime(CLOCK_MONOTONIC, &t);
+    return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
+  }
+
+  static bool Attach(pid_t pid) {
+    if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+      return false;
+    }
+
+    uint64_t start = NanoTime();
+    siginfo_t si;
+    while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
+      if ((NanoTime() - start) > 10 * NS_PER_SEC) {
+        printf("%d: Failed to stop after 10 seconds.\n", pid);
+        return false;
+      }
+      usleep(30);
+    }
+    return true;
+  }
+
+  static bool Detach(pid_t pid) {
+    return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
+  }
+
+  static constexpr size_t NS_PER_SEC = 1000000000ULL;
+};
+
+TEST_F(MemoryRemoteTest, read) {
+  std::vector<uint8_t> src(1024);
+  memset(src.data(), 0x4c, 1024);
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true);
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
+
+  std::vector<uint8_t> dst(1024);
+  ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+  for (size_t i = 0; i < 1024; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_TRUE(Detach(pid));
+
+  kill(pid, SIGKILL);
+}
+
+TEST_F(MemoryRemoteTest, read_fail) {
+  int pagesize = getpagesize();
+  void* src = mmap(nullptr, pagesize * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,-1, 0);
+  memset(src, 0x4c, pagesize * 2);
+  ASSERT_NE(MAP_FAILED, src);
+  // Put a hole right after the first page.
+  ASSERT_EQ(0, munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(src) + pagesize),
+                      pagesize));
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true);
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
+
+  std::vector<uint8_t> dst(pagesize);
+  ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src), dst.data(), pagesize));
+  for (size_t i = 0; i < 1024; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize, dst.data(), 1));
+  ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 1, dst.data(), 1));
+  ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 4, dst.data(), 8));
+
+  ASSERT_EQ(0, munmap(src, pagesize));
+
+  ASSERT_TRUE(Detach(pid));
+
+  kill(pid, SIGKILL);
+}
+
+TEST_F(MemoryRemoteTest, read_illegal) {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true);
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
+
+  std::vector<uint8_t> dst(100);
+  ASSERT_FALSE(remote.Read(0, dst.data(), 1));
+  ASSERT_FALSE(remote.Read(0, dst.data(), 100));
+
+  ASSERT_TRUE(Detach(pid));
+
+  kill(pid, SIGKILL);
+}
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
new file mode 100644
index 0000000..f9e8b0e
--- /dev/null
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include "Regs.h"
+
+class RegsTest : public ::testing::Test {};
+
+TEST_F(RegsTest, regs32) {
+  Regs32 regs32(10, 20, 30);
+
+  ASSERT_EQ(10U, regs32.pc_reg());
+  ASSERT_EQ(20U, regs32.sp_reg());
+  ASSERT_EQ(30U, regs32.total_regs());
+
+  uint32_t* raw = reinterpret_cast<uint32_t*>(regs32.raw_data());
+  for (size_t i = 0; i < 30; i++) {
+    raw[i] = 0xf0000000 + i;
+  }
+
+  ASSERT_EQ(0xf000000aU, regs32.pc());
+  ASSERT_EQ(0xf0000014U, regs32.sp());
+
+  ASSERT_EQ(0xf0000001U, regs32[1]);
+  regs32[1] = 10;
+  ASSERT_EQ(10U, regs32[1]);
+
+  ASSERT_EQ(0xf000001dU, regs32[29]);
+}
+
+TEST_F(RegsTest, regs64) {
+  Regs64 regs64(10, 20, 30);
+
+  ASSERT_EQ(10U, regs64.pc_reg());
+  ASSERT_EQ(20U, regs64.sp_reg());
+  ASSERT_EQ(30U, regs64.total_regs());
+
+  uint64_t* raw = reinterpret_cast<uint64_t*>(regs64.raw_data());
+  for (size_t i = 0; i < 30; i++) {
+    raw[i] = 0xf123456780000000UL + i;
+  }
+
+  ASSERT_EQ(0xf12345678000000aUL, regs64.pc());
+  ASSERT_EQ(0xf123456780000014UL, regs64.sp());
+
+  ASSERT_EQ(0xf123456780000008U, regs64[8]);
+  regs64[8] = 10;
+  ASSERT_EQ(10U, regs64[8]);
+
+  ASSERT_EQ(0xf12345678000001dU, regs64[29]);
+}