Merge "Add OWNERS file to fastdeploy so idries@ can approve changes"
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 577e336..d79d20b 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -348,8 +348,16 @@
   return vm_pid;
 }
 
+static void InstallSigPipeHandler() {
+  struct sigaction action = {};
+  action.sa_handler = SIG_IGN;
+  action.sa_flags = SA_RESTART;
+  sigaction(SIGPIPE, &action, nullptr);
+}
+
 int main(int argc, char** argv) {
   DefuseSignalHandlers();
+  InstallSigPipeHandler();
 
   atrace_begin(ATRACE_TAG, "before reparent");
   pid_t target_process = getppid();
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index e066bff..0978ec1 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -162,9 +162,17 @@
         // clang-format on
 };
 
-static std::string find_item_given_name(const std::string& img_name) {
+static char* get_android_product_out() {
     char* dir = getenv("ANDROID_PRODUCT_OUT");
     if (dir == nullptr || dir[0] == '\0') {
+        return nullptr;
+    }
+    return dir;
+}
+
+static std::string find_item_given_name(const std::string& img_name) {
+    char* dir = get_android_product_out();
+    if (!dir) {
         die("ANDROID_PRODUCT_OUT not set");
     }
     return std::string(dir) + "/" + img_name;
@@ -1466,15 +1474,13 @@
             fprintf(stderr, "File system type %s not supported.\n", partition_type.c_str());
             return;
         }
-        fprintf(stderr, "Formatting is not supported for file system with type '%s'.\n",
-                partition_type.c_str());
-        return;
+        die("Formatting is not supported for file system with type '%s'.",
+            partition_type.c_str());
     }
 
     int64_t size;
     if (!android::base::ParseInt(partition_size, &size)) {
-        fprintf(stderr, "Couldn't parse partition size '%s'.\n", partition_size.c_str());
-        return;
+        die("Couldn't parse partition size '%s'.", partition_size.c_str());
     }
 
     unsigned eraseBlkSize, logicalBlkSize;
@@ -1484,17 +1490,14 @@
     if (fs_generator_generate(gen, output.path, size, initial_dir,
             eraseBlkSize, logicalBlkSize)) {
         die("Cannot generate image for %s", partition.c_str());
-        return;
     }
 
     fd.reset(open(output.path, O_RDONLY));
     if (fd == -1) {
-        fprintf(stderr, "Cannot open generated image: %s\n", strerror(errno));
-        return;
+        die("Cannot open generated image: %s", strerror(errno));
     }
     if (!load_buf_fd(fd.release(), &buf)) {
-        fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
-        return;
+        die("Cannot read image: %s", strerror(errno));
     }
     flash_buf(partition, &buf);
     return;
@@ -1505,9 +1508,15 @@
         if (errMsg) fprintf(stderr, "%s", errMsg);
     }
     fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
+    if (!skip_if_not_supported) {
+        die("Command failed");
+    }
 }
 
 static bool should_flash_in_userspace(const std::string& partition_name) {
+    if (!get_android_product_out()) {
+        return false;
+    }
     auto path = find_item_given_name("super_empty.img");
     if (path.empty()) {
         return false;
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 8e57e1e..6310238 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1603,9 +1603,6 @@
         } else if (slot == -1) {
             suffix = fs_mgr_get_slot_suffix();
         }
-        if (suffix.empty()) {
-            LFATAL << "Super partition name can only be overridden on A/B devices.";
-        }
         return super_partition + suffix;
     }
     return LP_METADATA_DEFAULT_PARTITION_NAME;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index a1de005..bef46e1 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -432,7 +432,7 @@
         if (change) *change = true;
         if (!DestroyLogicalPartition(partition_name, 0s)) return false;
     } else {
-        PERROR << "delete partition " << overlay;
+        LERROR << "delete partition " << overlay;
         return false;
     }
     errno = save_errno;
@@ -647,49 +647,65 @@
     if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
     auto mnt_type = fs_mgr_overlayfs_scratch_mount_type();
     auto scratch_device = fs_mgr_overlayfs_scratch_device();
-    auto partition_exists = fs_mgr_rw_access(scratch_device);
+    auto partition_create = !fs_mgr_rw_access(scratch_device);
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+    if (!fs_mgr_rw_access(super_device)) return false;
+    if (!fs_mgr_overlayfs_has_logical(fstab)) return false;
+    auto builder = MetadataBuilder::New(super_device, slot_number);
+    if (!builder) {
+        LERROR << "open " << super_device << " metadata";
+        return false;
+    }
+    const auto partition_name = android::base::Basename(kScratchMountPoint);
+    auto partition = builder->FindPartition(partition_name);
+    auto partition_exists = partition != nullptr;
+    auto changed = false;
     if (!partition_exists) {
-        auto slot_number = fs_mgr_overlayfs_slot_number();
-        auto super_device = fs_mgr_overlayfs_super_device(slot_number);
-        if (!fs_mgr_rw_access(super_device)) return false;
-        if (!fs_mgr_overlayfs_has_logical(fstab)) return false;
-        auto builder = MetadataBuilder::New(super_device, slot_number);
-        if (!builder) {
-            PERROR << "open " << super_device << " metadata";
+        partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
+        if (!partition) {
+            LERROR << "create " << partition_name;
             return false;
         }
-        const auto partition_name = android::base::Basename(kScratchMountPoint);
-        partition_exists = builder->FindPartition(partition_name) != nullptr;
-        if (!partition_exists) {
-            auto partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
-            if (!partition) {
-                PERROR << "create " << partition_name;
-                return false;
+        changed = true;
+    }
+    // Take half of free space, minimum 512MB or free space - 256KB margin.
+    static constexpr auto kMinimumSize = uint64_t(512 * 1024 * 1024);
+    static constexpr auto kMarginSize = uint64_t(256 * 1024);
+    if (partition->size() < kMinimumSize) {
+        auto partition_size =
+                builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
+        if ((partition_size > kMinimumSize) || !partition->size()) {
+            partition_size = std::max(std::min(kMinimumSize, partition_size - kMarginSize),
+                                      partition_size / 2);
+            if (partition_size > partition->size()) {
+                if (!builder->ResizePartition(partition, partition_size)) {
+                    LERROR << "resize " << partition_name;
+                    return false;
+                }
+                if (!partition_create) DestroyLogicalPartition(partition_name, 10s);
+                changed = true;
+                partition_exists = false;
             }
-            auto partition_size = builder->AllocatableSpace() - builder->UsedSpace();
-            // 512MB or half the remaining available space, whichever is greater.
-            partition_size = std::max(uint64_t(512 * 1024 * 1024), partition_size / 2);
-            if (!builder->ResizePartition(partition, partition_size)) {
-                PERROR << "resize " << partition_name;
-                return false;
-            }
-
-            auto metadata = builder->Export();
-            if (!metadata) {
-                LERROR << "generate new metadata " << partition_name;
-                return false;
-            }
-            if (!UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
-                LERROR << "update " << partition_name;
-                return false;
-            }
-
-            if (change) *change = true;
+        }
+    }
+    // land the update back on to the partition
+    if (changed) {
+        auto metadata = builder->Export();
+        if (!metadata || !UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
+            LERROR << "add partition " << partition_name;
+            return false;
         }
 
+        if (change) *change = true;
+    }
+
+    if (changed || partition_create) {
         if (!CreateLogicalPartition(super_device, slot_number, partition_name, true, 0s,
                                     &scratch_device))
             return false;
+
+        if (change) *change = true;
     }
 
     if (partition_exists) {
@@ -709,6 +725,7 @@
     } else if (mnt_type == "ext4") {
         command = kMkExt4 + " -b 4096 -t ext4 -m 0 -O has_journal -M " + kScratchMountPoint;
     } else {
+        errno = ESRCH;
         LERROR << mnt_type << " has no mkfs cookbook";
         return false;
     }
@@ -773,6 +790,8 @@
 }
 
 std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab) {
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return {};
+
     if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), kScratchMountPoint)) {
         return {};
     }
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 7a00194..d9e017c 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -63,6 +63,7 @@
 
 Returns: the logcat output" ]
 adb_logcat() {
+  echo "${RED}[     INFO ]${NORMAL} logcat ${@}" >&2 &&
   adb logcat "${@}" </dev/null |
     grep -v 'logd    : logdr: UID=' |
     sed -e '${/------- beginning of kernel/d}' -e 's/^[0-1][0-9]-[0-3][0-9] //'
@@ -106,12 +107,13 @@
 
 Returns: true if the reboot command succeeded" ]
 adb_reboot() {
-  adb reboot remount-test
+  adb reboot remount-test &&
+  sleep 2
 }
 
 [ "USAGE: adb_wait [timeout]
 
-Returns: waits until the device has returned or the optional timeout" ]
+Returns: waits until the device has returned for adb or optional timeout" ]
 adb_wait() {
   if [ -n "${1}" ]; then
     timeout --preserve-status --signal=KILL ${1} adb wait-for-device
@@ -120,14 +122,49 @@
   fi
 }
 
+[ "USAGE: fastboot_wait [timeout]
+
+Returns: waits until the device has returned for fastboot or optional timeout" ]
+fastboot_wait() {
+  # fastboot has no wait-for-device, but it does an automatic
+  # wait and requires (even a nonsensical) command to do so.
+  if [ -n "${1}" ]; then
+    timeout --preserve-status --signal=KILL ${1} fastboot wait-for-device
+  else
+    fastboot wait-for-device >/dev/null
+  fi >/dev/null 2>/dev/null ||
+    inFastboot
+}
+
 [ "USAGE: adb_root
 
 Returns: true if device in root state" ]
 adb_root() {
-  adb root >/dev/null </dev/null 2>/dev/null &&
-  sleep 1 &&
-  adb_wait &&
-  sleep 1
+  adb root >/dev/null </dev/null 2>/dev/null
+  sleep 2
+  adb_wait 2m &&
+    [ `adb_sh echo '${USER}'` = root ]
+}
+
+[ "USAGE: fastboot_getvar var expected
+
+Returns: true if var output matches expected" ]
+fastboot_getvar() {
+  O=`fastboot getvar ${1} 2>&1`
+  err=${?}
+  O="${O#< waiting for * >?}"
+  O="${O%%?Finished. Total time: *}"
+  if [ 0 -ne ${err} ]; then
+    echo ${O} >&2
+    false
+    return
+  fi
+  if [ -n "${2}" -a "${1}: ${2}" != "${O}" ]; then
+    echo "${2} != ${O}" >&2
+    false
+    return
+  fi
+  echo ${O} >&2
 }
 
 [ "USAGE: die [-t <epoch>] [message] >/dev/stderr
@@ -233,8 +270,7 @@
 adb_su ls /sys/module/overlay/parameters/override_creds </dev/null >/dev/null &&
   echo "${GREEN}[       OK ]${NORMAL} overlay module supports override_creds" >&2 ||
   die "overlay module can not be used on ANDROID"
-adb_root &&
-  adb_wait ||
+adb_root ||
   die "initial setup"
 reboot=false
 OVERLAYFS_BACKING="cache mnt/scratch"
@@ -250,8 +286,7 @@
   echo "${ORANGE}[  WARNING ]${NORMAL} rebooting before test" >&2
   adb_reboot &&
     adb_wait 2m &&
-    adb_root &&
-    adb_wait ||
+    adb_root ||
     die "reboot after wipe"
 fi
 D=`adb_sh df -k </dev/null` &&
@@ -288,7 +323,6 @@
 
 T=`adb_date`
 adb_root &&
-  adb_wait &&
   adb remount &&
   D=`adb_sh df -k </dev/null` ||
   die -t ${T} "can not collect filesystem data"
@@ -340,19 +374,31 @@
   die "re-read vendor hello after reboot w/o root"
 check_eq "cat: /vendor/hello: Permission denied" "${B}" vendor after reboot w/o root
 adb_root &&
-  adb_wait &&
   B="`adb_cat /vendor/hello`" ||
   die "re-read vendor hello after reboot"
 check_eq "${A}" "${B}" vendor after reboot
 
 adb reboot-fastboot &&
-  fastboot flash vendor &&
-  fastboot reboot ||
+  fastboot_wait 2m &&
+  fastboot flash vendor ||
   die "fastbootd flash vendor"
+# check scratch via fastboot
+fastboot_getvar partition-type:scratch raw &&
+  fastboot_getvar has-slot:scratch no &&
+  fastboot_getvar is-logical:scratch yes ||
+  die "fastboot can not see scratch parameters"
+echo "${ORANGE}[     INFO ]${NORMAL} expect fastboot erase scratch to fail" >&2
+fastboot erase scratch &&
+  die "fastbootd can erase scratch"
+echo "${ORANGE}[     INFO ]${NORMAL} expect fastboot format scratch to fail" >&2
+fastboot format scratch &&
+  die "fastbootd can format scratch"
+fastboot reboot ||
+  die "can not reboot out of fastbootd"
+echo "${ORANGE}[  WARNING ]${NORMAL} adb after fastboot ... waiting 2 minutes"
 adb_wait 2m ||
   die "did not reboot after flash"
 adb_root &&
-  adb_wait &&
   D=`adb_sh df -k </dev/null` &&
   H=`echo "${D}" | head -1` &&
   D=`echo "${D}" | grep "^overlay "` &&
@@ -365,8 +411,7 @@
 B="`adb_cat /system/hello`" ||
   die "re-read system hello after flash vendor"
 check_eq "${A}" "${B}" system after flash vendor
-adb_root &&
-  adb_wait ||
+adb_root ||
   die "adb root"
 B="`adb_cat /vendor/hello`" &&
   die "re-read vendor hello after flash vendor"
@@ -384,4 +429,27 @@
   die "re-read vendor hello after rm"
 check_eq "cat: /vendor/hello: No such file or directory" "${B}" after flash rm
 
+adb reboot-fastboot &&
+  dd if=/dev/zero of=adb-remount-test.img bs=4096 count=16 &&
+  fastboot_wait 2m ||
+  die "reboot into fastbootd"
+fastboot flash scratch adb-remount-test.img
+err=${?}
+rm adb-remount-test.img
+[ 0 -eq ${err} ] ||
+  die "fastbootd flash scratch"
+fastboot reboot ||
+  die "can not reboot out of fastbootd"
+adb_wait 2m &&
+  adb_root ||
+  die "did not reboot after flash"
+T=`adb_date`
+D=`adb disable-verity 2>&1`
+err=${?}
+echo "${D}"
+[ ${err} = 0 ] &&
+  [ X"${D}" = X"${D##*setup failed}" ] &&
+  [ X"${D}" != X"${D##*using overlayfs}" ] ||
+  die -t ${T} "setup for overlayfs"
+
 echo "${GREEN}[  PASSED  ]${NORMAL} adb remount" >&2
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 43b2ebf..a3257f5 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -449,7 +449,9 @@
     // Includes the partition names of fstab records and verity_loc_device (if any).
     // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
     for (auto fstab_rec : mount_fstab_recs_) {
-        required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+        if (!fs_mgr_is_logical(fstab_rec)) {
+            required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+        }
     }
 
     if (!verity_loc_device.empty()) {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 14f82c7..a5e0dc9 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -184,6 +184,7 @@
         "tests/MapInfoGetLoadBiasTest.cpp",
         "tests/MapsTest.cpp",
         "tests/MemoryBufferTest.cpp",
+        "tests/MemoryCacheTest.cpp",
         "tests/MemoryFake.cpp",
         "tests/MemoryFileTest.cpp",
         "tests/MemoryLocalTest.cpp",
@@ -310,6 +311,28 @@
     ],
 }
 
+//-------------------------------------------------------------------------
+// Benchmarks
+//-------------------------------------------------------------------------
+cc_benchmark {
+    name: "unwind_benchmarks",
+    host_supported: true,
+    defaults: ["libunwindstack_flags"],
+
+    // Disable optimizations so that all of the calls are not optimized away.
+    cflags: [
+        "-O0",
+    ],
+
+    srcs: [
+        "benchmarks/unwind_benchmarks.cpp",
+    ],
+
+    shared_libs: [
+        "libunwindstack",
+    ],
+}
+
 // Generates the elf data for use in the tests for .gnu_debugdata frames.
 // Once these files are generated, use the xz command to compress the data.
 cc_binary_host {
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index cfa8c6d..a30d65e 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -174,6 +174,13 @@
   return std::shared_ptr<Memory>(new MemoryRemote(pid));
 }
 
+std::shared_ptr<Memory> Memory::CreateProcessMemoryCached(pid_t pid) {
+  if (pid == getpid()) {
+    return std::shared_ptr<Memory>(new MemoryCache(new MemoryLocal()));
+  }
+  return std::shared_ptr<Memory>(new MemoryCache(new MemoryRemote(pid)));
+}
+
 size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
   if (addr >= raw_.size()) {
     return 0;
@@ -398,4 +405,50 @@
   return 0;
 }
 
+size_t MemoryCache::Read(uint64_t addr, void* dst, size_t size) {
+  // Only bother caching and looking at the cache if this is a small read for now.
+  if (size > 64) {
+    return impl_->Read(addr, dst, size);
+  }
+
+  uint64_t addr_page = addr >> kCacheBits;
+  auto entry = cache_.find(addr_page);
+  uint8_t* cache_dst;
+  if (entry != cache_.end()) {
+    cache_dst = entry->second;
+  } else {
+    cache_dst = cache_[addr_page];
+    if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) {
+      // Erase the entry.
+      cache_.erase(addr_page);
+      return impl_->Read(addr, dst, size);
+    }
+  }
+  size_t max_read = ((addr_page + 1) << kCacheBits) - addr;
+  if (size <= max_read) {
+    memcpy(dst, &cache_dst[addr & kCacheMask], size);
+    return size;
+  }
+
+  // The read crossed into another cached entry, since a read can only cross
+  // into one extra cached page, duplicate the code rather than looping.
+  memcpy(dst, &cache_dst[addr & kCacheMask], max_read);
+  dst = &reinterpret_cast<uint8_t*>(dst)[max_read];
+  addr_page++;
+
+  entry = cache_.find(addr_page);
+  if (entry != cache_.end()) {
+    cache_dst = entry->second;
+  } else {
+    cache_dst = cache_[addr_page];
+    if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) {
+      // Erase the entry.
+      cache_.erase(addr_page);
+      return impl_->Read(addr_page << kCacheBits, dst, size - max_read) + max_read;
+    }
+  }
+  memcpy(dst, cache_dst, size - max_read);
+  return size;
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/benchmarks/unwind_benchmarks.cpp b/libunwindstack/benchmarks/unwind_benchmarks.cpp
new file mode 100644
index 0000000..db0fb54
--- /dev/null
+++ b/libunwindstack/benchmarks/unwind_benchmarks.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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 <memory>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
+size_t Call6(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+  unwindstack::RegsGetLocal(regs.get());
+  unwindstack::Unwinder unwinder(32, maps, regs.get(), process_memory);
+  unwinder.Unwind();
+  return unwinder.NumFrames();
+}
+
+size_t Call5(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  return Call6(process_memory, maps);
+}
+
+size_t Call4(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  return Call5(process_memory, maps);
+}
+
+size_t Call3(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  return Call4(process_memory, maps);
+}
+
+size_t Call2(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  return Call3(process_memory, maps);
+}
+
+size_t Call1(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  return Call2(process_memory, maps);
+}
+
+static void BM_uncached_unwind(benchmark::State& state) {
+  auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
+  unwindstack::LocalMaps maps;
+  if (!maps.Parse()) {
+    state.SkipWithError("Failed to parse local maps.");
+  }
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(Call1(process_memory, &maps));
+  }
+}
+BENCHMARK(BM_uncached_unwind);
+
+static void BM_cached_unwind(benchmark::State& state) {
+  auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
+  unwindstack::LocalMaps maps;
+  if (!maps.Parse()) {
+    state.SkipWithError("Failed to parse local maps.");
+  }
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(Call1(process_memory, &maps));
+  }
+}
+BENCHMARK(BM_cached_unwind);
+
+BENCHMARK_MAIN();
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index 9c425cb..dba41d1 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -25,6 +25,7 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 namespace unwindstack {
@@ -35,9 +36,12 @@
   virtual ~Memory() = default;
 
   static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid);
+  static std::shared_ptr<Memory> CreateProcessMemoryCached(pid_t pid);
 
   virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
 
+  virtual void Clear() {}
+
   virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0;
 
   bool ReadFully(uint64_t addr, void* dst, size_t size);
@@ -51,6 +55,24 @@
   }
 };
 
+class MemoryCache : public Memory {
+ public:
+  MemoryCache(Memory* memory) : impl_(memory) {}
+  virtual ~MemoryCache() = default;
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+  void Clear() override { cache_.clear(); }
+
+ private:
+  constexpr static size_t kCacheBits = 12;
+  constexpr static size_t kCacheMask = (1 << kCacheBits) - 1;
+  constexpr static size_t kCacheSize = 1 << kCacheBits;
+  std::unordered_map<uint64_t, uint8_t[kCacheSize]> cache_;
+
+  std::unique_ptr<Memory> impl_;
+};
+
 class MemoryBuffer : public Memory {
  public:
   MemoryBuffer() = default;
diff --git a/libunwindstack/tests/MemoryCacheTest.cpp b/libunwindstack/tests/MemoryCacheTest.cpp
new file mode 100644
index 0000000..a3def20
--- /dev/null
+++ b/libunwindstack/tests/MemoryCacheTest.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 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 <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MemoryCacheTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    memory_cache_.reset(new MemoryCache(memory_));
+
+    memory_->SetMemoryBlock(0x8000, 4096, 0xab);
+    memory_->SetMemoryBlock(0x9000, 4096, 0xde);
+    memory_->SetMemoryBlock(0xa000, 3000, 0x50);
+  }
+
+  MemoryFake* memory_;
+  std::unique_ptr<MemoryCache> memory_cache_;
+
+  constexpr static size_t kMaxCachedSize = 64;
+};
+
+TEST_F(MemoryCacheTest, cached_read) {
+  for (size_t i = 1; i <= kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+  }
+
+  // Verify the cached data is used.
+  memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+  for (size_t i = 1; i <= kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+  }
+}
+
+TEST_F(MemoryCacheTest, no_cached_read_after_clear) {
+  for (size_t i = 1; i <= kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+  }
+
+  // Verify the cached data is not used after a reset.
+  memory_cache_->Clear();
+  memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+  for (size_t i = 1; i <= kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i;
+  }
+}
+
+TEST_F(MemoryCacheTest, cached_read_across_caches) {
+  std::vector<uint8_t> expect(16, 0xab);
+  expect.resize(32, 0xde);
+
+  std::vector<uint8_t> buffer(32);
+  ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32));
+  ASSERT_EQ(expect, buffer);
+
+  // Verify the cached data is used.
+  memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+  memory_->SetMemoryBlock(0x9000, 4096, 0xff);
+  ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32));
+  ASSERT_EQ(expect, buffer);
+}
+
+TEST_F(MemoryCacheTest, no_cache_read) {
+  for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+  }
+
+  // Verify the cached data is not used.
+  memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+  for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i;
+  }
+}
+
+TEST_F(MemoryCacheTest, read_for_cache_fail) {
+  std::vector<uint8_t> buffer(kMaxCachedSize);
+  ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize));
+  ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0x50), buffer);
+
+  // Verify the cached data is not used.
+  memory_->SetMemoryBlock(0xa000, 3000, 0xff);
+  ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize));
+  ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0xff), buffer);
+}
+
+TEST_F(MemoryCacheTest, read_for_cache_fail_cross) {
+  std::vector<uint8_t> expect(16, 0xde);
+  expect.resize(32, 0x50);
+
+  std::vector<uint8_t> buffer(32);
+  ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32));
+  ASSERT_EQ(expect, buffer);
+
+  // Verify the cached data is not used for the second half but for the first.
+  memory_->SetMemoryBlock(0xa000, 3000, 0xff);
+  ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32));
+  expect.resize(16);
+  expect.resize(32, 0xff);
+  ASSERT_EQ(expect, buffer);
+}
+
+}  // namespace unwindstack