Snap for 8570526 from f09ba56496c440750920bb51eb8a1d3ac450f8f2 to mainline-networking-release

Change-Id: I47817fd93e94a094455b30a8f71ee63d307fc3b4
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 38645cc..af14cde 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -56,9 +56,6 @@
         "//apex_available:anyapex",
     ],
     target: {
-        linux_glibc: {
-            enabled: true,
-        },
         linux_bionic: {
             enabled: true,
         },
@@ -119,6 +116,19 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    // This library is being deprecated in favor of libunwindstack.
+    // Therefore, only allow the current set of users, and block anyone
+    // else.
+    visibility: [
+        "//art:__subpackages__",
+        "//bionic/libc/malloc_debug",
+        "//frameworks/native/opengl/libs",
+        "//packages/modules/vndk/apex",
+        "//packages/modules/Bluetooth/tools/rootcanal",
+        "//packages/modules/Bluetooth/system/gd",
+        "//system/core/init",
+        "//system/core/libutils",
+    ],
     min_sdk_version: "apex_inherit",
     vndk: {
         enabled: true,
@@ -152,6 +162,7 @@
 // Static library without DEX support to avoid dependencies on the ART APEX.
 cc_library_static {
     name: "libbacktrace_no_dex",
+    ramdisk_available: true,
     visibility: [
         "//system/core/debuggerd",
         "//system/core/init",
@@ -182,7 +193,7 @@
     ],
 
     target: {
-        linux_glibc: {
+        host_linux: {
             // This forces the creation of eh_frame with unwind information
             // for host.
             cflags: [
@@ -220,8 +231,6 @@
         "libunwindstack",
     ],
 
-    group_static_libs: true,
-
     target: {
         android: {
             // So that the dlopen can find the libbacktrace_test.so.
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index a506575..84c7bc8 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -28,6 +28,7 @@
 
 #include <string>
 
+#include <android-base/file.h>
 #include <android-base/threads.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
@@ -97,7 +98,7 @@
 
 bool BacktraceCurrent::DiscardFrame(const backtrace_frame_data_t& frame) {
   if (BacktraceMap::IsValid(frame.map)) {
-    const std::string library = basename(frame.map.name.c_str());
+    const std::string library = android::base::Basename(frame.map.name);
     if (library == "libunwind.so" || library == "libbacktrace.so") {
       return true;
     }
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 0b3bb70..d2e65cb 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -95,10 +95,6 @@
         error->error_code = BACKTRACE_UNWIND_ERROR_INVALID_ELF;
         break;
 
-      case unwindstack::ERROR_SYSTEM_CALL:
-        error->error_code = BACKTRACE_UNWIND_ERROR_INTERNAL;
-        break;
-
       case unwindstack::ERROR_THREAD_DOES_NOT_EXIST:
         error->error_code = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
         break;
@@ -106,6 +102,13 @@
       case unwindstack::ERROR_THREAD_TIMEOUT:
         error->error_code = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
         break;
+
+      case unwindstack::ERROR_SYSTEM_CALL:
+      case unwindstack::ERROR_MAPS_PARSE:
+      case unwindstack::ERROR_BAD_ARCH:
+      case unwindstack::ERROR_INVALID_PARAMETER:
+        error->error_code = BACKTRACE_UNWIND_ERROR_INTERNAL;
+        break;
     }
   }
 
@@ -137,12 +140,20 @@
     }
     back_frame->func_offset = frame->function_offset;
 
-    back_frame->map.name = frame->map_name;
-    back_frame->map.start = frame->map_start;
-    back_frame->map.end = frame->map_end;
-    back_frame->map.offset = frame->map_elf_start_offset;
-    back_frame->map.load_bias = frame->map_load_bias;
-    back_frame->map.flags = frame->map_flags;
+    if (frame->map_info != nullptr) {
+      back_frame->map.name = frame->map_info->name();
+      back_frame->map.start = frame->map_info->start();
+      back_frame->map.end = frame->map_info->end();
+      back_frame->map.offset = frame->map_info->elf_start_offset();
+      back_frame->map.load_bias = frame->map_info->load_bias();
+      back_frame->map.flags = frame->map_info->flags();
+    } else {
+      back_frame->map.start = 0;
+      back_frame->map.end = 0;
+      back_frame->map.offset = 0;
+      back_frame->map.load_bias = 0;
+      back_frame->map.flags = 0;
+    }
   }
 
   return true;
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 7c15ed7..b0508d9 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -92,19 +92,24 @@
   }
 
   // Fill in the load_bias.
-  unwindstack::MapInfo* map_info = stack_maps_->Find(addr);
+  std::shared_ptr<unwindstack::MapInfo> map_info = stack_maps_->Find(addr);
   if (map_info == nullptr) {
     return;
   }
   map->load_bias = map_info->GetLoadBias(process_memory_);
 }
 
+std::string UnwindStackMap::GetBuildId(uint64_t addr) {
+  auto map_info = stack_maps_->Find(addr);
+  return map_info == nullptr ? std::string() : map_info->GetPrintableBuildID();
+}
+
 uint64_t UnwindStackMap::GetLoadBias(size_t index) {
   if (index >= stack_maps_->Total()) {
     return 0;
   }
 
-  unwindstack::MapInfo* map_info = stack_maps_->Get(index);
+  std::shared_ptr<unwindstack::MapInfo> map_info = stack_maps_->Get(index);
   if (map_info == nullptr) {
     return 0;
   }
@@ -116,7 +121,7 @@
   unwindstack::Maps* maps = stack_maps();
 
   // Get the map for this
-  unwindstack::MapInfo* map_info = maps->Find(pc);
+  auto map_info = maps->Find(pc);
   if (map_info == nullptr || map_info->flags() & PROT_DEVICE_MAP) {
     return "";
   }
@@ -135,7 +140,7 @@
 
   unwindstack::SharedString name;
   uint64_t func_offset;
-  if (!elf->GetFunctionName(elf->GetRelPc(pc, map_info), &name, &func_offset)) {
+  if (!elf->GetFunctionName(elf->GetRelPc(pc, map_info.get()), &name, &func_offset)) {
     return "";
   }
   *offset = func_offset;
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index f0e7d8b..3c2591e 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -47,6 +47,8 @@
 
   void FillIn(uint64_t addr, backtrace_map_t* map) override;
 
+  std::string GetBuildId(uint64_t addr) override;
+
   virtual std::string GetFunctionName(uint64_t pc, uint64_t* offset) override;
   virtual std::shared_ptr<unwindstack::Memory> GetProcessMemory() override final;
 
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index a9160d5..b978349 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -45,6 +45,7 @@
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
+#include <android-base/file.h>
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <android-base/test_utils.h>
@@ -281,7 +282,7 @@
   // None of the frames should be in the backtrace libraries.
   for (const auto& frame : *backtrace ) {
     if (BacktraceMap::IsValid(frame.map)) {
-      const std::string name = basename(frame.map.name.c_str());
+      const std::string name = android::base::Basename(frame.map.name);
       for (const auto& lib : kBacktraceLibs) {
         ASSERT_TRUE(name != lib) << DumpFrames(backtrace.get());
       }
@@ -302,7 +303,7 @@
   size_t first_frame_non_backtrace_lib = 0;
   for (const auto& frame : *backtrace) {
     if (BacktraceMap::IsValid(frame.map)) {
-      const std::string name = basename(frame.map.name.c_str());
+      const std::string name = android::base::Basename(frame.map.name);
       bool found = false;
       for (const auto& lib : kBacktraceLibs) {
         if (name == lib) {
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index e000a00..eab1328 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -117,6 +117,9 @@
   // Fill in the map data structure for the given address.
   virtual void FillIn(uint64_t addr, backtrace_map_t* map);
 
+  // Get BuildId of ELF file at the given address, or empty string on failure.
+  virtual std::string GetBuildId(uint64_t /*pc*/) { return std::string(); }
+
   // Only supported with the new unwinder.
   virtual std::string GetFunctionName(uint64_t /*pc*/, uint64_t* /*offset*/) { return ""; }
   virtual std::shared_ptr<unwindstack::Memory> GetProcessMemory() { return nullptr; }
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 5443ce7..bed17e4 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -51,42 +51,44 @@
     },
 }
 
+libunwindstack_common_src_files = [
+    "AndroidUnwinder.cpp",
+    "ArmExidx.cpp",
+    "DexFiles.cpp",
+    "DwarfCfa.cpp",
+    "DwarfEhFrameWithHdr.cpp",
+    "DwarfMemory.cpp",
+    "DwarfOp.cpp",
+    "DwarfSection.cpp",
+    "Elf.cpp",
+    "ElfInterface.cpp",
+    "ElfInterfaceArm.cpp",
+    "Global.cpp",
+    "JitDebug.cpp",
+    "MapInfo.cpp",
+    "Maps.cpp",
+    "Memory.cpp",
+    "MemoryMte.cpp",
+    "MemoryXz.cpp",
+    "Regs.cpp",
+    "RegsArm.cpp",
+    "RegsArm64.cpp",
+    "RegsX86.cpp",
+    "RegsX86_64.cpp",
+    "RegsMips.cpp",
+    "RegsMips64.cpp",
+    "Symbols.cpp",
+    "ThreadEntry.cpp",
+    "ThreadUnwinder.cpp",
+    "Unwinder.cpp",
+]
+
 cc_defaults {
     name: "libunwindstack_defaults",
     defaults: ["libunwindstack_flags"],
     export_include_dirs: ["include"],
 
-    srcs: [
-        "ArmExidx.cpp",
-        "DexFiles.cpp",
-        "DwarfCfa.cpp",
-        "DwarfEhFrameWithHdr.cpp",
-        "DwarfMemory.cpp",
-        "DwarfOp.cpp",
-        "DwarfSection.cpp",
-        "Elf.cpp",
-        "ElfInterface.cpp",
-        "ElfInterfaceArm.cpp",
-        "Global.cpp",
-        "JitDebug.cpp",
-        "Log.cpp",
-        "MapInfo.cpp",
-        "Maps.cpp",
-        "Memory.cpp",
-        "MemoryMte.cpp",
-        "LocalUnwinder.cpp",
-        "Regs.cpp",
-        "RegsArm.cpp",
-        "RegsArm64.cpp",
-        "RegsX86.cpp",
-        "RegsX86_64.cpp",
-        "RegsMips.cpp",
-        "RegsMips64.cpp",
-        "Symbols.cpp",
-        "ThreadEntry.cpp",
-        "ThreadUnwinder.cpp",
-        "Unwinder.cpp",
-    ],
+    srcs: libunwindstack_common_src_files,
 
     cflags: [
         "-Wexit-time-destructors",
@@ -140,35 +142,44 @@
         support_system_process: true,
     },
     defaults: ["libunwindstack_defaults"],
-    srcs: ["DexFile.cpp"],
+    srcs: [
+        "DexFile.cpp",
+        "LogAndroid.cpp",
+    ],
     cflags: ["-DDEXFILE_SUPPORT"],
     static_libs: ["libdexfile_support"],
+    runtime_libs: ["libdexfile"], // libdexfile_support dependency
 
     target: {
         vendor: {
             cflags: ["-UDEXFILE_SUPPORT"],
             exclude_srcs: ["DexFile.cpp"],
             exclude_static_libs: ["libdexfile_support"],
+            exclude_runtime_libs: ["libdexfile"],
         },
         product: {
             cflags: ["-UDEXFILE_SUPPORT"],
             exclude_srcs: ["DexFile.cpp"],
             exclude_static_libs: ["libdexfile_support"],
+            exclude_runtime_libs: ["libdexfile"],
         },
         recovery: {
             cflags: ["-UDEXFILE_SUPPORT"],
             exclude_srcs: ["DexFile.cpp"],
             exclude_static_libs: ["libdexfile_support"],
+            exclude_runtime_libs: ["libdexfile"],
         },
         vendor_ramdisk: {
             cflags: ["-UDEXFILE_SUPPORT"],
             exclude_srcs: ["DexFile.cpp"],
             exclude_static_libs: ["libdexfile_support"],
+            exclude_runtime_libs: ["libdexfile"],
         },
         native_bridge: {
             cflags: ["-UDEXFILE_SUPPORT"],
             exclude_srcs: ["DexFile.cpp"],
             exclude_static_libs: ["libdexfile_support"],
+            exclude_runtime_libs: ["libdexfile"],
         },
     },
 
@@ -180,12 +191,23 @@
     min_sdk_version: "29",
 }
 
+// Make sure that the code can be compiled without Android Logging.
+cc_library {
+    name: "libunwindstack_stdout_log",
+    defaults: ["libunwindstack_defaults"],
+    srcs: [
+        "LogStdout.cpp",
+    ],
+}
+
 // Static library without DEX support to avoid dependencies on the ART APEX.
 cc_library_static {
     name: "libunwindstack_no_dex",
+    ramdisk_available: true,
     recovery_available: true,
     vendor_ramdisk_available: true,
     defaults: ["libunwindstack_defaults"],
+    srcs: ["LogAndroid.cpp"],
 
     visibility: [
         "//external/gwp_asan",
@@ -200,6 +222,30 @@
 }
 
 //-------------------------------------------------------------------------
+// Utils
+//-------------------------------------------------------------------------
+cc_library {
+    name: "libunwindstack_utils",
+    defaults: ["libunwindstack_flags"],
+    export_include_dirs: ["utils"],
+    shared_libs: [
+        "libbase",
+        "libunwindstack",
+        "libprocinfo",
+    ],
+    whole_static_libs: [
+        "libc++fs",
+        "libz",
+    ],
+    srcs: [
+        "utils/MemoryFake.cpp",
+        "utils/OfflineUnwindUtils.cpp",
+        "utils/PidUtils.cpp",
+        "utils/ProcessTracer.cpp",
+    ],
+}
+
+//-------------------------------------------------------------------------
 // Unit Tests
 //-------------------------------------------------------------------------
 cc_library_shared {
@@ -222,6 +268,7 @@
     defaults: ["libunwindstack_flags"],
 
     srcs: [
+        "tests/AndroidUnwinderTest.cpp",
         "tests/ArmExidxDecodeTest.cpp",
         "tests/ArmExidxExtractTest.cpp",
         "tests/DexFileTest.cpp",
@@ -243,9 +290,9 @@
         "tests/ElfTest.cpp",
         "tests/ElfTestUtils.cpp",
         "tests/GlobalDebugImplTest.cpp",
+        "tests/GlobalTest.cpp",
         "tests/IsolatedSettings.cpp",
         "tests/JitDebugTest.cpp",
-        "tests/LocalUnwinderTest.cpp",
         "tests/LocalUpdatableMapsTest.cpp",
         "tests/LogFake.cpp",
         "tests/MapInfoCreateMemoryTest.cpp",
@@ -256,7 +303,6 @@
         "tests/MapsTest.cpp",
         "tests/MemoryBufferTest.cpp",
         "tests/MemoryCacheTest.cpp",
-        "tests/MemoryFake.cpp",
         "tests/MemoryFileTest.cpp",
         "tests/MemoryLocalTest.cpp",
         "tests/MemoryOfflineBufferTest.cpp",
@@ -270,6 +316,7 @@
         "tests/MemoryXzTest.cpp",
         "tests/RegsInfoTest.cpp",
         "tests/RegsIterateTest.cpp",
+        "tests/RegsRemoteTest.cpp",
         "tests/RegsStepIfSignalHandlerTest.cpp",
         "tests/RegsTest.cpp",
         "tests/SymbolsTest.cpp",
@@ -278,6 +325,7 @@
         "tests/UnwindTest.cpp",
         "tests/UnwinderTest.cpp",
         "tests/VerifyBionicTerminationTest.cpp",
+        "utils/tests/ProcessTracerTest.cpp",
     ],
 
     cflags: [
@@ -295,6 +343,8 @@
     static_libs: [
         "libdexfile_support",
         "libgmock",
+        "libprocinfo",
+        "libunwindstack_utils",
     ],
 
     test_suites: ["device-tests"],
@@ -306,30 +356,47 @@
         "tests/files/boot_arm.oat.gnu_debugdata.xz.one-block",
         "tests/files/elf32.xz",
         "tests/files/elf64.xz",
-        "tests/files/offline/art_quick_osr_stub_arm/*",
-        "tests/files/offline/bad_eh_frame_hdr_arm64/*",
-        "tests/files/offline/debug_frame_first_x86/*",
-        "tests/files/offline/debug_frame_load_bias_arm/*",
-        "tests/files/offline/eh_frame_bias_x86/*",
-        "tests/files/offline/eh_frame_hdr_begin_x86_64/*",
-        "tests/files/offline/empty_arm64/*",
-        "tests/files/offline/invalid_elf_offset_arm/*",
-        "tests/files/offline/jit_debug_arm/*",
-        "tests/files/offline/jit_debug_x86/*",
-        "tests/files/offline/jit_map_arm/*",
-        "tests/files/offline/gnu_debugdata_arm/*",
-        "tests/files/offline/load_bias_different_section_bias_arm64/*",
-        "tests/files/offline/load_bias_ro_rx_x86_64/*",
-        "tests/files/offline/offset_arm/*",
-        "tests/files/offline/pauth_pc_arm64/*",
-        "tests/files/offline/shared_lib_in_apk_arm64/*",
-        "tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
-        "tests/files/offline/shared_lib_in_apk_single_map_arm64/*",
-        "tests/files/offline/signal_load_bias_arm/*",
-        "tests/files/offline/signal_fde_x86/*",
-        "tests/files/offline/signal_fde_x86_64/*",
-        "tests/files/offline/straddle_arm/*",
-        "tests/files/offline/straddle_arm64/*",
+        "offline_files/common/*",
+        "offline_files/apk_rorx_arm64/*",
+        "offline_files/apk_rorx_unreadable_arm64/*",
+        "offline_files/apk_rx_arm64/*",
+        "offline_files/apk_rx_unreadable_arm64/*",
+        "offline_files/art_quick_osr_stub_arm/*",
+        "offline_files/bad_eh_frame_hdr_arm64/*",
+        "offline_files/debug_frame_first_x86/*",
+        "offline_files/debug_frame_load_bias_arm/*",
+        "offline_files/eh_frame_bias_x86/*",
+        "offline_files/eh_frame_hdr_begin_x86_64/*",
+        "offline_files/empty_arm64/*",
+        "offline_files/invalid_elf_offset_arm/*",
+        "offline_files/jit_debug_arm/*",
+        "offline_files/jit_map_arm/*",
+        "offline_files/gnu_debugdata_arm/*",
+        "offline_files/load_bias_different_section_bias_arm64/*",
+        "offline_files/load_bias_ro_rx_x86_64/*",
+        "offline_files/offset_arm/*",
+        "offline_files/pauth_pc_arm64/*",
+        "offline_files/shared_lib_in_apk_arm64/*",
+        "offline_files/shared_lib_in_apk_memory_only_arm64/*",
+        "offline_files/shared_lib_in_apk_single_map_arm64/*",
+        "offline_files/signal_load_bias_arm/*",
+        "offline_files/signal_fde_x86/*",
+        "offline_files/signal_fde_x86_64/*",
+        "offline_files/straddle_arm/*",
+        "offline_files/jit_debug_x86/*",
+        "offline_files/straddle_arm64/*",
+        "offline_files/bluetooth_arm64/pc_1/*",
+        "offline_files/bluetooth_arm64/pc_2/*",
+        "offline_files/bluetooth_arm64/pc_3/*",
+        "offline_files/bluetooth_arm64/pc_4/*",
+        "offline_files/photos_reset_arm64/*",
+        "offline_files/youtube_compiled_arm64/*",
+        "offline_files/yt_music_arm64/*",
+        "offline_files/maps_compiled_arm64/28613_main-thread/*",
+        "offline_files/maps_compiled_arm64/28644/*",
+        "offline_files/maps_compiled_arm64/28648/*",
+        "offline_files/maps_compiled_arm64/28656_oat_odex_jar/*",
+        "offline_files/maps_compiled_arm64/28667/*",
     ],
 
     target: {
@@ -371,6 +438,7 @@
     ],
     static_libs: [
         "libdexfile_support",
+        "libunwindstack_utils",
     ],
 }
 
@@ -378,7 +446,6 @@
     name: "libunwindstack_fuzz_unwinder",
     defaults: ["libunwindstack_fuzz_defaults"],
     srcs: [
-        "tests/MemoryFake.cpp",
         "tests/ElfFake.cpp",
         "tests/fuzz/UnwinderComponentCreator.cpp",
         "tests/fuzz/UnwinderFuzz.cpp",
@@ -393,7 +460,7 @@
     defaults: ["libunwindstack_flags"],
 
     shared_libs: [
-        "libunwindstack",
+        "libunwindstack_stdout_log",
         "libbase",
         "liblzma",
     ],
@@ -438,6 +505,10 @@
 cc_binary {
     name: "unwind_for_offline",
     defaults: ["libunwindstack_tools"],
+    static_libs: [
+        "libunwindstack_utils",
+        "libc++fs",
+    ],
 
     srcs: [
         "tools/unwind_for_offline.cpp",
@@ -475,10 +546,27 @@
         "benchmarks/main.cpp",
         "benchmarks/remote_unwind_benchmarks.cpp",
         "benchmarks/thread_unwind_benchmarks.cpp",
+        "benchmarks/OfflineUnwindBenchmarks.cpp",
+        "benchmarks/EvalBenchmark.cpp",
     ],
 
     data: [
         "benchmarks/files/*",
+        "offline_files/common/*",
+        "offline_files/jit_debug_arm/*",
+        "offline_files/straddle_arm64/*",
+        "offline_files/bluetooth_arm64/pc_1/*",
+        "offline_files/bluetooth_arm64/pc_2/*",
+        "offline_files/bluetooth_arm64/pc_3/*",
+        "offline_files/bluetooth_arm64/pc_4/*",
+        "offline_files/photos_reset_arm64/*",
+        "offline_files/youtube_compiled_arm64/*",
+        "offline_files/yt_music_arm64/*",
+        "offline_files/maps_compiled_arm64/28613_main-thread/*",
+        "offline_files/maps_compiled_arm64/28644/*",
+        "offline_files/maps_compiled_arm64/28648/*",
+        "offline_files/maps_compiled_arm64/28656_oat_odex_jar/*",
+        "offline_files/maps_compiled_arm64/28667/*",
     ],
 
     shared_libs: [
@@ -486,11 +574,15 @@
         "libunwindstack",
     ],
 
+    static_libs: [
+        "libunwindstack_utils",
+        "libprocinfo",
+    ],
+
     target: {
         android: {
             static_libs: [
                 "libmeminfo",
-                "libprocinfo",
             ],
         },
     },
diff --git a/libunwindstack/AndroidUnwinder.cpp b/libunwindstack/AndroidUnwinder.cpp
new file mode 100644
index 0000000..24e991e
--- /dev/null
+++ b/libunwindstack/AndroidUnwinder.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2022 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 <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
+
+#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Arch.h>
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/Error.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
+#if defined(__BIONIC__)
+#include <bionic/reserved_signals.h>
+static constexpr int kThreadUnwindSignal = BIONIC_SIGNAL_BACKTRACE;
+#else
+#include <signal.h>
+static int kThreadUnwindSignal = SIGRTMIN;
+#endif
+
+// Use the demangler from libc++.
+extern "C" char* __cxa_demangle(const char*, char*, size_t*, int* status);
+
+namespace unwindstack {
+
+void AndroidUnwinderData::DemangleFunctionNames() {
+  for (auto& frame : frames) {
+    char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr, nullptr);
+    if (demangled_name != nullptr) {
+      frame.function_name = demangled_name;
+      free(demangled_name);
+    }
+  }
+}
+
+std::string AndroidUnwinderData::GetErrorString() {
+  std::string error_msg(GetErrorCodeString(error.code));
+  if (error.address != 0) {
+    error_msg += android::base::StringPrintf(" at address 0x%" PRIx64, error.address);
+  }
+  return error_msg;
+}
+
+AndroidUnwinder* AndroidUnwinder::Create(pid_t pid) {
+  if (pid == getpid()) {
+    return new AndroidLocalUnwinder;
+  } else {
+    return new AndroidRemoteUnwinder(pid);
+  }
+}
+
+bool AndroidUnwinder::Initialize(ErrorData& error) {
+  // Android stores the jit and dex file location only in the library
+  // libart.so or libartd.so.
+  static std::vector<std::string> search_libs [[clang::no_destroy]] = {"libart.so", "libartd.so"};
+
+  bool initialize = true;
+  std::call_once(initialize_, [this, &initialize, &error]() {
+    initialize = InternalInitialize(error);
+    if (!initialize) {
+      return;
+    }
+
+    jit_debug_ = CreateJitDebug(arch_, process_memory_, search_libs);
+
+#if defined(DEXFILE_SUPPORT)
+    dex_files_ = CreateDexFiles(arch_, process_memory_, search_libs);
+#endif
+  });
+
+  return initialize;
+}
+
+std::string AndroidUnwinder::FormatFrame(const FrameData& frame) const {
+  if (arch_ == ARCH_UNKNOWN) {
+    return "";
+  }
+  return Unwinder::FormatFrame(arch_, frame);
+}
+
+bool AndroidLocalUnwinder::InternalInitialize(ErrorData& error) {
+  arch_ = Regs::CurrentArch();
+
+  maps_.reset(new LocalUpdatableMaps);
+  if (!maps_->Parse()) {
+    error.code = ERROR_MAPS_PARSE;
+    return false;
+  }
+
+  if (process_memory_ == nullptr) {
+    process_memory_ = Memory::CreateProcessMemoryThreadCached(getpid());
+  }
+
+  return true;
+}
+
+FrameData AndroidUnwinder::BuildFrameFromPcOnly(uint64_t pc) {
+  return Unwinder::BuildFrameFromPcOnly(pc, arch_, maps_.get(), jit_debug_.get(), process_memory_,
+                                        true);
+}
+
+bool AndroidUnwinder::Unwind(AndroidUnwinderData& data) {
+  return Unwind(std::nullopt, data);
+}
+
+bool AndroidUnwinder::Unwind(std::optional<pid_t> tid, AndroidUnwinderData& data) {
+  if (!Initialize(data.error)) {
+    return false;
+  }
+
+  return InternalUnwind(tid, data);
+}
+
+bool AndroidUnwinder::Unwind(void* ucontext, AndroidUnwinderData& data) {
+  if (ucontext == nullptr) {
+    data.error.code = ERROR_INVALID_PARAMETER;
+    return false;
+  }
+  std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(arch_, ucontext));
+  return Unwind(regs.get(), data);
+}
+
+bool AndroidUnwinder::Unwind(Regs* initial_regs, AndroidUnwinderData& data) {
+  if (initial_regs == nullptr) {
+    data.error.code = ERROR_INVALID_PARAMETER;
+    return false;
+  }
+
+  if (!Initialize(data.error)) {
+    return false;
+  }
+
+  if (arch_ != initial_regs->Arch()) {
+    data.error.code = ERROR_BAD_ARCH;
+    return false;
+  }
+
+  std::unique_ptr<Regs> regs(initial_regs->Clone());
+  if (data.saved_initial_regs) {
+    (*data.saved_initial_regs).reset(initial_regs->Clone());
+  }
+  Unwinder unwinder(data.max_frames.value_or(max_frames_), maps_.get(), regs.get(),
+                    process_memory_);
+  unwinder.SetJitDebug(jit_debug_.get());
+  unwinder.SetDexFiles(dex_files_.get());
+  unwinder.Unwind(data.show_all_frames ? nullptr : &initial_map_names_to_skip_,
+                  &map_suffixes_to_ignore_);
+  data.frames = unwinder.ConsumeFrames();
+  data.error = unwinder.LastError();
+  return data.frames.size() != 0;
+}
+
+bool AndroidLocalUnwinder::InternalUnwind(std::optional<pid_t> tid, AndroidUnwinderData& data) {
+  if (!tid) {
+    *tid = android::base::GetThreadId();
+  }
+
+  if (static_cast<uint64_t>(*tid) == android::base::GetThreadId()) {
+    // Unwind current thread.
+    std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+    RegsGetLocal(regs.get());
+    return AndroidUnwinder::Unwind(regs.get(), data);
+  }
+
+  ThreadUnwinder unwinder(data.max_frames.value_or(max_frames_), maps_.get(), process_memory_);
+  unwinder.SetJitDebug(jit_debug_.get());
+  unwinder.SetDexFiles(dex_files_.get());
+  std::unique_ptr<Regs>* initial_regs = nullptr;
+  if (data.saved_initial_regs) {
+    initial_regs = &data.saved_initial_regs.value();
+  }
+  unwinder.UnwindWithSignal(kThreadUnwindSignal, *tid, initial_regs,
+                            data.show_all_frames ? nullptr : &initial_map_names_to_skip_,
+                            &map_suffixes_to_ignore_);
+  data.frames = unwinder.ConsumeFrames();
+  data.error = unwinder.LastError();
+  return data.frames.size() != 0;
+}
+
+bool AndroidRemoteUnwinder::InternalInitialize(ErrorData& error) {
+  if (arch_ == ARCH_UNKNOWN) {
+    arch_ = Regs::RemoteGetArch(pid_);
+  }
+  if (arch_ == ARCH_UNKNOWN) {
+    error.code = ERROR_BAD_ARCH;
+    return false;
+  }
+
+  maps_.reset(new RemoteMaps(pid_));
+  if (!maps_->Parse()) {
+    error.code = ERROR_MAPS_PARSE;
+    return false;
+  }
+
+  if (process_memory_ == nullptr) {
+    process_memory_ = Memory::CreateProcessMemoryCached(pid_);
+  }
+
+  return true;
+}
+
+bool AndroidRemoteUnwinder::InternalUnwind(std::optional<pid_t> tid, AndroidUnwinderData& data) {
+  if (!tid) {
+    *tid = pid_;
+  }
+
+  std::unique_ptr<Regs> regs(Regs::RemoteGet(*tid));
+  return AndroidUnwinder::Unwind(regs.get(), data);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/AndroidVersions.md b/libunwindstack/AndroidVersions.md
index b4e29ca..ff46829 100644
--- a/libunwindstack/AndroidVersions.md
+++ b/libunwindstack/AndroidVersions.md
@@ -114,3 +114,19 @@
   and uses pc relative FDEs, the unwind will be incorrect. This tends
   to truncate unwinds since the unwinder could not find the correct unwind
   information for a given pc.
+
+## Android 12 ("S", API level 31)
+* Fix bug where, if a shared library is dlopen'ed from within an apk file,
+  is not readable, and the shared library only produces a single read-
+  executable map for the elf data and executable data, the offset into the
+  apk will not be displayed. Previously the line would look like:
+
+    #01 pc 000000000222675c  GoogleCamera.apk
+
+  to:
+
+    #01 pc 000000000222675c  GoogleCamera.apk (offset 0x269f000)
+
+  If the apk file is readable, or dlopen'ing the shared library creates
+  a read-only map of the elf data, and a read-executable map of the
+  code, the offset will be displayed properly without this fix.
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index 818f5d1..c737cef 100644
--- a/libunwindstack/ArmExidx.cpp
+++ b/libunwindstack/ArmExidx.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
 #include <stdint.h>
 
 #include <deque>
@@ -38,7 +39,7 @@
   for (const uint8_t data : data_) {
     log_str += android::base::StringPrintf(" 0x%02x", data);
   }
-  log(log_indent_, log_str.c_str());
+  Log::Info(log_indent_, "%s", log_str.c_str());
 }
 
 bool ArmExidx::ExtractEntryData(uint32_t entry_offset) {
@@ -67,9 +68,9 @@
     status_ = ARM_STATUS_NO_UNWIND;
     if (log_type_ != ARM_LOG_NONE) {
       if (log_type_ == ARM_LOG_FULL) {
-        log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
+        Log::Info(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
       }
-      log(log_indent_, "[cantunwind]");
+      Log::Info(log_indent_, "[cantunwind]");
     }
     return false;
   }
@@ -195,7 +196,7 @@
   if (registers == 0) {
     // 10000000 00000000: Refuse to unwind
     if (log_type_ != ARM_LOG_NONE) {
-      log(log_indent_, "Refuse to unwind");
+      Log::Info(log_indent_, "Refuse to unwind");
     }
     status_ = ARM_STATUS_NO_UNWIND;
     return false;
@@ -216,7 +217,7 @@
           add_comma = true;
         }
       }
-      log(log_indent_, "%s}", msg.c_str());
+      Log::Info(log_indent_, "%s}", msg.c_str());
     } else {
       uint32_t cfa_offset = __builtin_popcount(registers) * 4;
       log_cfa_offset_ += cfa_offset;
@@ -264,7 +265,7 @@
     // 10011101: Reserved as prefix for ARM register to register moves
     // 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
     if (log_type_ != ARM_LOG_NONE) {
-      log(log_indent_, "[Reserved]");
+      Log::Info(log_indent_, "[Reserved]");
     }
     status_ = ARM_STATUS_RESERVED;
     return false;
@@ -272,7 +273,7 @@
   // 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
   if (log_type_ != ARM_LOG_NONE) {
     if (log_type_ == ARM_LOG_FULL) {
-      log(log_indent_, "vsp = r%d", bits);
+      Log::Info(log_indent_, "vsp = r%d", bits);
     } else {
       log_regs_[LOG_CFA_REG] = bits;
     }
@@ -300,9 +301,9 @@
         msg += android::base::StringPrintf("-r%d", 4 + end_reg);
       }
       if (byte & 0x8) {
-        log(log_indent_, "%s, r14}", msg.c_str());
+        Log::Info(log_indent_, "%s, r14}", msg.c_str());
       } else {
-        log(log_indent_, "%s}", msg.c_str());
+        Log::Info(log_indent_, "%s}", msg.c_str());
       }
     } else {
       end_reg += 4;
@@ -350,7 +351,7 @@
   // 10110000: Finish
   if (log_type_ != ARM_LOG_NONE) {
     if (log_type_ == ARM_LOG_FULL) {
-      log(log_indent_, "finish");
+      Log::Info(log_indent_, "finish");
     }
 
     if (log_skip_execution_) {
@@ -371,7 +372,7 @@
   if (byte == 0) {
     // 10110001 00000000: Spare
     if (log_type_ != ARM_LOG_NONE) {
-      log(log_indent_, "Spare");
+      Log::Info(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
     return false;
@@ -379,7 +380,7 @@
   if (byte >> 4) {
     // 10110001 xxxxyyyy: Spare (xxxx != 0000)
     if (log_type_ != ARM_LOG_NONE) {
-      log(log_indent_, "Spare");
+      Log::Info(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
     return false;
@@ -399,7 +400,7 @@
           add_comma = true;
         }
       }
-      log(log_indent_, "%s}", msg.c_str());
+      Log::Info(log_indent_, "%s}", msg.c_str());
     } else {
       byte &= 0xf;
       uint32_t cfa_offset = __builtin_popcount(byte) * 4;
@@ -456,7 +457,7 @@
   if (log_type_ != ARM_LOG_NONE) {
     int32_t cfa_offset = 0x204 + result;
     if (log_type_ == ARM_LOG_FULL) {
-      log(log_indent_, "vsp = vsp + %d", cfa_offset);
+      Log::Info(log_indent_, "vsp = vsp + %d", cfa_offset);
     } else {
       log_cfa_offset_ += cfa_offset;
     }
@@ -486,9 +487,9 @@
       if (end_reg) {
         msg += android::base::StringPrintf("-d%d", end_reg);
       }
-      log(log_indent_, "%s}", msg.c_str());
+      Log::Info(log_indent_, "%s}", msg.c_str());
     } else {
-      log(log_indent_, "Unsupported DX register display");
+      Log::Info(log_indent_, "Unsupported DX register display");
     }
 
     if (log_skip_execution_) {
@@ -502,7 +503,7 @@
 inline bool ArmExidx::DecodePrefix_10_11_01nn() {
   // 101101nn: Spare
   if (log_type_ != ARM_LOG_NONE) {
-    log(log_indent_, "Spare");
+    Log::Info(log_indent_, "Spare");
   }
   status_ = ARM_STATUS_SPARE;
   return false;
@@ -519,9 +520,9 @@
       if (last_reg) {
         msg += android::base::StringPrintf("-d%d", last_reg + 8);
       }
-      log(log_indent_, "%s}", msg.c_str());
+      Log::Info(log_indent_, "%s}", msg.c_str());
     } else {
-      log(log_indent_, "Unsupported DX register display");
+      Log::Info(log_indent_, "Unsupported DX register display");
     }
 
     if (log_skip_execution_) {
@@ -581,9 +582,9 @@
         if (end_reg) {
           msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
         }
-        log(log_indent_, "%s}", msg.c_str());
+        Log::Info(log_indent_, "%s}", msg.c_str());
       } else {
-        log(log_indent_, "Unsupported wRX register display");
+        Log::Info(log_indent_, "Unsupported wRX register display");
       }
 
       if (log_skip_execution_) {
@@ -600,7 +601,7 @@
     if (byte == 0) {
       // 11000111 00000000: Spare
       if (log_type_ != ARM_LOG_NONE) {
-        log(log_indent_, "Spare");
+        Log::Info(log_indent_, "Spare");
       }
       status_ = ARM_STATUS_SPARE;
       return false;
@@ -619,9 +620,9 @@
               add_comma = true;
             }
           }
-          log(log_indent_, "%s}", msg.c_str());
+          Log::Info(log_indent_, "%s}", msg.c_str());
         } else {
-          log(log_indent_, "Unsupported wCGR register display");
+          Log::Info(log_indent_, "Unsupported wCGR register display");
         }
 
         if (log_skip_execution_) {
@@ -633,7 +634,7 @@
     } else {
       // 11000111 xxxxyyyy: Spare (xxxx != 0000)
       if (log_type_ != ARM_LOG_NONE) {
-        log(log_indent_, "Spare");
+        Log::Info(log_indent_, "Spare");
       }
       status_ = ARM_STATUS_SPARE;
       return false;
@@ -647,9 +648,9 @@
         if (nnn) {
           msg += android::base::StringPrintf("-wR%d", 10 + nnn);
         }
-        log(log_indent_, "%s}", msg.c_str());
+        Log::Info(log_indent_, "%s}", msg.c_str());
       } else {
-        log(log_indent_, "Unsupported wRX register display");
+        Log::Info(log_indent_, "Unsupported wRX register display");
       }
 
       if (log_skip_execution_) {
@@ -680,9 +681,9 @@
         if (end_reg) {
           msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
         }
-        log(log_indent_, "%s}", msg.c_str());
+        Log::Info(log_indent_, "%s}", msg.c_str());
       } else {
-        log(log_indent_, "Unsupported DX register display");
+        Log::Info(log_indent_, "Unsupported DX register display");
       }
 
       if (log_skip_execution_) {
@@ -705,9 +706,9 @@
         if (end_reg) {
           msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
         }
-        log(log_indent_, "%s}", msg.c_str());
+        Log::Info(log_indent_, "%s}", msg.c_str());
       } else {
-        log(log_indent_, "Unsupported DX register display");
+        Log::Info(log_indent_, "Unsupported DX register display");
       }
 
       if (log_skip_execution_) {
@@ -719,7 +720,7 @@
   } else {
     // 11001yyy: Spare (yyy != 000, 001)
     if (log_type_ != ARM_LOG_NONE) {
-      log(log_indent_, "Spare");
+      Log::Info(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
     return false;
@@ -738,9 +739,9 @@
       if (end_reg) {
         msg += android::base::StringPrintf("-d%d", 8 + end_reg);
       }
-      log(log_indent_, "%s}", msg.c_str());
+      Log::Info(log_indent_, "%s}", msg.c_str());
     } else {
-      log(log_indent_, "Unsupported DX register display");
+      Log::Info(log_indent_, "Unsupported DX register display");
     }
 
     if (log_skip_execution_) {
@@ -764,7 +765,7 @@
   default:
     // 11xxxyyy: Spare (xxx != 000, 001, 010)
     if (log_type_ != ARM_LOG_NONE) {
-      log(log_indent_, "Spare");
+      Log::Info(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
     return false;
@@ -784,7 +785,7 @@
     if (log_type_ != ARM_LOG_NONE) {
       int32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
       if (log_type_ == ARM_LOG_FULL) {
-        log(log_indent_, "vsp = vsp + %d", cfa_offset);
+        Log::Info(log_indent_, "vsp = vsp + %d", cfa_offset);
       } else {
         log_cfa_offset_ += cfa_offset;
       }
@@ -801,7 +802,7 @@
     if (log_type_ != ARM_LOG_NONE) {
       uint32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
       if (log_type_ == ARM_LOG_FULL) {
-        log(log_indent_, "vsp = vsp - %d", cfa_offset);
+        Log::Info(log_indent_, "vsp = vsp - %d", cfa_offset);
       } else {
         log_cfa_offset_ -= cfa_offset;
       }
@@ -841,9 +842,9 @@
 
   if (log_cfa_offset_ != 0) {
     char sign = (log_cfa_offset_ > 0) ? '+' : '-';
-    log(log_indent_, "cfa = r%zu %c %d", cfa_reg, sign, abs(log_cfa_offset_));
+    Log::Info(log_indent_, "cfa = r%" PRIu8 " %c %d", cfa_reg, sign, abs(log_cfa_offset_));
   } else {
-    log(log_indent_, "cfa = r%zu", cfa_reg);
+    Log::Info(log_indent_, "cfa = r%" PRIu8, cfa_reg);
   }
 
   for (const auto& entry : log_regs_) {
@@ -851,10 +852,10 @@
       break;
     }
     if (entry.second == 0) {
-      log(log_indent_, "r%zu = [cfa]", entry.first);
+      Log::Info(log_indent_, "r%" PRIu8 " = [cfa]", entry.first);
     } else {
       char sign = (entry.second > 0) ? '-' : '+';
-      log(log_indent_, "r%zu = [cfa %c %d]", entry.first, sign, abs(entry.second));
+      Log::Info(log_indent_, "r%" PRIu8 " = [cfa %c %d]", entry.first, sign, abs(entry.second));
     }
   }
 }
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h
index d9fc371..847613a 100644
--- a/libunwindstack/ArmExidx.h
+++ b/libunwindstack/ArmExidx.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_ARM_EXIDX_H
-#define _LIBUNWINDSTACK_ARM_EXIDX_H
+#pragma once
 
 #include <stdint.h>
 
@@ -122,5 +121,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_ARM_EXIDX_H
diff --git a/libunwindstack/Check.h b/libunwindstack/Check.h
index 9643d76..102cbb1 100644
--- a/libunwindstack/Check.h
+++ b/libunwindstack/Check.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_CHECK_H
-#define _LIBUNWINDSTACK_CHECK_H
+#pragma once
 
 #include <stdlib.h>
 
@@ -23,12 +22,10 @@
 
 namespace unwindstack {
 
-#define CHECK(assertion)                                   \
-  if (__builtin_expect(!(assertion), false)) {             \
-    log(0, "%s:%d: %s\n", __FILE__, __LINE__, #assertion); \
-    abort();                                               \
+#define CHECK(assertion)                                       \
+  if (__builtin_expect(!(assertion), false)) {                 \
+    Log::Error("%s:%d: %s\n", __FILE__, __LINE__, #assertion); \
+    abort();                                                   \
   }
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_CHECK_H
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index e576354..220ee82 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -22,12 +22,10 @@
 
 #include <memory>
 
-#define LOG_TAG "unwind"
-#include <log/log.h>
-
 #include <android-base/unique_fd.h>
 #include <art_api/dex_file_support.h>
 
+#include <unwindstack/Log.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
 
@@ -41,7 +39,7 @@
 
 static bool CheckDexSupport() {
   if (std::string err_msg; !art_api::dex::TryLoadLibdexfile(&err_msg)) {
-    ALOGW("Failed to initialize DEX file support: %s", err_msg.c_str());
+    Log::Error("Failed to initialize DEX file support: %s", err_msg.c_str());
     return false;
   }
   return true;
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
index 6775530..3bb65da 100644
--- a/libunwindstack/DexFile.h
+++ b/libunwindstack/DexFile.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_DEX_FILE_H
-#define _LIBUNWINDSTACK_DEX_FILE_H
+#pragma once
 
 #include <stdint.h>
 
@@ -80,5 +79,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_DEX_FILE_H
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index bc99012..981f158 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -29,7 +29,7 @@
 template <>
 bool GlobalDebugInterface<DexFile>::Load(Maps* maps, std::shared_ptr<Memory>& memory, uint64_t addr,
                                          uint64_t size, /*out*/ std::shared_ptr<DexFile>& dex) {
-  dex = DexFile::Create(addr, size, memory.get(), maps->Find(addr));
+  dex = DexFile::Create(addr, size, memory.get(), maps->Find(addr).get());
   return dex.get() != nullptr;
 }
 
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 884acf5..c167cd4 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -93,7 +93,7 @@
       }
       case 3: {
         if (cie_loc_regs_ == nullptr) {
-          log(0, "restore while processing cie");
+          Log::Error("Invalid: restore while processing cie.");
           last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
           return false;
         }
@@ -197,8 +197,8 @@
     }
     raw_data += android::base::StringPrintf(" 0x%02x", value);
   }
-  log(indent, "DW_CFA_offset register(%d) %" PRId64, reg, offset);
-  log(indent, "%s", raw_data.c_str());
+  Log::Info(indent, "DW_CFA_offset register(%d) %" PRId64, reg, offset);
+  Log::Info(indent, "%s", raw_data.c_str());
   return true;
 }
 
@@ -208,11 +208,11 @@
   const auto* cfa = &DwarfCfaInfo::kTable[op];
   if (cfa->name[0] == '\0' || (arch_ != ARCH_ARM64 && op == 0x2d)) {
     if (op == 0x2d) {
-      log(indent, "Illegal (Only valid on aarch64)");
+      Log::Info(indent, "Illegal (Only valid on aarch64)");
     } else {
-      log(indent, "Illegal");
+      Log::Info(indent, "Illegal");
     }
-    log(indent, "Raw Data: 0x%02x", op);
+    Log::Info(indent, "Raw Data: 0x%02x", op);
     return true;
   }
 
@@ -239,7 +239,7 @@
       log_string += GetOperandString(cfa->display_operands[i], value, cur_pc);
     }
   }
-  log(indent, "%s", log_string.c_str());
+  Log::Info(indent, "%s", log_string.c_str());
 
   // Get the raw bytes of the data.
   uint64_t end_offset = memory_->cur_offset();
@@ -253,7 +253,7 @@
 
     // Only show 10 raw bytes per line.
     if ((i % 10) == 0 && i != 0) {
-      log(indent, "%s", raw_data.c_str());
+      Log::Info(indent, "%s", raw_data.c_str());
       raw_data.clear();
     }
     if (raw_data.empty()) {
@@ -262,12 +262,12 @@
     raw_data += android::base::StringPrintf(" 0x%02x", value);
   }
   if (!raw_data.empty()) {
-    log(indent, "%s", raw_data.c_str());
+    Log::Info(indent, "%s", raw_data.c_str());
   }
 
   // Log any of the expression data.
   for (const auto& line : expression_lines) {
-    log(indent + 1, "%s", line.c_str());
+    Log::Info(indent + 1, "%s", line.c_str());
   }
   return true;
 }
@@ -295,8 +295,8 @@
         }
         break;
       case 1:
-        log(indent, "DW_CFA_advance_loc %d", cfa_low);
-        log(indent, "Raw Data: 0x%02x", cfa_value);
+        Log::Info(indent, "DW_CFA_advance_loc %d", cfa_low);
+        Log::Info(indent, "Raw Data: 0x%02x", cfa_value);
         cur_pc += cfa_low * fde_->cie->code_alignment_factor;
         break;
       case 2:
@@ -305,13 +305,14 @@
         }
         break;
       case 3:
-        log(indent, "DW_CFA_restore register(%d)", cfa_low);
-        log(indent, "Raw Data: 0x%02x", cfa_value);
+        Log::Info(indent, "DW_CFA_restore register(%d)", cfa_low);
+        Log::Info(indent, "Raw Data: 0x%02x", cfa_value);
         break;
     }
     if (cur_pc != old_pc) {
-      log(0, "");
-      log(indent, "PC 0x%" PRIx64, cur_pc);
+      // This forces a newline or empty log line.
+      Log::Info("");
+      Log::Info(indent, "PC 0x%" PRIx64, cur_pc);
     }
     old_pc = cur_pc;
   }
@@ -324,16 +325,23 @@
   return true;
 }
 
-template <typename AddressType>
-bool DwarfCfa<AddressType>::cfa_set_loc(DwarfLocations*) {
-  AddressType cur_pc = cur_pc_;
-  AddressType new_pc = operands_[0];
+template <>
+bool DwarfCfa<uint32_t>::cfa_set_loc(DwarfLocations*) {
+  uint32_t cur_pc = cur_pc_;
+  uint32_t new_pc = operands_[0];
   if (new_pc < cur_pc) {
-    if (std::is_same<AddressType, uint32_t>::value) {
-      log(0, "Warning: PC is moving backwards: old 0x%" PRIx32 " new 0x%" PRIx32, cur_pc, new_pc);
-    } else {
-      log(0, "Warning: PC is moving backwards: old 0x%" PRIx64 " new 0x%" PRIx64, cur_pc, new_pc);
-    }
+    Log::Info("Warning: PC is moving backwards: old 0x%" PRIx32 " new 0x%" PRIx32, cur_pc, new_pc);
+  }
+  cur_pc_ = new_pc;
+  return true;
+}
+
+template <>
+bool DwarfCfa<uint64_t>::cfa_set_loc(DwarfLocations*) {
+  uint64_t cur_pc = cur_pc_;
+  uint64_t new_pc = operands_[0];
+  if (new_pc < cur_pc) {
+    Log::Info("Warning: PC is moving backwards: old 0x%" PRIx64 " new 0x%" PRIx64, cur_pc, new_pc);
   }
   cur_pc_ = new_pc;
   return true;
@@ -356,7 +364,7 @@
 bool DwarfCfa<AddressType>::cfa_restore(DwarfLocations* loc_regs) {
   AddressType reg = operands_[0];
   if (cie_loc_regs_ == nullptr) {
-    log(0, "restore while processing cie");
+    Log::Error("Invalid: restore while processing cie.");
     last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
     return false;
   }
@@ -400,7 +408,7 @@
 template <typename AddressType>
 bool DwarfCfa<AddressType>::cfa_restore_state(DwarfLocations* loc_regs) {
   if (loc_reg_state_.size() == 0) {
-    log(0, "Warning: Attempt to restore without remember.");
+    Log::Info("Warning: Attempt to restore without remember.");
     return true;
   }
   *loc_regs = loc_reg_state_.top();
@@ -418,7 +426,7 @@
 bool DwarfCfa<AddressType>::cfa_def_cfa_register(DwarfLocations* loc_regs) {
   auto cfa_location = loc_regs->find(CFA_REG);
   if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
-    log(0, "Attempt to set new register, but cfa is not already set to a register.");
+    Log::Error("Attempt to set new register, but cfa is not already set to a register.");
     last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
     return false;
   }
@@ -432,7 +440,7 @@
   // Changing the offset if this is not a register is illegal.
   auto cfa_location = loc_regs->find(CFA_REG);
   if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
-    log(0, "Attempt to set offset, but cfa is not set to a register.");
+    Log::Error("Attempt to set offset, but cfa is not set to a register.");
     last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
     return false;
   }
@@ -479,7 +487,7 @@
   // Changing the offset if this is not a register is illegal.
   auto cfa_location = loc_regs->find(CFA_REG);
   if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
-    log(0, "Attempt to set offset, but cfa is not set to a register.");
+    Log::Error("Attempt to set offset, but cfa is not set to a register.");
     last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
     return false;
   }
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index c4425bd..e9656e5 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_DWARF_CFA_H
-#define _LIBUNWINDSTACK_DWARF_CFA_H
+#pragma once
 
 #include <stdint.h>
 
@@ -270,5 +269,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_DWARF_CFA_H
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
index 635cefd..017568d 100644
--- a/libunwindstack/DwarfDebugFrame.h
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
-#define _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
+#pragma once
 
 #include <stdint.h>
 
@@ -46,5 +45,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
index 7a41e45..93befd4 100644
--- a/libunwindstack/DwarfEhFrame.h
+++ b/libunwindstack/DwarfEhFrame.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_DWARF_EH_FRAME_H
-#define _LIBUNWINDSTACK_DWARF_EH_FRAME_H
+#pragma once
 
 #include <stdint.h>
 
@@ -45,5 +44,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
index f7c010c..d074b7b 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.h
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
-#define _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
+#pragma once
 
 #include <stdint.h>
 
@@ -82,5 +81,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
diff --git a/libunwindstack/DwarfEncoding.h b/libunwindstack/DwarfEncoding.h
index 20db222..c9fbf24 100644
--- a/libunwindstack/DwarfEncoding.h
+++ b/libunwindstack/DwarfEncoding.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_DWARF_ENCODING_H
-#define _LIBUNWINDSTACK_DWARF_ENCODING_H
+#pragma once
 
 #include <stdint.h>
 
@@ -47,5 +46,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_DWARF_ENCODING_H
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
index ac9fd2d..2f33465 100644
--- a/libunwindstack/DwarfOp.h
+++ b/libunwindstack/DwarfOp.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_DWARF_OP_H
-#define _LIBUNWINDSTACK_DWARF_OP_H
+#pragma once
 
 #include <stdint.h>
 
@@ -140,5 +139,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_DWARF_OP_H
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 8a2997e..34d37b0 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -102,7 +102,8 @@
     }
 
     cie->cfa_instructions_end = memory_.cur_offset() + length64;
-    cie->fde_address_encoding = DW_EH_PE_sdata8;
+    // TODO(b/192012848): This is wrong. We need to propagate pointer size here.
+    cie->fde_address_encoding = DW_EH_PE_udata8;
 
     uint64_t cie_id;
     if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
@@ -118,7 +119,8 @@
   } else {
     // 32 bit Cie
     cie->cfa_instructions_end = memory_.cur_offset() + length32;
-    cie->fde_address_encoding = DW_EH_PE_sdata4;
+    // TODO(b/192012848): This is wrong. We need to propagate pointer size here.
+    cie->fde_address_encoding = DW_EH_PE_udata4;
 
     uint32_t cie_id;
     if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
@@ -161,8 +163,13 @@
   } while (aug_value != '\0');
 
   if (cie->version == 4 || cie->version == 5) {
-    // Skip the Address Size field since we only use it for validation.
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
+    char address_size;
+    if (!memory_.ReadBytes(&address_size, 1)) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    cie->fde_address_encoding = address_size == 8 ? DW_EH_PE_udata8 : DW_EH_PE_udata4;
 
     // Segment Size
     if (!memory_.ReadBytes(&cie->segment_size, 1)) {
@@ -667,7 +674,7 @@
 
     if (value64 == cie64_value_) {
       entry_is_cie = true;
-      cie_fde_encoding = DW_EH_PE_sdata8;
+      cie_fde_encoding = DW_EH_PE_udata8;
     } else {
       cie_offset = GetCieOffsetFromFde64(value64);
     }
@@ -683,7 +690,7 @@
 
     if (value32 == cie32_value_) {
       entry_is_cie = true;
-      cie_fde_encoding = DW_EH_PE_sdata4;
+      cie_fde_encoding = DW_EH_PE_udata4;
     } else {
       cie_offset = GetCieOffsetFromFde32(value32);
     }
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 7f4142b..44db356 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -15,21 +15,26 @@
  */
 
 #include <elf.h>
+#include <inttypes.h>
 #include <string.h>
+#include <sys/mman.h>
 
 #include <memory>
 #include <mutex>
 #include <string>
 #include <utility>
 
-#define LOG_TAG "unwind"
-#include <log/log.h>
+#include <android-base/stringprintf.h>
 
 #include <unwindstack/Elf.h>
 #include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
+#include <unwindstack/SharedString.h>
+
+#include <android-base/stringprintf.h>
 
 #include "ElfInterfaceArm.h"
 #include "Symbols.h"
@@ -37,7 +42,7 @@
 namespace unwindstack {
 
 bool Elf::cache_enabled_;
-std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* Elf::cache_;
+std::unordered_map<std::string, std::unordered_map<uint64_t, std::shared_ptr<Elf>>>* Elf::cache_;
 std::mutex* Elf::cache_lock_;
 
 bool Elf::Init() {
@@ -302,7 +307,6 @@
       interface.reset(new ElfInterface32(memory));
     } else {
       // Unsupported.
-      ALOGI("32 bit elf that is neither arm nor x86 nor mips: e_machine = %d\n", e_machine);
       return nullptr;
     }
   } else if (class_type_ == ELFCLASS64) {
@@ -320,8 +324,6 @@
       arch_ = ARCH_MIPS64;
     } else {
       // Unsupported.
-      ALOGI("64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = %d\n",
-            e_machine);
       return nullptr;
     }
     interface.reset(new ElfInterface64(memory));
@@ -351,7 +353,8 @@
 void Elf::SetCachingEnabled(bool enable) {
   if (!cache_enabled_ && enable) {
     cache_enabled_ = true;
-    cache_ = new std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>;
+    cache_ =
+        new std::unordered_map<std::string, std::unordered_map<uint64_t, std::shared_ptr<Elf>>>;
     cache_lock_ = new std::mutex;
   } else if (cache_enabled_ && !enable) {
     cache_enabled_ = false;
@@ -369,58 +372,49 @@
 }
 
 void Elf::CacheAdd(MapInfo* info) {
-  // If elf_offset != 0, then cache both name:offset and name.
-  // The cached name is used to do lookups if multiple maps for the same
-  // named elf file exist.
-  // For example, if there are two maps boot.odex:1000 and boot.odex:2000
-  // where each reference the entire boot.odex, the cache will properly
-  // use the same cached elf object.
-
-  if (info->offset() == 0 || info->elf_offset() != 0) {
-    (*cache_)[info->name()] = std::make_pair(info->elf(), true);
+  if (!info->elf()->valid()) {
+    return;
   }
-
-  if (info->offset() != 0) {
-    // The second element in the pair indicates whether elf_offset should
-    // be set to offset when getting out of the cache.
-    std::string key = std::string(info->name()) + ':' + std::to_string(info->offset());
-    (*cache_)[key] = std::make_pair(info->elf(), info->elf_offset() != 0);
-  }
-}
-
-bool Elf::CacheAfterCreateMemory(MapInfo* info) {
-  if (info->name().empty() || info->offset() == 0 || info->elf_offset() == 0) {
-    return false;
-  }
-
-  auto entry = cache_->find(info->name());
-  if (entry == cache_->end()) {
-    return false;
-  }
-
-  // In this case, the whole file is the elf, and the name has already
-  // been cached. Add an entry at name:offset to get this directly out
-  // of the cache next time.
-  info->set_elf(entry->second.first);
-  std::string key = std::string(info->name()) + ':' + std::to_string(info->offset());
-  (*cache_)[key] = std::make_pair(info->elf(), true);
-  return true;
+  (*cache_)[std::string(info->name())].emplace(info->elf_start_offset(), info->elf());
 }
 
 bool Elf::CacheGet(MapInfo* info) {
-  std::string name(info->name());
-  if (info->offset() != 0) {
-    name += ':' + std::to_string(info->offset());
+  auto name_entry = cache_->find(std::string(info->name()));
+  if (name_entry == cache_->end()) {
+    return false;
   }
-  auto entry = cache_->find(name);
-  if (entry != cache_->end()) {
-    info->set_elf(entry->second.first);
-    if (entry->second.second) {
-      info->set_elf_offset(info->offset());
+  // First look to see if there is a zero offset entry, this indicates
+  // the whole elf is the file.
+  auto& offset_cache = name_entry->second;
+  uint64_t elf_start_offset = 0;
+  auto entry = offset_cache.find(elf_start_offset);
+  if (entry == offset_cache.end()) {
+    // Try and find using the current offset.
+    elf_start_offset = info->offset();
+    entry = offset_cache.find(elf_start_offset);
+    if (entry == offset_cache.end()) {
+      // If this is an execute map, then see if the previous read-only
+      // map is the start of the elf.
+      if (!(info->flags() & PROT_EXEC)) {
+        return false;
+      }
+      auto prev_map = info->GetPrevRealMap();
+      if (prev_map == nullptr || info->offset() <= prev_map->offset() ||
+          (prev_map->flags() != PROT_READ)) {
+        return false;
+      }
+      elf_start_offset = prev_map->offset();
+      entry = offset_cache.find(elf_start_offset);
+      if (entry == offset_cache.end()) {
+        return false;
+      }
     }
-    return true;
   }
-  return false;
+
+  info->set_elf(entry->second);
+  info->set_elf_start_offset(elf_start_offset);
+  info->set_elf_offset(info->offset() - elf_start_offset);
+  return true;
 }
 
 std::string Elf::GetBuildID(Memory* memory) {
@@ -441,4 +435,21 @@
   return "";
 }
 
+std::string Elf::GetPrintableBuildID(std::string& build_id) {
+  if (build_id.empty()) {
+    return "";
+  }
+  std::string printable_build_id;
+  for (const char& c : build_id) {
+    // Use %hhx to avoid sign extension on abis that have signed chars.
+    printable_build_id += android::base::StringPrintf("%02hhx", c);
+  }
+  return printable_build_id;
+}
+
+std::string Elf::GetPrintableBuildID() {
+  std::string build_id = GetBuildID();
+  return Elf::GetPrintableBuildID(build_id);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 8268ed7..0389198 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -21,10 +21,6 @@
 #include <string>
 #include <utility>
 
-#include <7zCrc.h>
-#include <Xz.h>
-#include <XzCrc64.h>
-
 #include <unwindstack/DwarfError.h>
 #include <unwindstack/DwarfSection.h>
 #include <unwindstack/ElfInterface.h>
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index 9bfad0b..6ee6dc9 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
-#define _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
+#pragma once
 
 #include <elf.h>
 #include <stdint.h>
@@ -94,5 +93,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
index b8adc5c..0183bd3 100644
--- a/libunwindstack/Global.cpp
+++ b/libunwindstack/Global.cpp
@@ -21,6 +21,8 @@
 #include <string>
 #include <vector>
 
+#include <android-base/file.h>
+
 #include <unwindstack/Global.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
@@ -48,7 +50,7 @@
     return false;
   }
 
-  const char* base_name = basename(name.c_str());
+  std::string base_name = android::base::Basename(name);
   for (const std::string& lib : search_libs_) {
     if (base_name == lib) {
       return true;
@@ -77,8 +79,7 @@
   //   f3000-f4000 2000 rw- /system/lib/libc.so
   MapInfo* map_zero = nullptr;
   for (const auto& info : *maps) {
-    if (info->offset() != 0 &&
-        (info->flags() & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE) &&
+    if ((info->flags() & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE) &&
         map_zero != nullptr && Searchable(info->name()) && info->name() == map_zero->name()) {
       Elf* elf = map_zero->GetElf(memory_, arch());
       uint64_t ptr;
diff --git a/libunwindstack/GlobalDebugImpl.h b/libunwindstack/GlobalDebugImpl.h
index db8068d..dcea0ef 100644
--- a/libunwindstack/GlobalDebugImpl.h
+++ b/libunwindstack/GlobalDebugImpl.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_GLOBAL_DEBUG_IMPL_H
-#define _LIBUNWINDSTACK_GLOBAL_DEBUG_IMPL_H
+#pragma once
 
 #include <stdint.h>
 #include <string.h>
@@ -435,5 +434,3 @@
 }
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_GLOBAL_DEBUG_IMPL_H
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
deleted file mode 100644
index 466dd68..0000000
--- a/libunwindstack/LocalUnwinder.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <pthread.h>
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <unwindstack/Elf.h>
-#include <unwindstack/LocalUnwinder.h>
-#include <unwindstack/MapInfo.h>
-#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
-#include <unwindstack/Regs.h>
-#include <unwindstack/RegsGetLocal.h>
-
-namespace unwindstack {
-
-bool LocalUnwinder::Init() {
-  pthread_rwlock_init(&maps_rwlock_, nullptr);
-
-  // Create the maps.
-  maps_.reset(new unwindstack::LocalUpdatableMaps());
-  if (!maps_->Parse()) {
-    maps_.reset();
-    return false;
-  }
-
-  process_memory_ = unwindstack::Memory::CreateProcessMemoryThreadCached(getpid());
-
-  return true;
-}
-
-bool LocalUnwinder::ShouldSkipLibrary(const std::string& map_name) {
-  for (const std::string& skip_library : skip_libraries_) {
-    if (skip_library == map_name) {
-      return true;
-    }
-  }
-  return false;
-}
-
-bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
-  std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
-  unwindstack::RegsGetLocal(regs.get());
-  ArchEnum arch = regs->Arch();
-
-  // Clear any cached data from previous unwinds.
-  process_memory_->Clear();
-
-  size_t num_frames = 0;
-  bool adjust_pc = false;
-  while (true) {
-    uint64_t cur_pc = regs->pc();
-    uint64_t cur_sp = regs->sp();
-
-    MapInfo* map_info = maps_->Find(cur_pc);
-    if (map_info == nullptr) {
-      break;
-    }
-
-    Elf* elf = map_info->GetElf(process_memory_, arch);
-    uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
-    uint64_t step_pc = rel_pc;
-    uint64_t pc_adjustment;
-    if (adjust_pc) {
-      pc_adjustment = GetPcAdjustment(rel_pc, elf, arch);
-    } else {
-      pc_adjustment = 0;
-    }
-    step_pc -= pc_adjustment;
-
-    bool finished = false;
-    bool is_signal_frame = false;
-    if (elf->StepIfSignalHandler(rel_pc, regs.get(), process_memory_.get())) {
-      step_pc = rel_pc;
-    } else if (!elf->Step(step_pc, regs.get(), process_memory_.get(), &finished,
-                          &is_signal_frame)) {
-      finished = true;
-    }
-
-    // Skip any locations that are within this library.
-    if (num_frames != 0 || !ShouldSkipLibrary(map_info->name())) {
-      // Add frame information.
-      SharedString func_name;
-      uint64_t func_offset;
-      if (elf->GetFunctionName(rel_pc, &func_name, &func_offset)) {
-        frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment,
-                                 func_name, func_offset);
-      } else {
-        frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment, "", 0);
-      }
-      num_frames++;
-    }
-
-    if (finished || frame_info->size() == max_frames ||
-        (cur_pc == regs->pc() && cur_sp == regs->sp())) {
-      break;
-    }
-    adjust_pc = true;
-  }
-  return num_frames != 0;
-}
-
-}  // namespace unwindstack
diff --git a/libunwindstack/Log.cpp b/libunwindstack/LogAndroid.cpp
similarity index 66%
rename from libunwindstack/Log.cpp
rename to libunwindstack/LogAndroid.cpp
index 3bf73e1..a2bfc39 100644
--- a/libunwindstack/Log.cpp
+++ b/libunwindstack/LogAndroid.cpp
@@ -32,45 +32,51 @@
 
 namespace unwindstack {
 
-static bool g_print_to_stdout = false;
-
-void log_to_stdout(bool enable) {
-  g_print_to_stdout = enable;
-}
+namespace Log {
 
 // Send the data to the log.
-void log(uint8_t indent, const char* format, ...) {
+static void LogWithPriority(int priority, uint8_t indent, const char* format, va_list args) {
   std::string real_format;
   if (indent > 0) {
     real_format = android::base::StringPrintf("%*s%s", 2 * indent, " ", format);
   } else {
     real_format = format;
   }
+  LOG_PRI_VA(priority, LOG_TAG, real_format.c_str(), args);
+}
+
+void Info(const char* format, ...) {
   va_list args;
   va_start(args, format);
-  if (g_print_to_stdout) {
-    real_format += '\n';
-    vprintf(real_format.c_str(), args);
-  } else {
-    LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, real_format.c_str(), args);
-  }
+  LogWithPriority(ANDROID_LOG_INFO, 0, format, args);
+  va_end(args);
+}
+
+void Info(uint8_t indent, const char* format, ...) {
+  va_list args;
+  va_start(args, format);
+  LogWithPriority(ANDROID_LOG_INFO, indent, format, args);
+  va_end(args);
+}
+
+void Error(const char* format, ...) {
+  va_list args;
+  va_start(args, format);
+  LogWithPriority(ANDROID_LOG_ERROR, 0, format, args);
   va_end(args);
 }
 
 #if defined(__BIONIC__)
-void log_async_safe(const char* format, ...) {
-  if (g_print_to_stdout) {
-    // Printing to stdout is never async safe, so throw the message away.
-    return;
-  }
-
+void AsyncSafe(const char* format, ...) {
   va_list args;
   va_start(args, format);
   async_safe_format_log_va_list(ANDROID_LOG_ERROR, "libunwindstack", format, args);
   va_end(args);
 }
 #else
-void log_async_safe(const char*, ...) {}
+void AsyncSafe(const char*, ...) {}
 #endif
 
+}  // namespace Log
+
 }  // namespace unwindstack
diff --git a/libunwindstack/LogStdout.cpp b/libunwindstack/LogStdout.cpp
new file mode 100644
index 0000000..6ce06f8
--- /dev/null
+++ b/libunwindstack/LogStdout.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 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 <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/Log.h>
+
+namespace unwindstack {
+
+namespace Log {
+
+static void PrintToStdout(uint8_t indent, const char* format, va_list args) {
+  std::string real_format;
+  if (indent > 0) {
+    real_format = android::base::StringPrintf("%*s%s", 2 * indent, " ", format);
+  } else {
+    real_format = format;
+  }
+  real_format += '\n';
+
+  vprintf(real_format.c_str(), args);
+}
+
+void Info(const char* format, ...) {
+  va_list args;
+  va_start(args, format);
+  PrintToStdout(0, format, args);
+  va_end(args);
+}
+
+void Info(uint8_t indent, const char* format, ...) {
+  va_list args;
+  va_start(args, format);
+  PrintToStdout(indent, format, args);
+  va_end(args);
+}
+
+void Error(const char* format, ...) {
+  va_list args;
+  va_start(args, format);
+  PrintToStdout(0, format, args);
+  va_end(args);
+}
+
+// Do nothing for async safe.
+void AsyncSafe(const char*, ...) {}
+
+}  // namespace Log
+
+}  // namespace unwindstack
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 8fb6f7e..6db1183 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -23,7 +23,7 @@
 #include <mutex>
 #include <string>
 
-#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
@@ -34,15 +34,55 @@
 
 namespace unwindstack {
 
+bool MapInfo::ElfFileNotReadable() {
+  const std::string& map_name = name();
+  return memory_backed_elf() && !map_name.empty() && map_name[0] != '[' &&
+         !android::base::StartsWith(map_name, "/memfd:");
+}
+
+std::shared_ptr<MapInfo> MapInfo::GetPrevRealMap() {
+  if (name().empty()) {
+    return nullptr;
+  }
+
+  for (auto prev = prev_map(); prev != nullptr; prev = prev->prev_map()) {
+    if (!prev->IsBlank()) {
+      if (prev->name() == name()) {
+        return prev;
+      }
+      return nullptr;
+    }
+  }
+  return nullptr;
+}
+
+std::shared_ptr<MapInfo> MapInfo::GetNextRealMap() {
+  if (name().empty()) {
+    return nullptr;
+  }
+
+  for (auto next = next_map(); next != nullptr; next = next->next_map()) {
+    if (!next->IsBlank()) {
+      if (next->name() == name()) {
+        return next;
+      }
+      return nullptr;
+    }
+  }
+  return nullptr;
+}
+
 bool MapInfo::InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory) {
   // One last attempt, see if the previous map is read-only with the
   // same name and stretches across this map.
-  if (prev_real_map() == nullptr || prev_real_map()->flags() != PROT_READ) {
+  auto prev_real_map = GetPrevRealMap();
+  if (prev_real_map == nullptr || prev_real_map->flags() != PROT_READ ||
+      prev_real_map->offset() >= offset()) {
     return false;
   }
 
-  uint64_t map_size = end() - prev_real_map()->end();
-  if (!memory->Init(name(), prev_real_map()->offset(), map_size)) {
+  uint64_t map_size = end() - prev_real_map->end();
+  if (!memory->Init(name(), prev_real_map->offset(), map_size)) {
     return false;
   }
 
@@ -51,16 +91,21 @@
     return false;
   }
 
-  if (!memory->Init(name(), prev_real_map()->offset(), max_size)) {
+  if (!memory->Init(name(), prev_real_map->offset(), max_size)) {
     return false;
   }
 
-  set_elf_offset(offset() - prev_real_map()->offset());
-  set_elf_start_offset(prev_real_map()->offset());
+  set_elf_offset(offset() - prev_real_map->offset());
+  set_elf_start_offset(prev_real_map->offset());
   return true;
 }
 
 Memory* MapInfo::GetFileMemory() {
+  // Fail on device maps.
+  if (flags() & MAPS_FLAGS_DEVICE_MAP) {
+    return nullptr;
+  }
+
   std::unique_ptr<MemoryFileAtOffset> memory(new MemoryFileAtOffset);
   if (offset() == 0) {
     if (memory->Init(name(), 0)) {
@@ -83,7 +128,11 @@
   // and reinit to that size. This is needed because the dynamic linker
   // only maps in a portion of the original elf, and never the symbol
   // file data.
-  uint64_t map_size = end() - start();
+  //
+  // For maps with MAPS_FLAGS_JIT_SYMFILE_MAP, the map range is for a JIT function,
+  // which can be smaller than elf header size. So make sure map_size is large enough
+  // to read elf header.
+  uint64_t map_size = std::max<uint64_t>(end() - start(), sizeof(ElfTypes64::Ehdr));
   if (!memory->Init(name(), offset(), map_size)) {
     return nullptr;
   }
@@ -109,13 +158,6 @@
   // No elf at offset, try to init as if the whole file is an elf.
   if (memory->Init(name(), 0) && Elf::IsValidElf(memory.get())) {
     set_elf_offset(offset());
-    // Need to check how to set the elf start offset. If this map is not
-    // the r-x map of a r-- map, then use the real offset value. Otherwise,
-    // use 0.
-    if (prev_real_map() == nullptr || prev_real_map()->offset() != 0 ||
-        prev_real_map()->flags() != PROT_READ || prev_real_map()->name() != name()) {
-      set_elf_start_offset(offset());
-    }
     return memory.release();
   }
 
@@ -166,10 +208,13 @@
   // option is used.
   std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start(), end() - start(), 0));
   if (Elf::IsValidElf(memory.get())) {
+    set_elf_start_offset(offset());
+
+    auto next_real_map = GetNextRealMap();
+
     // Might need to peek at the next map to create a memory object that
     // includes that map too.
-    if (offset() != 0 || name().empty() || next_real_map() == nullptr ||
-        offset() >= next_real_map()->offset() || next_real_map()->name() != name()) {
+    if (offset() != 0 || next_real_map == nullptr || offset() >= next_real_map->offset()) {
       return memory.release();
     }
 
@@ -179,95 +224,103 @@
     // be discarded.
     MemoryRanges* ranges = new MemoryRanges;
     ranges->Insert(new MemoryRange(process_memory, start(), end() - start(), 0));
-    ranges->Insert(new MemoryRange(process_memory, next_real_map()->start(),
-                                   next_real_map()->end() - next_real_map()->start(),
-                                   next_real_map()->offset() - offset()));
+    ranges->Insert(new MemoryRange(process_memory, next_real_map->start(),
+                                   next_real_map->end() - next_real_map->start(),
+                                   next_real_map->offset() - offset()));
 
     return ranges;
   }
 
+  auto prev_real_map = GetPrevRealMap();
+
   // Find the read-only map by looking at the previous map. The linker
   // doesn't guarantee that this invariant will always be true. However,
   // if that changes, there is likely something else that will change and
   // break something.
-  if (offset() == 0 || name().empty() || prev_real_map() == nullptr ||
-      prev_real_map()->name() != name() || prev_real_map()->offset() >= offset()) {
+  if (offset() == 0 || prev_real_map == nullptr || prev_real_map->offset() >= offset()) {
     set_memory_backed_elf(false);
     return nullptr;
   }
 
   // Make sure that relative pc values are corrected properly.
-  set_elf_offset(offset() - prev_real_map()->offset());
+  set_elf_offset(offset() - prev_real_map->offset());
   // Use this as the elf start offset, otherwise, you always get offsets into
   // the r-x section, which is not quite the right information.
-  set_elf_start_offset(prev_real_map()->offset());
+  set_elf_start_offset(prev_real_map->offset());
 
-  MemoryRanges* ranges = new MemoryRanges;
-  ranges->Insert(new MemoryRange(process_memory, prev_real_map()->start(),
-                                 prev_real_map()->end() - prev_real_map()->start(), 0));
-  ranges->Insert(new MemoryRange(process_memory, start(), end() - start(), elf_offset()));
-
-  return ranges;
+  std::unique_ptr<MemoryRanges> ranges(new MemoryRanges);
+  if (!ranges->Insert(new MemoryRange(process_memory, prev_real_map->start(),
+                                      prev_real_map->end() - prev_real_map->start(), 0))) {
+    return nullptr;
+  }
+  if (!ranges->Insert(new MemoryRange(process_memory, start(), end() - start(), elf_offset()))) {
+    return nullptr;
+  }
+  return ranges.release();
 }
 
-Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch) {
-  {
-    // Make sure no other thread is trying to add the elf to this map.
-    std::lock_guard<std::mutex> guard(elf_mutex());
+class ScopedElfCacheLock {
+ public:
+  ScopedElfCacheLock() {
+    if (Elf::CachingEnabled()) Elf::CacheLock();
+  }
+  ~ScopedElfCacheLock() {
+    if (Elf::CachingEnabled()) Elf::CacheUnlock();
+  }
+};
 
-    if (elf().get() != nullptr) {
+Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch) {
+  // Make sure no other thread is trying to add the elf to this map.
+  std::lock_guard<std::mutex> guard(elf_mutex());
+
+  if (elf().get() != nullptr) {
+    return elf().get();
+  }
+
+  ScopedElfCacheLock elf_cache_lock;
+  if (Elf::CachingEnabled() && !name().empty()) {
+    if (Elf::CacheGet(this)) {
       return elf().get();
     }
+  }
 
-    bool locked = false;
-    if (Elf::CachingEnabled() && !name().empty()) {
-      Elf::CacheLock();
-      locked = true;
-      if (Elf::CacheGet(this)) {
-        Elf::CacheUnlock();
-        return elf().get();
-      }
-    }
-
-    Memory* memory = CreateMemory(process_memory);
-    if (locked) {
-      if (Elf::CacheAfterCreateMemory(this)) {
-        delete memory;
-        Elf::CacheUnlock();
-        return elf().get();
-      }
-    }
-    elf().reset(new Elf(memory));
-    // If the init fails, keep the elf around as an invalid object so we
-    // don't try to reinit the object.
-    elf()->Init();
-    if (elf()->valid() && expected_arch != elf()->arch()) {
-      // Make the elf invalid, mismatch between arch and expected arch.
-      elf()->Invalidate();
-    }
-
-    if (locked) {
-      Elf::CacheAdd(this);
-      Elf::CacheUnlock();
-    }
+  elf().reset(new Elf(CreateMemory(process_memory)));
+  // If the init fails, keep the elf around as an invalid object so we
+  // don't try to reinit the object.
+  elf()->Init();
+  if (elf()->valid() && expected_arch != elf()->arch()) {
+    // Make the elf invalid, mismatch between arch and expected arch.
+    elf()->Invalidate();
   }
 
   if (!elf()->valid()) {
     set_elf_start_offset(offset());
-  } else if (prev_real_map() != nullptr && elf_start_offset() != offset() &&
-             prev_real_map()->offset() == elf_start_offset() && prev_real_map()->name() == name()) {
+  } else if (auto prev_real_map = GetPrevRealMap(); prev_real_map != nullptr &&
+                                                    prev_real_map->flags() == PROT_READ &&
+                                                    prev_real_map->offset() < offset()) {
     // If there is a read-only map then a read-execute map that represents the
     // same elf object, make sure the previous map is using the same elf
-    // object if it hasn't already been set.
-    std::lock_guard<std::mutex> guard(prev_real_map()->elf_mutex());
-    if (prev_real_map()->elf().get() == nullptr) {
-      prev_real_map()->set_elf(elf());
-      prev_real_map()->set_memory_backed_elf(memory_backed_elf());
-    } else {
+    // object if it hasn't already been set. Locking this should not result
+    // in a deadlock as long as the invariant that the code only ever tries
+    // to lock the previous real map holds true.
+    std::lock_guard<std::mutex> guard(prev_real_map->elf_mutex());
+    if (prev_real_map->elf() == nullptr) {
+      // Need to verify if the map is the previous read-only map.
+      prev_real_map->set_elf(elf());
+      prev_real_map->set_memory_backed_elf(memory_backed_elf());
+      prev_real_map->set_elf_start_offset(elf_start_offset());
+      prev_real_map->set_elf_offset(prev_real_map->offset() - elf_start_offset());
+    } else if (prev_real_map->elf_start_offset() == elf_start_offset()) {
       // Discard this elf, and use the elf from the previous map instead.
-      set_elf(prev_real_map()->elf());
+      set_elf(prev_real_map->elf());
     }
   }
+
+  // Cache the elf only after all of the above checks since we might
+  // discard the original elf we created.
+  if (Elf::CachingEnabled()) {
+    Elf::CacheAdd(this);
+  }
   return elf().get();
 }
 
@@ -284,25 +337,31 @@
   return elf()->GetFunctionName(addr, name, func_offset);
 }
 
-uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
-  int64_t cur_load_bias = load_bias().load();
-  if (cur_load_bias != INT64_MAX) {
+uint64_t MapInfo::GetLoadBias() {
+  uint64_t cur_load_bias = load_bias().load();
+  if (cur_load_bias != UINT64_MAX) {
     return cur_load_bias;
   }
 
-  {
-    // Make sure no other thread is trying to add the elf to this map.
-    std::lock_guard<std::mutex> guard(elf_mutex());
-    if (elf() != nullptr) {
-      if (elf()->valid()) {
-        cur_load_bias = elf()->GetLoadBias();
-        set_load_bias(cur_load_bias);
-        return cur_load_bias;
-      } else {
-        set_load_bias(0);
-        return 0;
-      }
-    }
+  Elf* elf_obj = GetElfObj();
+  if (elf_obj == nullptr) {
+    return UINT64_MAX;
+  }
+
+  if (elf_obj->valid()) {
+    cur_load_bias = elf_obj->GetLoadBias();
+    set_load_bias(cur_load_bias);
+    return cur_load_bias;
+  }
+
+  set_load_bias(0);
+  return 0;
+}
+
+uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
+  uint64_t cur_load_bias = GetLoadBias();
+  if (cur_load_bias != UINT64_MAX) {
+    return cur_load_bias;
   }
 
   // Call lightweight static function that will only read enough of the
@@ -321,6 +380,23 @@
   }
 }
 
+std::string MapInfo::GetFullName() {
+  Elf* elf_obj = GetElfObj();
+  if (elf_obj == nullptr || elf_start_offset() == 0 || name().empty()) {
+    return name();
+  }
+
+  std::string soname = elf_obj->GetSoname();
+  if (soname.empty()) {
+    return name();
+  }
+
+  std::string full_name(name());
+  full_name += '!';
+  full_name += soname;
+  return full_name;
+}
+
 SharedString MapInfo::GetBuildID() {
   SharedString* id = build_id().load();
   if (id != nullptr) {
@@ -331,12 +407,8 @@
   // time it should be detected and only one thread should win and
   // save the data.
 
-  // Now need to see if the elf object exists.
-  // Make sure no other thread is trying to add the elf to this map.
-  elf_mutex().lock();
-  Elf* elf_obj = elf().get();
-  elf_mutex().unlock();
   std::string result;
+  Elf* elf_obj = GetElfObj();
   if (elf_obj != nullptr) {
     result = elf_obj->GetBuildID();
   } else {
@@ -382,15 +454,7 @@
 
 std::string MapInfo::GetPrintableBuildID() {
   std::string raw_build_id = GetBuildID();
-  if (raw_build_id.empty()) {
-    return "";
-  }
-  std::string printable_build_id;
-  for (const char& c : raw_build_id) {
-    // Use %hhx to avoid sign extension on abis that have signed chars.
-    printable_build_id += android::base::StringPrintf("%02hhx", c);
-  }
-  return printable_build_id;
+  return Elf::GetPrintableBuildID(raw_build_id);
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 7a9e2e9..9527176 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -39,7 +39,7 @@
 
 namespace unwindstack {
 
-MapInfo* Maps::Find(uint64_t pc) {
+std::shared_ptr<MapInfo> Maps::Find(uint64_t pc) {
   if (maps_.empty()) {
     return nullptr;
   }
@@ -49,7 +49,7 @@
     size_t index = (first + last) / 2;
     const auto& cur = maps_[index];
     if (pc >= cur->start() && pc < cur->end()) {
-      return cur.get();
+      return cur;
     } else if (pc < cur->start()) {
       last = index;
     } else {
@@ -60,8 +60,7 @@
 }
 
 bool Maps::Parse() {
-  MapInfo* prev_map = nullptr;
-  MapInfo* prev_real_map = nullptr;
+  std::shared_ptr<MapInfo> prev_map;
   return android::procinfo::ReadMapFile(GetMapsFile(),
                       [&](const android::procinfo::MapInfo& mapinfo) {
     // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
@@ -70,52 +69,53 @@
         strncmp(mapinfo.name.c_str() + 5, "ashmem/", 7) != 0) {
       flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
     }
-    maps_.emplace_back(new MapInfo(prev_map, prev_real_map, mapinfo.start, mapinfo.end,
-                                   mapinfo.pgoff, flags, mapinfo.name));
-    prev_map = maps_.back().get();
-    if (!prev_map->IsBlank()) {
-      prev_real_map = prev_map;
-    }
+    maps_.emplace_back(
+        MapInfo::Create(prev_map, mapinfo.start, mapinfo.end, mapinfo.pgoff, flags, mapinfo.name));
+    prev_map = maps_.back();
   });
 }
 
 void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
-               const std::string& name, uint64_t load_bias) {
-  MapInfo* prev_map = maps_.empty() ? nullptr : maps_.back().get();
-  MapInfo* prev_real_map = prev_map;
-  while (prev_real_map != nullptr && prev_real_map->IsBlank()) {
-    prev_real_map = prev_real_map->prev_map();
-  }
+               const std::string& name) {
+  std::shared_ptr<MapInfo> prev_map(maps_.empty() ? nullptr : maps_.back());
+  auto map_info = MapInfo::Create(prev_map, start, end, offset, flags, name);
+  maps_.emplace_back(std::move(map_info));
+}
 
-  auto map_info =
-      std::make_unique<MapInfo>(prev_map, prev_real_map, start, end, offset, flags, name);
+void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+               const std::string& name, uint64_t load_bias) {
+  std::shared_ptr<MapInfo> prev_map(maps_.empty() ? nullptr : maps_.back());
+  auto map_info = MapInfo::Create(prev_map, start, end, offset, flags, name);
   map_info->set_load_bias(load_bias);
   maps_.emplace_back(std::move(map_info));
 }
 
 void Maps::Sort() {
+  if (maps_.empty()) {
+    return;
+  }
+
   std::sort(maps_.begin(), maps_.end(),
-            [](const std::unique_ptr<MapInfo>& a, const std::unique_ptr<MapInfo>& b) {
+            [](const std::shared_ptr<MapInfo>& a, const std::shared_ptr<MapInfo>& b) {
               return a->start() < b->start();
             });
 
-  // Set the prev_map values on the info objects.
-  MapInfo* prev_map = nullptr;
-  MapInfo* prev_real_map = nullptr;
-  for (const auto& map_info : maps_) {
+  // Set prev_map and next_map on the info objects.
+  std::shared_ptr<MapInfo> prev_map;
+  // Set the last next_map to nullptr.
+  maps_.back()->set_next_map(prev_map);
+  for (auto& map_info : maps_) {
     map_info->set_prev_map(prev_map);
-    map_info->set_prev_real_map(prev_real_map);
-    prev_map = map_info.get();
-    if (!prev_map->IsBlank()) {
-      prev_real_map = prev_map;
+    if (prev_map) {
+      prev_map->set_next_map(map_info);
     }
+    prev_map = map_info;
   }
 }
 
 bool BufferMaps::Parse() {
   std::string content(buffer_);
-  MapInfo* prev_map = nullptr;
-  MapInfo* prev_real_map = nullptr;
+  std::shared_ptr<MapInfo> prev_map;
   return android::procinfo::ReadMapFileContent(
       &content[0], [&](const android::procinfo::MapInfo& mapinfo) {
         // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
@@ -124,12 +124,9 @@
             strncmp(mapinfo.name.c_str() + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        maps_.emplace_back(new MapInfo(prev_map, prev_real_map, mapinfo.start, mapinfo.end,
-                                       mapinfo.pgoff, flags, mapinfo.name));
-        prev_map = maps_.back().get();
-        if (!prev_map->IsBlank()) {
-          prev_real_map = prev_map;
-        }
+        maps_.emplace_back(MapInfo::Create(prev_map, mapinfo.start, mapinfo.end, mapinfo.pgoff,
+                                           flags, mapinfo.name));
+        prev_map = maps_.back();
       });
 }
 
@@ -145,9 +142,9 @@
   pthread_rwlock_init(&maps_rwlock_, nullptr);
 }
 
-MapInfo* LocalUpdatableMaps::Find(uint64_t pc) {
+std::shared_ptr<MapInfo> LocalUpdatableMaps::Find(uint64_t pc) {
   pthread_rwlock_rdlock(&maps_rwlock_);
-  MapInfo* map_info = Maps::Find(pc);
+  std::shared_ptr<MapInfo> map_info = Maps::Find(pc);
   pthread_rwlock_unlock(&maps_rwlock_);
 
   if (map_info == nullptr) {
@@ -191,13 +188,22 @@
       auto& info = maps_[old_map_idx];
       if (start == info->start() && end == info->end() && flags == info->flags() &&
           name == info->name()) {
-        // No need to check
         search_map_idx = old_map_idx + 1;
-        if (new_map_idx + 1 < maps_.size()) {
-          maps_[new_map_idx + 1]->set_prev_map(info.get());
-          maps_[new_map_idx + 1]->set_prev_real_map(info->IsBlank() ? info->prev_real_map()
-                                                                    : info.get());
+        // Since we are throwing away a map from the new list, need to
+        // adjust the next/prev pointers in the old map entry.
+        auto prev = new_map_info->prev_map();
+        auto next = new_map_info->next_map();
+        info->set_prev_map(prev);
+        info->set_next_map(next);
+
+        // Fix up the pointers in the prev and next entries.
+        if (prev != nullptr) {
+          prev->set_next_map(info);
         }
+        if (next != nullptr) {
+          next->set_prev_map(info);
+        }
+
         maps_[new_map_idx] = nullptr;
         num_deleted_new_entries++;
         break;
@@ -210,7 +216,9 @@
       // Never delete these maps, they may be in use. The assumption is
       // that there will only every be a handful of these so waiting
       // to destroy them is not too expensive.
-      saved_maps_.emplace_back(std::move(info));
+      // Since these are all shared_ptrs, we can just remove the references.
+      // Any code still holding on to the pointer, will still have a
+      // valid pointer after this.
       search_map_idx = old_map_idx + 1;
       maps_[old_map_idx] = nullptr;
       num_deleted_old_entries++;
@@ -220,9 +228,7 @@
     }
   }
 
-  // Now move out any of the maps that never were found.
   for (size_t i = search_map_idx; i < last_map_idx; i++) {
-    saved_maps_.emplace_back(std::move(maps_[i]));
     maps_[i] = nullptr;
     num_deleted_old_entries++;
   }
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index e1fe0e1..5cfe2f0 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -25,21 +25,17 @@
 #include <sys/uio.h>
 #include <unistd.h>
 
-#include <7zCrc.h>
-#include <Xz.h>
-#include <XzCrc64.h>
-
 #include <algorithm>
 #include <memory>
 #include <mutex>
 #include <optional>
+#include <string>
 
 #include <android-base/unique_fd.h>
 
 #include <unwindstack/Log.h>
 #include <unwindstack/Memory.h>
 
-#include "Check.h"
 #include "MemoryBuffer.h"
 #include "MemoryCache.h"
 #include "MemoryFileAtOffset.h"
@@ -48,16 +44,9 @@
 #include "MemoryOfflineBuffer.h"
 #include "MemoryRange.h"
 #include "MemoryRemote.h"
-#include "MemoryXz.h"
 
 namespace unwindstack {
 
-// Statistics (used only for optional debug log messages).
-static constexpr bool kLogMemoryXzUsage = false;
-std::atomic_size_t MemoryXz::total_used_ = 0;
-std::atomic_size_t MemoryXz::total_size_ = 0;
-std::atomic_size_t MemoryXz::total_open_ = 0;
-
 static size_t ProcessVmRead(pid_t pid, uint64_t remote_src, void* dst, size_t len) {
 
   // Split up the remote read across page boundaries.
@@ -380,7 +369,7 @@
   return memory_->Read(read_addr, dst, read_length);
 }
 
-void MemoryRanges::Insert(MemoryRange* memory) {
+bool MemoryRanges::Insert(MemoryRange* memory) {
   uint64_t last_addr;
   if (__builtin_add_overflow(memory->offset(), memory->length(), &last_addr)) {
     // This should never happen in the real world. However, it is possible
@@ -389,7 +378,12 @@
     // value.
     last_addr = UINT64_MAX;
   }
-  maps_.emplace(last_addr, memory);
+  auto entry = maps_.try_emplace(last_addr, memory);
+  if (entry.second) {
+    return true;
+  }
+  delete memory;
+  return false;
 }
 
 size_t MemoryRanges::Read(uint64_t addr, void* dst, size_t size) {
@@ -421,6 +415,16 @@
   return true;
 }
 
+bool MemoryOffline::Init(const std::string& file, uint64_t offset, uint64_t start, uint64_t size) {
+  auto memory_file = std::make_shared<MemoryFileAtOffset>();
+  if (!memory_file->Init(file, offset)) {
+    return false;
+  }
+
+  memory_ = std::make_unique<MemoryRange>(memory_file, 0, size, start);
+  return true;
+}
+
 size_t MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
   if (!memory_) {
     return 0;
@@ -531,7 +535,7 @@
         CacheDataType* cache = reinterpret_cast<CacheDataType*>(memory);
         delete cache;
       }) != 0) {
-    log_async_safe("Failed to create pthread key.");
+    Log::AsyncSafe("Failed to create pthread key.");
     thread_cache_.reset();
   }
 }
@@ -559,6 +563,10 @@
 }
 
 void MemoryThreadCache::Clear() {
+  if (!thread_cache_) {
+    return;
+  }
+
   CacheDataType* cache = reinterpret_cast<CacheDataType*>(pthread_getspecific(*thread_cache_));
   if (cache != nullptr) {
     delete cache;
@@ -566,229 +574,4 @@
   }
 }
 
-MemoryXz::MemoryXz(Memory* memory, uint64_t addr, uint64_t size, const std::string& name)
-    : compressed_memory_(memory), compressed_addr_(addr), compressed_size_(size), name_(name) {
-  total_open_ += 1;
-}
-
-bool MemoryXz::Init() {
-  static std::once_flag crc_initialized;
-  std::call_once(crc_initialized, []() {
-    CrcGenerateTable();
-    Crc64GenerateTable();
-  });
-  if (compressed_size_ >= kMaxCompressedSize) {
-    return false;
-  }
-  if (!ReadBlocks()) {
-    return false;
-  }
-
-  // All blocks (except the last one) must have the same power-of-2 size.
-  if (blocks_.size() > 1) {
-    size_t block_size_log2 = __builtin_ctz(blocks_.front().decompressed_size);
-    auto correct_size = [=](XzBlock& b) { return b.decompressed_size == (1 << block_size_log2); };
-    if (std::all_of(blocks_.begin(), std::prev(blocks_.end()), correct_size) &&
-        blocks_.back().decompressed_size <= (1 << block_size_log2)) {
-      block_size_log2_ = block_size_log2;
-    } else {
-      // Inconsistent block-sizes.  Decompress and merge everything now.
-      std::unique_ptr<uint8_t[]> data(new uint8_t[size_]);
-      size_t offset = 0;
-      for (XzBlock& block : blocks_) {
-        if (!Decompress(&block)) {
-          return false;
-        }
-        memcpy(data.get() + offset, block.decompressed_data.get(), block.decompressed_size);
-        offset += block.decompressed_size;
-      }
-      blocks_.clear();
-      blocks_.push_back(XzBlock{
-          .decompressed_data = std::move(data),
-          .decompressed_size = size_,
-      });
-      block_size_log2_ = 31;  // Because 32 bits is too big (shift right by 32 is not allowed).
-    }
-  }
-
-  return true;
-}
-
-MemoryXz::~MemoryXz() {
-  total_used_ -= used_;
-  total_size_ -= size_;
-  total_open_ -= 1;
-}
-
-size_t MemoryXz::Read(uint64_t addr, void* buffer, size_t size) {
-  if (addr >= size_) {
-    return 0;  // Read past the end.
-  }
-  uint8_t* dst = reinterpret_cast<uint8_t*>(buffer);  // Position in the output buffer.
-  for (size_t i = addr >> block_size_log2_; i < blocks_.size(); i++) {
-    XzBlock* block = &blocks_[i];
-    if (block->decompressed_data == nullptr) {
-      if (!Decompress(block)) {
-        break;
-      }
-    }
-    size_t offset = (addr - (i << block_size_log2_));  // Start inside the block.
-    size_t copy_bytes = std::min<size_t>(size, block->decompressed_size - offset);
-    memcpy(dst, block->decompressed_data.get() + offset, copy_bytes);
-    dst += copy_bytes;
-    addr += copy_bytes;
-    size -= copy_bytes;
-    if (size == 0) {
-      break;
-    }
-  }
-  return dst - reinterpret_cast<uint8_t*>(buffer);
-}
-
-bool MemoryXz::ReadBlocks() {
-  static ISzAlloc alloc;
-  alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
-  alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
-
-  // Read the compressed data, so we can quickly scan through the headers.
-  std::unique_ptr<uint8_t[]> compressed_data(new (std::nothrow) uint8_t[compressed_size_]);
-  if (compressed_data.get() == nullptr) {
-    return false;
-  }
-  if (!compressed_memory_->ReadFully(compressed_addr_, compressed_data.get(), compressed_size_)) {
-    return false;
-  }
-
-  // Implement the required interface for communication
-  // (written in C so we can not use virtual methods or member functions).
-  struct XzLookInStream : public ILookInStream, public ICompressProgress {
-    static SRes LookImpl(const ILookInStream* p, const void** buf, size_t* size) {
-      auto* ctx = reinterpret_cast<const XzLookInStream*>(p);
-      *buf = ctx->data + ctx->offset;
-      *size = std::min(*size, ctx->size - ctx->offset);
-      return SZ_OK;
-    }
-    static SRes SkipImpl(const ILookInStream* p, size_t len) {
-      auto* ctx = reinterpret_cast<XzLookInStream*>(const_cast<ILookInStream*>(p));
-      ctx->offset += len;
-      return SZ_OK;
-    }
-    static SRes ReadImpl(const ILookInStream* p, void* buf, size_t* size) {
-      auto* ctx = reinterpret_cast<const XzLookInStream*>(p);
-      *size = std::min(*size, ctx->size - ctx->offset);
-      memcpy(buf, ctx->data + ctx->offset, *size);
-      return SZ_OK;
-    }
-    static SRes SeekImpl(const ILookInStream* p, Int64* pos, ESzSeek origin) {
-      auto* ctx = reinterpret_cast<XzLookInStream*>(const_cast<ILookInStream*>(p));
-      switch (origin) {
-        case SZ_SEEK_SET:
-          ctx->offset = *pos;
-          break;
-        case SZ_SEEK_CUR:
-          ctx->offset += *pos;
-          break;
-        case SZ_SEEK_END:
-          ctx->offset = ctx->size + *pos;
-          break;
-      }
-      *pos = ctx->offset;
-      return SZ_OK;
-    }
-    static SRes ProgressImpl(const ICompressProgress*, UInt64, UInt64) { return SZ_OK; }
-    size_t offset;
-    uint8_t* data;
-    size_t size;
-  };
-  XzLookInStream callbacks;
-  callbacks.Look = &XzLookInStream::LookImpl;
-  callbacks.Skip = &XzLookInStream::SkipImpl;
-  callbacks.Read = &XzLookInStream::ReadImpl;
-  callbacks.Seek = &XzLookInStream::SeekImpl;
-  callbacks.Progress = &XzLookInStream::ProgressImpl;
-  callbacks.offset = 0;
-  callbacks.data = compressed_data.get();
-  callbacks.size = compressed_size_;
-
-  // Iterate over the internal XZ blocks without decompressing them.
-  CXzs xzs;
-  Xzs_Construct(&xzs);
-  Int64 end_offset = compressed_size_;
-  if (Xzs_ReadBackward(&xzs, &callbacks, &end_offset, &callbacks, &alloc) == SZ_OK) {
-    blocks_.reserve(Xzs_GetNumBlocks(&xzs));
-    size_t dst_offset = 0;
-    for (int s = xzs.num - 1; s >= 0; s--) {
-      const CXzStream& stream = xzs.streams[s];
-      size_t src_offset = stream.startOffset + XZ_STREAM_HEADER_SIZE;
-      for (size_t b = 0; b < stream.numBlocks; b++) {
-        const CXzBlockSizes& block = stream.blocks[b];
-        blocks_.push_back(XzBlock{
-            .decompressed_data = nullptr,  // Lazy allocation and decompression.
-            .decompressed_size = static_cast<uint32_t>(block.unpackSize),
-            .compressed_offset = static_cast<uint32_t>(src_offset),
-            .compressed_size = static_cast<uint32_t>((block.totalSize + 3) & ~3u),
-            .stream_flags = stream.flags,
-        });
-        dst_offset += blocks_.back().decompressed_size;
-        src_offset += blocks_.back().compressed_size;
-      }
-    }
-    size_ = dst_offset;
-    total_size_ += dst_offset;
-  }
-  Xzs_Free(&xzs, &alloc);
-  return !blocks_.empty();
-}
-
-bool MemoryXz::Decompress(XzBlock* block) {
-  static ISzAlloc alloc;
-  alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
-  alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
-
-  // Read the compressed data for this block.
-  std::unique_ptr<uint8_t[]> compressed_data(new (std::nothrow) uint8_t[block->compressed_size]);
-  if (compressed_data.get() == nullptr) {
-    return false;
-  }
-  if (!compressed_memory_->ReadFully(compressed_addr_ + block->compressed_offset,
-                                     compressed_data.get(), block->compressed_size)) {
-    return false;
-  }
-
-  // Allocate decompressed memory.
-  std::unique_ptr<uint8_t[]> decompressed_data(new uint8_t[block->decompressed_size]);
-  if (decompressed_data == nullptr) {
-    return false;
-  }
-
-  // Decompress.
-  CXzUnpacker state{};
-  XzUnpacker_Construct(&state, &alloc);
-  state.streamFlags = block->stream_flags;
-  XzUnpacker_PrepareToRandomBlockDecoding(&state);
-  size_t decompressed_size = block->decompressed_size;
-  size_t compressed_size = block->compressed_size;
-  ECoderStatus status;
-  XzUnpacker_SetOutBuf(&state, decompressed_data.get(), decompressed_size);
-  int return_val =
-      XzUnpacker_Code(&state, /*decompressed_data=*/nullptr, &decompressed_size,
-                      compressed_data.get(), &compressed_size, true, CODER_FINISH_END, &status);
-  XzUnpacker_Free(&state);
-  if (return_val != SZ_OK || status != CODER_STATUS_FINISHED_WITH_MARK) {
-    log(0, "Can not decompress \"%s\"", name_.c_str());
-    return false;
-  }
-
-  used_ += block->decompressed_size;
-  total_used_ += block->decompressed_size;
-  if (kLogMemoryXzUsage) {
-    log(0, "decompressed memory: %zi%% of %ziKB (%zi files), %i%% of %iKB (%s)",
-        100 * total_used_ / total_size_, total_size_ / 1024, total_open_.load(),
-        100 * used_ / size_, size_ / 1024, name_.c_str());
-  }
-
-  block->decompressed_data = std::move(decompressed_data);
-  return true;
-}
-
 }  // namespace unwindstack
diff --git a/libunwindstack/MemoryBuffer.h b/libunwindstack/MemoryBuffer.h
index 24609f4..a5b5743 100644
--- a/libunwindstack/MemoryBuffer.h
+++ b/libunwindstack/MemoryBuffer.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MEMORY_BUFFER_H
-#define _LIBUNWINDSTACK_MEMORY_BUFFER_H
+#pragma once
 
 #include <stdint.h>
 
@@ -56,5 +55,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MEMORY_BUFFER_H
diff --git a/libunwindstack/MemoryCache.h b/libunwindstack/MemoryCache.h
index 523a4a1..de5e9a0 100644
--- a/libunwindstack/MemoryCache.h
+++ b/libunwindstack/MemoryCache.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MEMORY_CACHE_H
-#define _LIBUNWINDSTACK_MEMORY_CACHE_H
+#pragma once
 
 #include <pthread.h>
 #include <stdint.h>
@@ -91,5 +90,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MEMORY_CACHE_H
diff --git a/libunwindstack/MemoryFileAtOffset.h b/libunwindstack/MemoryFileAtOffset.h
index 9949f26..90cf00b 100644
--- a/libunwindstack/MemoryFileAtOffset.h
+++ b/libunwindstack/MemoryFileAtOffset.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
-#define _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
+#pragma once
 
 #include <stdint.h>
 
@@ -45,5 +44,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
diff --git a/libunwindstack/MemoryLocal.h b/libunwindstack/MemoryLocal.h
index a5c7a48..f98c9cb 100644
--- a/libunwindstack/MemoryLocal.h
+++ b/libunwindstack/MemoryLocal.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MEMORY_LOCAL_H
-#define _LIBUNWINDSTACK_MEMORY_LOCAL_H
+#pragma once
 
 #include <stdint.h>
 
@@ -33,5 +32,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MEMORY_LOCAL_H
diff --git a/libunwindstack/MemoryMte.cpp b/libunwindstack/MemoryMte.cpp
index 679f413..3841744 100644
--- a/libunwindstack/MemoryMte.cpp
+++ b/libunwindstack/MemoryMte.cpp
@@ -17,8 +17,10 @@
 #include <sys/ptrace.h>
 #include <sys/uio.h>
 
-#ifdef __BIONIC__
+#if defined(__BIONIC__)
 #include <bionic/mte.h>
+#else
+#define mte_supported() false
 #endif
 
 #include "MemoryLocal.h"
@@ -27,7 +29,7 @@
 namespace unwindstack {
 
 long MemoryRemote::ReadTag(uint64_t addr) {
-#if defined(__aarch64__)
+#if defined(PTRACE_PEEKMTETAGS) || defined(PT_PEEKMTETAGS)
   char tag;
   iovec iov = {&tag, 1};
   if (ptrace(PTRACE_PEEKMTETAGS, pid_, reinterpret_cast<void*>(addr), &iov) != 0 ||
diff --git a/libunwindstack/MemoryOffline.h b/libunwindstack/MemoryOffline.h
index 789f1a2..024e111 100644
--- a/libunwindstack/MemoryOffline.h
+++ b/libunwindstack/MemoryOffline.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MEMORY_OFFLINE_H
-#define _LIBUNWINDSTACK_MEMORY_OFFLINE_H
+#pragma once
 
 #include <stdint.h>
 
@@ -36,6 +35,8 @@
 
   bool Init(const std::string& file, uint64_t offset);
 
+  bool Init(const std::string& file, uint64_t offset, uint64_t start, uint64_t size);
+
   size_t Read(uint64_t addr, void* dst, size_t size) override;
 
  private:
@@ -56,5 +57,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MEMORY_OFFLINE_H
diff --git a/libunwindstack/MemoryOfflineBuffer.h b/libunwindstack/MemoryOfflineBuffer.h
index 64c49a1..d77008e 100644
--- a/libunwindstack/MemoryOfflineBuffer.h
+++ b/libunwindstack/MemoryOfflineBuffer.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
-#define _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
+#pragma once
 
 #include <stdint.h>
 
@@ -39,5 +38,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
diff --git a/libunwindstack/MemoryRange.h b/libunwindstack/MemoryRange.h
index 3b4ab5c..b789ee3 100644
--- a/libunwindstack/MemoryRange.h
+++ b/libunwindstack/MemoryRange.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MEMORY_RANGE_H
-#define _LIBUNWINDSTACK_MEMORY_RANGE_H
+#pragma once
 
 #include <stdint.h>
 
@@ -53,7 +52,7 @@
   MemoryRanges() = default;
   virtual ~MemoryRanges() = default;
 
-  void Insert(MemoryRange* memory);
+  bool Insert(MemoryRange* memory);
 
   size_t Read(uint64_t addr, void* dst, size_t size) override;
 
@@ -62,5 +61,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MEMORY_RANGE_H
diff --git a/libunwindstack/MemoryRemote.h b/libunwindstack/MemoryRemote.h
index dd09c88..563e5b7 100644
--- a/libunwindstack/MemoryRemote.h
+++ b/libunwindstack/MemoryRemote.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MEMORY_REMOTE_H
-#define _LIBUNWINDSTACK_MEMORY_REMOTE_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -42,5 +41,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MEMORY_REMOTE_H
diff --git a/libunwindstack/MemoryXz.cpp b/libunwindstack/MemoryXz.cpp
new file mode 100644
index 0000000..9e91a07
--- /dev/null
+++ b/libunwindstack/MemoryXz.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2021 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 <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <utility>
+
+#include <7zCrc.h>
+#include <Xz.h>
+#include <XzCrc64.h>
+
+#include <unwindstack/Log.h>
+
+#include "MemoryXz.h"
+
+namespace unwindstack {
+
+// Statistics (used only for optional debug log messages).
+static constexpr bool kLogMemoryXzUsage = false;
+std::atomic_size_t MemoryXz::total_used_ = 0;
+std::atomic_size_t MemoryXz::total_size_ = 0;
+std::atomic_size_t MemoryXz::total_open_ = 0;
+
+MemoryXz::MemoryXz(Memory* memory, uint64_t addr, uint64_t size, const std::string& name)
+    : compressed_memory_(memory), compressed_addr_(addr), compressed_size_(size), name_(name) {
+  total_open_ += 1;
+}
+
+bool MemoryXz::Init() {
+  static std::once_flag crc_initialized;
+  std::call_once(crc_initialized, []() {
+    CrcGenerateTable();
+    Crc64GenerateTable();
+  });
+  if (compressed_size_ >= kMaxCompressedSize) {
+    return false;
+  }
+  if (!ReadBlocks()) {
+    return false;
+  }
+
+  // All blocks (except the last one) must have the same power-of-2 size.
+  if (blocks_.size() > 1) {
+    size_t block_size_log2 = __builtin_ctz(blocks_.front().decompressed_size);
+    auto correct_size = [=](XzBlock& b) { return b.decompressed_size == (1 << block_size_log2); };
+    if (std::all_of(blocks_.begin(), std::prev(blocks_.end()), correct_size) &&
+        blocks_.back().decompressed_size <= (1 << block_size_log2)) {
+      block_size_log2_ = block_size_log2;
+    } else {
+      // Inconsistent block-sizes.  Decompress and merge everything now.
+      std::unique_ptr<uint8_t[]> data(new uint8_t[size_]);
+      size_t offset = 0;
+      for (XzBlock& block : blocks_) {
+        if (!Decompress(&block)) {
+          return false;
+        }
+        memcpy(data.get() + offset, block.decompressed_data.get(), block.decompressed_size);
+        offset += block.decompressed_size;
+      }
+      blocks_.clear();
+      blocks_.push_back(XzBlock{
+          .decompressed_data = std::move(data),
+          .decompressed_size = size_,
+      });
+      block_size_log2_ = 31;  // Because 32 bits is too big (shift right by 32 is not allowed).
+    }
+  }
+
+  return true;
+}
+
+MemoryXz::~MemoryXz() {
+  total_used_ -= used_;
+  total_size_ -= size_;
+  total_open_ -= 1;
+}
+
+size_t MemoryXz::Read(uint64_t addr, void* buffer, size_t size) {
+  if (addr >= size_) {
+    return 0;  // Read past the end.
+  }
+  uint8_t* dst = reinterpret_cast<uint8_t*>(buffer);  // Position in the output buffer.
+  for (size_t i = addr >> block_size_log2_; i < blocks_.size(); i++) {
+    XzBlock* block = &blocks_[i];
+    if (block->decompressed_data == nullptr) {
+      if (!Decompress(block)) {
+        break;
+      }
+    }
+    size_t offset = (addr - (i << block_size_log2_));  // Start inside the block.
+    size_t copy_bytes = std::min<size_t>(size, block->decompressed_size - offset);
+    memcpy(dst, block->decompressed_data.get() + offset, copy_bytes);
+    dst += copy_bytes;
+    addr += copy_bytes;
+    size -= copy_bytes;
+    if (size == 0) {
+      break;
+    }
+  }
+  return dst - reinterpret_cast<uint8_t*>(buffer);
+}
+
+bool MemoryXz::ReadBlocks() {
+  static ISzAlloc alloc;
+  alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
+  alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
+
+  // Read the compressed data, so we can quickly scan through the headers.
+  std::unique_ptr<uint8_t[]> compressed_data(new (std::nothrow) uint8_t[compressed_size_]);
+  if (compressed_data.get() == nullptr) {
+    return false;
+  }
+  if (!compressed_memory_->ReadFully(compressed_addr_, compressed_data.get(), compressed_size_)) {
+    return false;
+  }
+
+  // Implement the required interface for communication
+  // (written in C so we can not use virtual methods or member functions).
+  struct XzLookInStream : public ILookInStream, public ICompressProgress {
+    static SRes LookImpl(const ILookInStream* p, const void** buf, size_t* size) {
+      auto* ctx = reinterpret_cast<const XzLookInStream*>(p);
+      *buf = ctx->data + ctx->offset;
+      *size = std::min(*size, ctx->size - ctx->offset);
+      return SZ_OK;
+    }
+    static SRes SkipImpl(const ILookInStream* p, size_t len) {
+      auto* ctx = reinterpret_cast<XzLookInStream*>(const_cast<ILookInStream*>(p));
+      ctx->offset += len;
+      return SZ_OK;
+    }
+    static SRes ReadImpl(const ILookInStream* p, void* buf, size_t* size) {
+      auto* ctx = reinterpret_cast<const XzLookInStream*>(p);
+      *size = std::min(*size, ctx->size - ctx->offset);
+      memcpy(buf, ctx->data + ctx->offset, *size);
+      return SZ_OK;
+    }
+    static SRes SeekImpl(const ILookInStream* p, Int64* pos, ESzSeek origin) {
+      auto* ctx = reinterpret_cast<XzLookInStream*>(const_cast<ILookInStream*>(p));
+      switch (origin) {
+        case SZ_SEEK_SET:
+          ctx->offset = *pos;
+          break;
+        case SZ_SEEK_CUR:
+          ctx->offset += *pos;
+          break;
+        case SZ_SEEK_END:
+          ctx->offset = ctx->size + *pos;
+          break;
+      }
+      *pos = ctx->offset;
+      return SZ_OK;
+    }
+    static SRes ProgressImpl(const ICompressProgress*, UInt64, UInt64) { return SZ_OK; }
+    size_t offset;
+    uint8_t* data;
+    size_t size;
+  };
+  XzLookInStream callbacks;
+  callbacks.Look = &XzLookInStream::LookImpl;
+  callbacks.Skip = &XzLookInStream::SkipImpl;
+  callbacks.Read = &XzLookInStream::ReadImpl;
+  callbacks.Seek = &XzLookInStream::SeekImpl;
+  callbacks.Progress = &XzLookInStream::ProgressImpl;
+  callbacks.offset = 0;
+  callbacks.data = compressed_data.get();
+  callbacks.size = compressed_size_;
+
+  // Iterate over the internal XZ blocks without decompressing them.
+  CXzs xzs;
+  Xzs_Construct(&xzs);
+  Int64 end_offset = compressed_size_;
+  if (Xzs_ReadBackward(&xzs, &callbacks, &end_offset, &callbacks, &alloc) == SZ_OK) {
+    blocks_.reserve(Xzs_GetNumBlocks(&xzs));
+    size_t dst_offset = 0;
+    for (int s = xzs.num - 1; s >= 0; s--) {
+      const CXzStream& stream = xzs.streams[s];
+      size_t src_offset = stream.startOffset + XZ_STREAM_HEADER_SIZE;
+      for (size_t b = 0; b < stream.numBlocks; b++) {
+        const CXzBlockSizes& block = stream.blocks[b];
+        blocks_.push_back(XzBlock{
+            .decompressed_data = nullptr,  // Lazy allocation and decompression.
+            .decompressed_size = static_cast<uint32_t>(block.unpackSize),
+            .compressed_offset = static_cast<uint32_t>(src_offset),
+            .compressed_size = static_cast<uint32_t>((block.totalSize + 3) & ~3u),
+            .stream_flags = stream.flags,
+        });
+        dst_offset += blocks_.back().decompressed_size;
+        src_offset += blocks_.back().compressed_size;
+      }
+    }
+    size_ = dst_offset;
+    total_size_ += dst_offset;
+  }
+  Xzs_Free(&xzs, &alloc);
+  return !blocks_.empty();
+}
+
+bool MemoryXz::Decompress(XzBlock* block) {
+  static ISzAlloc alloc;
+  alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
+  alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
+
+  // Read the compressed data for this block.
+  std::unique_ptr<uint8_t[]> compressed_data(new (std::nothrow) uint8_t[block->compressed_size]);
+  if (compressed_data.get() == nullptr) {
+    return false;
+  }
+  if (!compressed_memory_->ReadFully(compressed_addr_ + block->compressed_offset,
+                                     compressed_data.get(), block->compressed_size)) {
+    return false;
+  }
+
+  // Allocate decompressed memory.
+  std::unique_ptr<uint8_t[]> decompressed_data(new uint8_t[block->decompressed_size]);
+  if (decompressed_data == nullptr) {
+    return false;
+  }
+
+  // Decompress.
+  CXzUnpacker state{};
+  XzUnpacker_Construct(&state, &alloc);
+  state.streamFlags = block->stream_flags;
+  XzUnpacker_PrepareToRandomBlockDecoding(&state);
+  size_t decompressed_size = block->decompressed_size;
+  size_t compressed_size = block->compressed_size;
+  ECoderStatus status;
+  XzUnpacker_SetOutBuf(&state, decompressed_data.get(), decompressed_size);
+  int return_val =
+      XzUnpacker_Code(&state, /*decompressed_data=*/nullptr, &decompressed_size,
+                      compressed_data.get(), &compressed_size, true, CODER_FINISH_END, &status);
+  XzUnpacker_Free(&state);
+  if (return_val != SZ_OK || status != CODER_STATUS_FINISHED_WITH_MARK) {
+    Log::Error("Cannot decompress \"%s\"", name_.c_str());
+    return false;
+  }
+
+  used_ += block->decompressed_size;
+  total_used_ += block->decompressed_size;
+  if (kLogMemoryXzUsage) {
+    Log::Info("decompressed memory: %zi%% of %ziKB (%zi files), %i%% of %iKB (%s)",
+              100 * total_used_ / total_size_, total_size_ / 1024, total_open_.load(),
+              100 * used_ / size_, size_ / 1024, name_.c_str());
+  }
+
+  block->decompressed_data = std::move(decompressed_data);
+  return true;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/MemoryXz.h b/libunwindstack/MemoryXz.h
index c72ba88..57bf6c5 100644
--- a/libunwindstack/MemoryXz.h
+++ b/libunwindstack/MemoryXz.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MEMORY_XZ_H
-#define _LIBUNWINDSTACK_MEMORY_XZ_H
+#pragma once
 
 #include <atomic>
 #include <memory>
@@ -71,5 +70,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MEMORY_XZ_H
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index 03aa6c2..08a35ea 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -18,6 +18,7 @@
 #include <sys/ptrace.h>
 #include <sys/uio.h>
 
+#include <algorithm>
 #include <vector>
 
 #include <unwindstack/Elf.h>
@@ -25,27 +26,26 @@
 #include <unwindstack/Regs.h>
 #include <unwindstack/RegsArm.h>
 #include <unwindstack/RegsArm64.h>
-#include <unwindstack/RegsMips.h>
-#include <unwindstack/RegsMips64.h>
 #include <unwindstack/RegsX86.h>
 #include <unwindstack/RegsX86_64.h>
 #include <unwindstack/UserArm.h>
 #include <unwindstack/UserArm64.h>
-#include <unwindstack/UserMips.h>
-#include <unwindstack/UserMips64.h>
 #include <unwindstack/UserX86.h>
 #include <unwindstack/UserX86_64.h>
 
 namespace unwindstack {
 
 // The largest user structure.
-constexpr size_t MAX_USER_REGS_SIZE = sizeof(mips64_user_regs) + 10;
+// constexpr size_t MAX_USER_REGS_SIZE = sizeof(mips64_user_regs) + 10;
+static constexpr size_t kMaxUserRegsSize = std::max(
+    sizeof(arm_user_regs),
+    std::max(sizeof(arm64_user_regs), std::max(sizeof(x86_user_regs), sizeof(x86_64_user_regs))));
 
 // This function assumes that reg_data is already aligned to a 64 bit value.
 // If not this could crash with an unaligned access.
 Regs* Regs::RemoteGet(pid_t pid) {
   // Make the buffer large enough to contain the largest registers type.
-  std::vector<uint64_t> buffer(MAX_USER_REGS_SIZE / sizeof(uint64_t));
+  std::vector<uint64_t> buffer(kMaxUserRegsSize / sizeof(uint64_t));
   struct iovec io;
   io.iov_base = buffer.data();
   io.iov_len = buffer.size() * sizeof(uint64_t);
@@ -54,6 +54,7 @@
     return nullptr;
   }
 
+  // Infer the process architecture from the size of its register structure.
   switch (io.iov_len) {
   case sizeof(x86_user_regs):
     return RegsX86::Read(buffer.data());
@@ -63,14 +64,35 @@
     return RegsArm::Read(buffer.data());
   case sizeof(arm64_user_regs):
     return RegsArm64::Read(buffer.data());
-  case sizeof(mips_user_regs):
-    return RegsMips::Read(buffer.data());
-  case sizeof(mips64_user_regs):
-    return RegsMips64::Read(buffer.data());
   }
   return nullptr;
 }
 
+ArchEnum Regs::RemoteGetArch(pid_t pid) {
+  // Make the buffer large enough to contain the largest registers type.
+  std::vector<uint64_t> buffer(kMaxUserRegsSize / sizeof(uint64_t));
+  struct iovec io;
+  io.iov_base = buffer.data();
+  io.iov_len = buffer.size() * sizeof(uint64_t);
+
+  if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, reinterpret_cast<void*>(&io)) == -1) {
+    return ARCH_UNKNOWN;
+  }
+
+  // Infer the process architecture from the size of its register structure.
+  switch (io.iov_len) {
+    case sizeof(x86_user_regs):
+      return ARCH_X86;
+    case sizeof(x86_64_user_regs):
+      return ARCH_X86_64;
+    case sizeof(arm_user_regs):
+      return ARCH_ARM;
+    case sizeof(arm64_user_regs):
+      return ARCH_ARM64;
+  }
+  return ARCH_UNKNOWN;
+}
+
 Regs* Regs::CreateFromUcontext(ArchEnum arch, void* ucontext) {
   switch (arch) {
     case ARCH_X86:
@@ -81,11 +103,6 @@
       return RegsArm::CreateFromUcontext(ucontext);
     case ARCH_ARM64:
       return RegsArm64::CreateFromUcontext(ucontext);
-    case ARCH_MIPS:
-      return RegsMips::CreateFromUcontext(ucontext);
-    case ARCH_MIPS64:
-      return RegsMips64::CreateFromUcontext(ucontext);
-    case ARCH_UNKNOWN:
     default:
       return nullptr;
   }
diff --git a/libunwindstack/RegsInfo.h b/libunwindstack/RegsInfo.h
index e445a91..28b297e 100644
--- a/libunwindstack/RegsInfo.h
+++ b/libunwindstack/RegsInfo.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_REGS_INFO_H
-#define _LIBUNWINDSTACK_REGS_INFO_H
+#pragma once
 
 #include <stdint.h>
 
@@ -64,5 +63,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_REGS_INFO_H
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index c1967b0..0b46751 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_SYMBOLS_H
-#define _LIBUNWINDSTACK_SYMBOLS_H
+#pragma once
 
 #include <stdint.h>
 
@@ -75,5 +74,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_SYMBOLS_H
diff --git a/libunwindstack/TEST_MAPPING b/libunwindstack/TEST_MAPPING
index 0c73ff9..2ebaeac 100644
--- a/libunwindstack/TEST_MAPPING
+++ b/libunwindstack/TEST_MAPPING
@@ -12,5 +12,11 @@
     {
       "name": "CtsPerfettoTestCases"
     }
+  ],
+
+  "hwasan-postsubmit": [
+    {
+      "name": "libunwindstack_unit_test"
+    }
   ]
 }
diff --git a/libunwindstack/ThreadEntry.cpp b/libunwindstack/ThreadEntry.cpp
index 86912a2..db34df0 100644
--- a/libunwindstack/ThreadEntry.cpp
+++ b/libunwindstack/ThreadEntry.cpp
@@ -75,12 +75,12 @@
 }
 
 bool ThreadEntry::Wait(WaitType type) {
-  static const std::chrono::duration wait_time(std::chrono::seconds(5));
+  static const std::chrono::duration wait_time(std::chrono::seconds(10));
   std::unique_lock<std::mutex> lock(wait_mutex_);
   if (wait_cond_.wait_for(lock, wait_time, [this, type] { return wait_value_ == type; })) {
     return true;
   } else {
-    log_async_safe("pthread_cond_timedwait for value %d failed", type);
+    Log::AsyncSafe("pthread_cond_timedwait for value %d failed", type);
     return false;
   }
 }
diff --git a/libunwindstack/ThreadEntry.h b/libunwindstack/ThreadEntry.h
index 1caba83..2f8e88d 100644
--- a/libunwindstack/ThreadEntry.h
+++ b/libunwindstack/ThreadEntry.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_THREAD_ENTRY_H
-#define _LIBUNWINDSTACK_THREAD_ENTRY_H
+#pragma once
 
 #include <pthread.h>
 #include <sys/types.h>
@@ -74,5 +73,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_THREAD_ENTRY_H
diff --git a/libunwindstack/ThreadUnwinder.cpp b/libunwindstack/ThreadUnwinder.cpp
index a04a997..7b45261 100644
--- a/libunwindstack/ThreadUnwinder.cpp
+++ b/libunwindstack/ThreadUnwinder.cpp
@@ -39,7 +39,7 @@
 static void SignalLogOnly(int, siginfo_t*, void*) {
   android::base::ErrnoRestorer restore;
 
-  log_async_safe("pid %d, tid %d: Received a spurious thread signal\n", getpid(),
+  Log::AsyncSafe("pid %d, tid %d: Received a spurious thread signal\n", getpid(),
                  static_cast<int>(android::base::GetThreadId()));
 }
 
@@ -65,13 +65,17 @@
     entry->Wake();
   } else {
     // At this point, it is possible that entry has been freed, so just exit.
-    log_async_safe("Timed out waiting for unwind thread to indicate it completed.");
+    Log::AsyncSafe("Timed out waiting for unwind thread to indicate it completed.");
   }
 }
 
 ThreadUnwinder::ThreadUnwinder(size_t max_frames, Maps* maps)
     : UnwinderFromPid(max_frames, getpid(), Regs::CurrentArch(), maps) {}
 
+ThreadUnwinder::ThreadUnwinder(size_t max_frames, Maps* maps,
+                               std::shared_ptr<Memory>& process_memory)
+    : UnwinderFromPid(max_frames, getpid(), Regs::CurrentArch(), maps, process_memory) {}
+
 ThreadUnwinder::ThreadUnwinder(size_t max_frames, const ThreadUnwinder* unwinder)
     : UnwinderFromPid(max_frames, getpid(), Regs::CurrentArch()) {
   process_memory_ = unwinder->process_memory_;
@@ -92,7 +96,7 @@
   struct sigaction old_action = {};
   sigemptyset(&new_action.sa_mask);
   if (sigaction(signal, &new_action, &old_action) != 0) {
-    log_async_safe("sigaction failed: %s", strerror(errno));
+    Log::AsyncSafe("sigaction failed: %s", strerror(errno));
     ThreadEntry::Remove(entry);
     last_error_.code = ERROR_SYSTEM_CALL;
     return nullptr;
@@ -137,7 +141,7 @@
     last_error_.code = ERROR_THREAD_DOES_NOT_EXIST;
   } else {
     last_error_.code = ERROR_THREAD_TIMEOUT;
-    log_async_safe("Timed out waiting for signal handler to get ucontext data.");
+    Log::AsyncSafe("Timed out waiting for signal handler to get ucontext data.");
   }
 
   ThreadEntry::Remove(entry);
@@ -145,11 +149,11 @@
   return nullptr;
 }
 
-void ThreadUnwinder::UnwindWithSignal(int signal, pid_t tid,
+void ThreadUnwinder::UnwindWithSignal(int signal, pid_t tid, std::unique_ptr<Regs>* initial_regs,
                                       const std::vector<std::string>* initial_map_names_to_skip,
                                       const std::vector<std::string>* map_suffixes_to_ignore) {
   ClearErrors();
-  if (tid == pid_) {
+  if (tid == static_cast<pid_t>(android::base::GetThreadId())) {
     last_error_.code = ERROR_UNSUPPORTED;
     return;
   }
@@ -164,6 +168,9 @@
   }
 
   std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentArch(), entry->GetUcontext()));
+  if (initial_regs != nullptr) {
+    initial_regs->reset(regs->Clone());
+  }
   SetRegs(regs.get());
   UnwinderFromPid::Unwind(initial_map_names_to_skip, map_suffixes_to_ignore);
 
@@ -173,7 +180,7 @@
   // Wait for the thread to indicate it is done with the ThreadEntry.
   if (!entry->Wait(WAIT_FOR_THREAD_TO_RESTART)) {
     // Send a warning, but do not mark as a failure to unwind.
-    log_async_safe("Timed out waiting for signal handler to indicate it finished.");
+    Log::AsyncSafe("Timed out waiting for signal handler to indicate it finished.");
   }
 
   ThreadEntry::Remove(entry);
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index bd34465..facff86 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -24,9 +24,10 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <memory>
 
+#include <android-base/file.h>
 #include <android-base/stringprintf.h>
-#include <android-base/strings.h>
 
 #include <unwindstack/DexFiles.h>
 #include <unwindstack/Elf.h>
@@ -60,21 +61,12 @@
   frame->pc = dex_pc;
   frame->sp = regs_->sp();
 
-  MapInfo* info = maps_->Find(dex_pc);
-  if (info != nullptr) {
-    frame->map_start = info->start();
-    frame->map_end = info->end();
-    // Since this is a dex file frame, the elf_start_offset is not set
-    // by any of the normal code paths. Use the offset of the map since
-    // that matches the actual offset.
-    frame->map_elf_start_offset = info->offset();
-    frame->map_exact_offset = info->offset();
-    frame->map_load_bias = info->load_bias();
-    frame->map_flags = info->flags();
-    if (resolve_names_) {
-      frame->map_name = info->name();
-    }
-    frame->rel_pc = dex_pc - info->start();
+  frame->map_info = maps_->Find(dex_pc);
+  if (frame->map_info != nullptr) {
+    frame->rel_pc = dex_pc - frame->map_info->start();
+    // Initialize the load bias for this map so subsequent calls
+    // to GetLoadBias() will always return data.
+    frame->map_info->set_load_bias(0);
   } else {
     frame->rel_pc = dex_pc;
     warnings_ |= WARNING_DEX_PC_NOT_IN_MAP;
@@ -94,7 +86,7 @@
 #endif
 }
 
-FrameData* Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc,
+FrameData* Unwinder::FillInFrame(std::shared_ptr<MapInfo>& map_info, Elf* /*elf*/, uint64_t rel_pc,
                                  uint64_t pc_adjustment) {
   size_t frame_num = frames_.size();
   frames_.resize(frame_num + 1);
@@ -109,25 +101,8 @@
     return nullptr;
   }
 
-  if (resolve_names_) {
-    frame->map_name = map_info->name();
-    if (embedded_soname_ && map_info->elf_start_offset() != 0 && !frame->map_name.empty()) {
-      std::string soname = elf->GetSoname();
-      if (!soname.empty()) {
-        std::string map_with_soname;
-        map_with_soname += frame->map_name;
-        map_with_soname += '!';
-        map_with_soname += soname;
-        frame->map_name = SharedString(std::move(map_with_soname));
-      }
-    }
-  }
-  frame->map_elf_start_offset = map_info->elf_start_offset();
-  frame->map_exact_offset = map_info->offset();
-  frame->map_start = map_info->start();
-  frame->map_end = map_info->end();
-  frame->map_flags = map_info->flags();
-  frame->map_load_bias = elf->GetLoadBias();
+  frame->map_info = map_info;
+
   return frame;
 }
 
@@ -151,7 +126,6 @@
   ClearErrors();
 
   frames_.clear();
-  elf_from_memory_not_file_ = false;
 
   // Clear any cached data from previous unwinds.
   process_memory_->Clear();
@@ -166,30 +140,32 @@
     uint64_t cur_pc = regs_->pc();
     uint64_t cur_sp = regs_->sp();
 
-    MapInfo* map_info = maps_->Find(regs_->pc());
+    std::shared_ptr<MapInfo> map_info = maps_->Find(regs_->pc());
     uint64_t pc_adjustment = 0;
     uint64_t step_pc;
     uint64_t rel_pc;
     Elf* elf;
+    bool ignore_frame = false;
     if (map_info == nullptr) {
       step_pc = regs_->pc();
       rel_pc = step_pc;
-      last_error_.code = ERROR_INVALID_MAP;
+      // If we get invalid map via return_address_attempt, don't hide error for the previous frame.
+      if (!return_address_attempt || last_error_.code == ERROR_NONE) {
+        last_error_.code = ERROR_INVALID_MAP;
+        last_error_.address = step_pc;
+      }
       elf = nullptr;
     } else {
-      if (ShouldStop(map_suffixes_to_ignore, map_info->name())) {
+      ignore_frame =
+          initial_map_names_to_skip != nullptr &&
+          std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
+                    android::base::Basename(map_info->name())) != initial_map_names_to_skip->end();
+      if (!ignore_frame && ShouldStop(map_suffixes_to_ignore, map_info->name())) {
         break;
       }
       elf = map_info->GetElf(process_memory_, arch_);
-      // If this elf is memory backed, and there is a valid file, then set
-      // an indicator that we couldn't open the file.
-      const std::string& map_name = map_info->name();
-      if (!elf_from_memory_not_file_ && map_info->memory_backed_elf() && !map_name.empty() &&
-          map_name[0] != '[' && !android::base::StartsWith(map_name, "/memfd:")) {
-        elf_from_memory_not_file_ = true;
-      }
       step_pc = regs_->pc();
-      rel_pc = elf->GetRelPc(step_pc, map_info);
+      rel_pc = elf->GetRelPc(step_pc, map_info.get());
       // Everyone except elf data in gdb jit debug maps uses the relative pc.
       if (!(map_info->flags() & MAPS_FLAGS_JIT_SYMFILE_MAP)) {
         step_pc = rel_pc;
@@ -215,9 +191,7 @@
     }
 
     FrameData* frame = nullptr;
-    if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
-        std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
-                  basename(map_info->name().c_str())) == initial_map_names_to_skip->end()) {
+    if (!ignore_frame) {
       if (regs_->dex_pc() != 0) {
         // Add a frame to represent the dex file.
         FillInDexFrame();
@@ -248,7 +222,7 @@
         // some of the speculative frames.
         in_device_map = true;
       } else {
-        MapInfo* sp_info = maps_->Find(regs_->sp());
+        auto sp_info = maps_->Find(regs_->sp());
         if (sp_info != nullptr && sp_info->flags() & MAPS_FLAGS_DEVICE_MAP) {
           // Do not stop here, fall through in case we are
           // in the speculative unwind path and need to remove
@@ -325,25 +299,30 @@
 }
 
 std::string Unwinder::FormatFrame(const FrameData& frame) const {
+  return FormatFrame(arch_, frame, display_build_id_);
+}
+
+std::string Unwinder::FormatFrame(ArchEnum arch, const FrameData& frame, bool display_build_id) {
   std::string data;
-  if (ArchIs32Bit(arch_)) {
+  if (ArchIs32Bit(arch)) {
     data += android::base::StringPrintf("  #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
   } else {
     data += android::base::StringPrintf("  #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
   }
 
-  if (frame.map_start == frame.map_end) {
+  auto map_info = frame.map_info;
+  if (map_info == nullptr) {
     // No valid map associated with this frame.
     data += "  <unknown>";
-  } else if (!frame.map_name.empty()) {
+  } else if (!map_info->name().empty()) {
     data += "  ";
-    data += frame.map_name;
+    data += map_info->GetFullName();
   } else {
-    data += android::base::StringPrintf("  <anonymous:%" PRIx64 ">", frame.map_start);
+    data += android::base::StringPrintf("  <anonymous:%" PRIx64 ">", map_info->start());
   }
 
-  if (frame.map_elf_start_offset != 0) {
-    data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_elf_start_offset);
+  if (map_info != nullptr && map_info->elf_start_offset() != 0) {
+    data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", map_info->elf_start_offset());
   }
 
   if (!frame.function_name.empty()) {
@@ -362,8 +341,7 @@
     data += ')';
   }
 
-  MapInfo* map_info = maps_->Find(frame.map_start);
-  if (map_info != nullptr && display_build_id_) {
+  if (map_info != nullptr && display_build_id) {
     std::string build_id = map_info->GetPrintableBuildID();
     if (!build_id.empty()) {
       data += " (BuildId: " + build_id + ')';
@@ -376,7 +354,7 @@
   if (frame_num >= frames_.size()) {
     return "";
   }
-  return FormatFrame(frames_[frame_num]);
+  return FormatFrame(arch_, frames_[frame_num], display_build_id_);
 }
 
 void Unwinder::SetJitDebug(JitDebug* jit_debug) {
@@ -446,7 +424,7 @@
                                          bool resolve_names) {
   FrameData frame;
 
-  MapInfo* map_info = maps->Find(pc);
+  std::shared_ptr<MapInfo> map_info = maps->Find(pc);
   if (map_info == nullptr || arch == ARCH_UNKNOWN) {
     frame.pc = pc;
     frame.rel_pc = pc;
@@ -455,7 +433,7 @@
 
   Elf* elf = map_info->GetElf(process_memory, arch);
 
-  uint64_t relative_pc = elf->GetRelPc(pc, map_info);
+  uint64_t relative_pc = elf->GetRelPc(pc, map_info.get());
 
   uint64_t pc_adjustment = GetPcAdjustment(relative_pc, elf, arch);
   relative_pc -= pc_adjustment;
@@ -475,13 +453,7 @@
   // Copy all the things we need into the frame for symbolization.
   frame.rel_pc = relative_pc;
   frame.pc = pc - pc_adjustment;
-  frame.map_name = map_info->name();
-  frame.map_elf_start_offset = map_info->elf_start_offset();
-  frame.map_exact_offset = map_info->offset();
-  frame.map_start = map_info->start();
-  frame.map_end = map_info->end();
-  frame.map_flags = map_info->flags();
-  frame.map_load_bias = elf->GetLoadBias();
+  frame.map_info = map_info;
 
   if (!resolve_names ||
       !elf->GetFunctionName(debug_pc, &frame.function_name, &frame.function_offset)) {
diff --git a/libunwindstack/benchmarks/ElfBenchmark.cpp b/libunwindstack/benchmarks/ElfBenchmark.cpp
index 3e1e21f..7bfd9db 100644
--- a/libunwindstack/benchmarks/ElfBenchmark.cpp
+++ b/libunwindstack/benchmarks/ElfBenchmark.cpp
@@ -47,7 +47,7 @@
 
     unwindstack::Elf elf(file_memory.release());
     if (!elf.Init() || !elf.valid()) {
-      errx(1, "Internal Error: Cannot open elf.");
+      errx(1, "Internal Error: Cannot open elf: %s", elf_file.c_str());
     }
 
     state.PauseTiming();
@@ -73,10 +73,15 @@
 }
 BENCHMARK(BM_elf_create);
 
-void BM_elf_create_compressed(benchmark::State& state) {
-  BenchmarkElfCreate(state, GetCompressedElfFile());
+void BM_elf_create_large_compressed(benchmark::State& state) {
+  BenchmarkElfCreate(state, GetLargeCompressedFrameElfFile());
 }
-BENCHMARK(BM_elf_create_compressed);
+BENCHMARK(BM_elf_create_large_compressed);
+
+void BM_elf_create_large_eh_frame(benchmark::State& state) {
+  BenchmarkElfCreate(state, GetLargeEhFrameElfFile());
+}
+BENCHMARK(BM_elf_create_large_eh_frame);
 
 static void InitializeBuildId(benchmark::State& state, unwindstack::Maps& maps,
                               unwindstack::MapInfo** build_id_map_info) {
diff --git a/libunwindstack/benchmarks/EvalBenchmark.cpp b/libunwindstack/benchmarks/EvalBenchmark.cpp
new file mode 100644
index 0000000..9b3d1d8
--- /dev/null
+++ b/libunwindstack/benchmarks/EvalBenchmark.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2021 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 <cstdint>
+#include <ios>
+#include <sstream>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfSection.h>
+
+#include "Utils.h"
+#include "utils/DwarfSectionImplFake.h"
+#include "utils/MemoryFake.h"
+#include "utils/RegsFake.h"
+
+namespace unwindstack {
+namespace {
+
+// This collection of benchmarks exercises the DwarfSectionImpl::Eval function with a set of
+// artificial unwind data. The number of registers and register evaluation method are varied
+// for each individual benchmark.
+
+constexpr int kReturnAddressReg = 5;
+
+template <typename AddresssType>
+class EvalBenchmark : public benchmark::Fixture {
+ public:
+  EvalBenchmark() {
+    memory_.Clear();
+    section_ = std::make_unique<DwarfSectionImplFake<AddresssType>>(&memory_);
+  }
+
+  void TearDown(benchmark::State& state) override { mem_tracker_.SetBenchmarkCounters(state); }
+
+  // Benchmarks DwarfSectionImpl::Eval given the DwarfLocation object, loc_regs, initialized in each
+  // individual benchmark macro/function.
+  //
+  // This method initializes the fake register object and the DwarfCie object the same regardless
+  // of the benchmark. So the initialization of loc_regs is carefully crafted in each benchmark
+  // macro so that the evaluated PC and SP match the expected values after each call to Eval in the
+  // benchmarking loop.
+  //
+  // In addition to the Eval call, register value assertion is included in the benchmarking loop
+  // to ensure that we always capture the actual register evaluation
+  // (DwarfSectionImpl::EvalRegister). For example, if Eval is modified to lazily evaluate register
+  // values, we will still capture the register evaluation for the PC and SP (common case) in the
+  // register value assertion.
+  void RunBenchmark(benchmark::State& state, DwarfLocations& loc_regs) {
+    DwarfCie cie{.return_address_register = kReturnAddressReg};
+    bool finished;
+    RegsImplFake<AddresssType> regs(64);
+    regs.set_pc(0x1000);
+    regs.set_sp(0x3500);
+    regs[0] = 0x10000000;
+    mem_tracker_.StartTrackingAllocations();
+    for (auto _ : state) {
+      std::stringstream err_stream;
+      if (!section_->Eval(&cie, &memory_, loc_regs, &regs, &finished)) {
+        err_stream << "Eval() failed at address " << section_->LastErrorAddress();
+        state.SkipWithError(err_stream.str().c_str());
+        return;
+      }
+      if (finished || regs.pc() != 0x60000000U || regs.sp() != 0x10000000U) {
+        err_stream
+            << "Eval() finished successfully but registers were not evaluated correctly."
+            << "\nExpected: finished == false, regs.pc() == 0x60000000, regs.sp() == 0x10000000."
+            << "\nActual: finished == " << std::boolalpha << finished << std::hex
+            << ", regs.pc() == 0x" << regs.pc() << ", regs.sp() == 0x" << regs.sp();
+        state.SkipWithError(err_stream.str().c_str());
+        return;
+      }
+    }
+    mem_tracker_.StopTrackingAllocations();
+  }
+
+ protected:
+  MemoryFake memory_;
+  std::unique_ptr<DwarfSectionImplFake<AddresssType>> section_;
+  MemoryTracker mem_tracker_;
+};
+
+// Benchmarks exercising Eval with the DWARF_LOCATION_REGISTER evaluation method.
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_register_few_regs, uint64_t)(benchmark::State& state) {
+  DwarfLocations loc_regs;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+  loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0x50000000}};
+  RunBenchmark(state, loc_regs);
+}
+
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_register_many_regs, uint64_t)(benchmark::State& state) {
+  DwarfLocations loc_regs;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+  for (uint64_t i = 0; i < 64; i++) {
+    loc_regs[i] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, i * 0x10000000}};
+  }
+  RunBenchmark(state, loc_regs);
+}
+
+// Benchmarks exercising Eval with the DWARF_LOCATION_VAL_OFFSET evaluation method.
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_offset_few_regs, uint64_t)
+(benchmark::State& state) {
+  DwarfLocations loc_regs;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+  loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0x50000000, 0}};
+  RunBenchmark(state, loc_regs);
+}
+
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_offset_many_regs, uint64_t)
+(benchmark::State& state) {
+  DwarfLocations loc_regs;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+  for (uint64_t i = 0; i < 64; i++) {
+    loc_regs[i] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {i * 0x10000000, 0}};
+  }
+  RunBenchmark(state, loc_regs);
+}
+
+// Benchmarks exercising Eval with the DWARF_LOCATION_OFFSET evaluation method.
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_offset_few_regs, uint64_t)
+(benchmark::State& state) {
+  memory_.SetData64(0x20000000, 0x60000000);
+  DwarfLocations loc_regs;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+  loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x10000000, 0}};
+  RunBenchmark(state, loc_regs);
+}
+
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_offset_many_regs, uint64_t)
+(benchmark::State& state) {
+  memory_.SetData64(0x20000000, 0x60000000);
+  memory_.SetData64(0x30000000, 0x10000000);
+  DwarfLocations loc_regs;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+  for (uint64_t i = 1; i < 64; i++) {
+    loc_regs[i] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x10000000, 0}};
+  }
+  // Read from different place in memory for reg 0 so reg 0 maintains value of 0x10000000
+  // across multiple calls to Eval.
+  loc_regs[0] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x20000000, 0}};
+  RunBenchmark(state, loc_regs);
+}
+
+// Benchmarks exercising Eval with the DWARF_LOCATION_EXPRESSION evaluation method.
+// The dwarf op-code used for the expression benchmarks are OP_const4u (see DwarfOp::Eval).
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_expression_few_regs, uint64_t)
+(benchmark::State& state) {
+  memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+  uint64_t pc_value = 0x60000000;
+  memory_.SetMemory(0x80000000, &pc_value, sizeof(pc_value));
+  DwarfLocations loc_regs;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+  loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
+  RunBenchmark(state, loc_regs);
+}
+
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_expression_many_regs, uint64_t)
+(benchmark::State& state) {
+  memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+  uint64_t pc_value = 0x60000000;
+  memory_.SetMemory(0x80000000, &pc_value, sizeof(pc_value));
+
+  memory_.SetMemory(0x6000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x90});
+  uint64_t sp_value = 0x10000000;
+  memory_.SetMemory(0x90000000, &sp_value, sizeof(sp_value));
+
+  DwarfLocations loc_regs;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+  for (uint64_t i = 1; i < 64; i++) {
+    loc_regs[i] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
+  }
+  // Read from different place in memory for reg 0 so reg 0 maintains value of 0x10000000
+  // across multiple calls to Eval.
+  loc_regs[0] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x6004}};
+  RunBenchmark(state, loc_regs);
+}
+
+// Benchmarks exercising Eval with the DWARF_LOCATION_VAL_EXPRESSION evaluation method.
+// The dwarf op-code used for the value expression benchmarks are OP_const4u (see DwarfOp::Eval).
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_expression_few_regs, uint64_t)
+(benchmark::State& state) {
+  memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x60});
+  DwarfLocations loc_regs;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+  loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
+  RunBenchmark(state, loc_regs);
+}
+
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_expression_many_regs, uint64_t)
+(benchmark::State& state) {
+  memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x60});
+  memory_.SetMemory(0x6000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x10});
+  DwarfLocations loc_regs;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+  for (uint64_t i = 1; i < 64; i++) {
+    loc_regs[i] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
+  }
+  // Read from different place in memory for reg 0 so reg 0 maintains value of 0x10000000
+  // across multiple calls to Eval.
+  loc_regs[0] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x6004}};
+  RunBenchmark(state, loc_regs);
+}
+
+}  // namespace
+}  // namespace unwindstack
diff --git a/libunwindstack/benchmarks/MemoryLocalUnsafe.h b/libunwindstack/benchmarks/MemoryLocalUnsafe.h
new file mode 100644
index 0000000..8edf473
--- /dev/null
+++ b/libunwindstack/benchmarks/MemoryLocalUnsafe.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <cstring>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// MemoryLocalUnsafe is a prototype class to compare the performance of MemoryLocal::Read
+// to an "unsafe" read that assumes the local memory address provided is valid (i.e. memory is
+// not corrupted and address range lies within the stack).
+class MemoryLocalUnsafe : public Memory {
+ public:
+  MemoryLocalUnsafe() = default;
+  virtual ~MemoryLocalUnsafe() = default;
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override {
+    memcpy(dst, reinterpret_cast<void*>(addr), size);
+    return size;
+  }
+};
+
+}  // namespace unwindstack
diff --git a/libunwindstack/benchmarks/OfflineUnwindBenchmarks.cpp b/libunwindstack/benchmarks/OfflineUnwindBenchmarks.cpp
new file mode 100644
index 0000000..bcb0bec
--- /dev/null
+++ b/libunwindstack/benchmarks/OfflineUnwindBenchmarks.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2021 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 <cstddef>
+#include <cstdint>
+#include <filesystem>
+#include <memory>
+#include <sstream>
+#include <unordered_map>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Arch.h>
+#include <unwindstack/Unwinder.h>
+
+#include "Utils.h"
+#include "utils/OfflineUnwindUtils.h"
+
+// This collection of benchmarks exercises Unwinder::Unwind for offline unwinds.
+//
+// See `libunwindstack/utils/OfflineUnwindUtils.h` for more info on offline unwinds
+// and b/192012600 for additional information regarding these benchmarks.
+namespace unwindstack {
+namespace {
+
+static constexpr char kStartup[] = "startup_case";
+static constexpr char kSteadyState[] = "steady_state_case";
+
+class OfflineUnwindBenchmark : public benchmark::Fixture {
+ public:
+  void SetUp(benchmark::State& state) override {
+    // Ensure each benchmarks has a fresh ELF cache at the start.
+    unwind_case_ = state.range(0) ? kSteadyState : kStartup;
+    resolve_names_ = state.range(1);
+    Elf::SetCachingEnabled(false);
+  }
+
+  void TearDown(benchmark::State& state) override {
+    offline_utils_.ReturnToCurrentWorkingDirectory();
+    mem_tracker_.SetBenchmarkCounters(state);
+  }
+
+  void SingleUnwindBenchmark(benchmark::State& state, const UnwindSampleInfo& sample_info) {
+    std::string error_msg;
+    if (!offline_utils_.Init(sample_info, &error_msg)) {
+      state.SkipWithError(error_msg.c_str());
+      return;
+    }
+    BenchmarkOfflineUnwindMultipleSamples(state, std::vector<UnwindSampleInfo>{sample_info});
+  }
+
+  void ConsecutiveUnwindBenchmark(benchmark::State& state,
+                                  const std::vector<UnwindSampleInfo>& sample_infos) {
+    std::string error_msg;
+    if (!offline_utils_.Init(sample_infos, &error_msg)) {
+      state.SkipWithError(error_msg.c_str());
+      return;
+    }
+    BenchmarkOfflineUnwindMultipleSamples(state, sample_infos);
+  }
+
+ private:
+  void BenchmarkOfflineUnwindMultipleSamples(benchmark::State& state,
+                                             const std::vector<UnwindSampleInfo>& sample_infos) {
+    std::string error_msg;
+    auto offline_unwind_multiple_samples = [&](bool benchmarking_unwind) {
+      // The benchmark should only measure the time / memory usage for the creation of
+      // each Unwinder object and the corresponding unwind as close as possible.
+      if (benchmarking_unwind) state.PauseTiming();
+
+      std::unordered_map<std::string_view, std::unique_ptr<Regs>> regs_copies;
+      for (const auto& sample_info : sample_infos) {
+        const std::string& sample_name = sample_info.offline_files_dir;
+
+        // Need to init unwinder with new copy of regs each iteration because unwinding changes
+        // the attributes of the regs object.
+        regs_copies.emplace(sample_name,
+                            std::unique_ptr<Regs>(offline_utils_.GetRegs(sample_name)->Clone()));
+
+        // The Maps object will still hold the parsed maps from the previous unwinds. So reset them
+        // unless we want to assume all Maps are cached.
+        if (!sample_info.create_maps) {
+          if (!offline_utils_.CreateMaps(&error_msg, sample_name)) {
+            state.SkipWithError(error_msg.c_str());
+            return;
+          }
+
+          // Since this maps object will be cached, need to make sure that
+          // all of the names are fully qualified paths. This allows the
+          // caching mechanism to properly cache elf files that are
+          // actually the same.
+          if (!offline_utils_.ChangeToSampleDirectory(&error_msg, sample_name)) {
+            state.SkipWithError(error_msg.c_str());
+            return;
+          }
+          for (auto& map_info : *offline_utils_.GetMaps(sample_name)) {
+            auto& name = map_info->name();
+            if (!name.empty()) {
+              std::filesystem::path path;
+              if (std::filesystem::is_symlink(name.c_str())) {
+                path = std::filesystem::read_symlink(name.c_str());
+              } else {
+                path = std::filesystem::current_path();
+                path /= name.c_str();
+              }
+              name = path.lexically_normal().c_str();
+            }
+          }
+        }
+      }
+
+      if (benchmarking_unwind) mem_tracker_.StartTrackingAllocations();
+      for (const auto& sample_info : sample_infos) {
+        const std::string& sample_name = sample_info.offline_files_dir;
+        // Need to change to sample directory for Unwinder to properly init ELF objects.
+        // See more info at OfflineUnwindUtils::ChangeToSampleDirectory.
+        if (!offline_utils_.ChangeToSampleDirectory(&error_msg, sample_name)) {
+          state.SkipWithError(error_msg.c_str());
+          return;
+        }
+        if (benchmarking_unwind) state.ResumeTiming();
+
+        Unwinder unwinder(128, offline_utils_.GetMaps(sample_name),
+                          regs_copies.at(sample_name).get(),
+                          offline_utils_.GetProcessMemory(sample_name));
+        if (sample_info.memory_flag == ProcessMemoryFlag::kIncludeJitMemory) {
+          unwinder.SetJitDebug(offline_utils_.GetJitDebug(sample_name));
+        }
+        unwinder.SetResolveNames(resolve_names_);
+        unwinder.Unwind();
+
+        if (benchmarking_unwind) state.PauseTiming();
+        size_t expected_num_frames;
+        if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg, sample_name)) {
+          state.SkipWithError(error_msg.c_str());
+          return;
+        }
+        if (unwinder.NumFrames() != expected_num_frames) {
+          std::stringstream err_stream;
+          err_stream << "Failed to unwind sample " << sample_name << " properly.Expected "
+                     << expected_num_frames << " frames, but unwinder contained "
+                     << unwinder.NumFrames() << " frames. Unwind:\n"
+                     << DumpFrames(unwinder);
+          state.SkipWithError(err_stream.str().c_str());
+          return;
+        }
+      }
+      if (benchmarking_unwind) mem_tracker_.StopTrackingAllocations();
+    };
+
+    if (unwind_case_ == kSteadyState) {
+      WarmUpUnwindCaches(offline_unwind_multiple_samples);
+    }
+
+    for (auto _ : state) {
+      offline_unwind_multiple_samples(/*benchmarking_unwind=*/true);
+    }
+  }
+
+  // This functions main purpose is to enable ELF caching for the steady state unwind case
+  // and then perform one unwind to warm up the cache for subsequent unwinds.
+  //
+  // Another reason for pulling this functionality out of the main benchmarking function is
+  // to add an additional call stack frame in between the cache warm-up unwinds and
+  // BenchmarkOfflineUnwindMultipleSamples so that it is easy to filter this set of unwinds out
+  // when profiling.
+  void WarmUpUnwindCaches(const std::function<void(bool)>& offline_unwind_multiple_samples) {
+    Elf::SetCachingEnabled(true);
+    offline_unwind_multiple_samples(/*benchmarking_unwind=*/false);
+  }
+
+  std::string unwind_case_;
+  bool resolve_names_;
+  MemoryTracker mem_tracker_;
+  OfflineUnwindUtils offline_utils_;
+};
+
+BENCHMARK_DEFINE_F(OfflineUnwindBenchmark, BM_offline_straddle_arm64)(benchmark::State& state) {
+  SingleUnwindBenchmark(
+      state, {.offline_files_dir = "straddle_arm64/", .arch = ARCH_ARM64, .create_maps = false});
+}
+BENCHMARK_REGISTER_F(OfflineUnwindBenchmark, BM_offline_straddle_arm64)
+    ->ArgNames({"is_steady_state_case", "resolve_names"})
+    ->Ranges({{false, true}, {false, true}});
+
+BENCHMARK_DEFINE_F(OfflineUnwindBenchmark, BM_offline_straddle_arm64_cached_maps)
+(benchmark::State& state) {
+  SingleUnwindBenchmark(state, {.offline_files_dir = "straddle_arm64/", .arch = ARCH_ARM64});
+}
+BENCHMARK_REGISTER_F(OfflineUnwindBenchmark, BM_offline_straddle_arm64_cached_maps)
+    ->ArgNames({"is_steady_state_case", "resolve_names"})
+    ->Ranges({{false, true}, {false, true}});
+
+BENCHMARK_DEFINE_F(OfflineUnwindBenchmark, BM_offline_jit_debug_arm)(benchmark::State& state) {
+  SingleUnwindBenchmark(state, {.offline_files_dir = "jit_debug_arm/",
+                                .arch = ARCH_ARM,
+                                .memory_flag = ProcessMemoryFlag::kIncludeJitMemory,
+                                .create_maps = false});
+}
+BENCHMARK_REGISTER_F(OfflineUnwindBenchmark, BM_offline_jit_debug_arm)
+    ->ArgNames({"is_steady_state_case", "resolve_names"})
+    ->Ranges({{false, true}, {false, true}});
+
+BENCHMARK_DEFINE_F(OfflineUnwindBenchmark, BM_offline_profiler_like_multi_process)
+(benchmark::State& state) {
+  ConsecutiveUnwindBenchmark(
+      state,
+      std::vector<UnwindSampleInfo>{
+          {.offline_files_dir = "bluetooth_arm64/pc_1/", .arch = ARCH_ARM64, .create_maps = false},
+          {.offline_files_dir = "jit_debug_arm/",
+           .arch = ARCH_ARM,
+           .memory_flag = ProcessMemoryFlag::kIncludeJitMemory,
+           .create_maps = false},
+          {.offline_files_dir = "photos_reset_arm64/", .arch = ARCH_ARM64, .create_maps = false},
+          {.offline_files_dir = "youtube_compiled_arm64/",
+           .arch = ARCH_ARM64,
+           .create_maps = false},
+          {.offline_files_dir = "yt_music_arm64/", .arch = ARCH_ARM64, .create_maps = false},
+          {.offline_files_dir = "maps_compiled_arm64/28656_oat_odex_jar/",
+           .arch = ARCH_ARM64,
+           .create_maps = false}});
+}
+BENCHMARK_REGISTER_F(OfflineUnwindBenchmark, BM_offline_profiler_like_multi_process)
+    ->ArgNames({"is_steady_state_case", "resolve_names"})
+    ->Ranges({{false, true}, {false, true}});
+
+BENCHMARK_DEFINE_F(OfflineUnwindBenchmark, BM_offline_profiler_like_single_process_multi_thread)
+(benchmark::State& state) {
+  ConsecutiveUnwindBenchmark(
+      state,
+      std::vector<UnwindSampleInfo>{{.offline_files_dir = "maps_compiled_arm64/28656_oat_odex_jar/",
+                                     .arch = ARCH_ARM64,
+                                     .create_maps = false},
+                                    {.offline_files_dir = "maps_compiled_arm64/28613_main-thread/",
+                                     .arch = ARCH_ARM64,
+                                     .create_maps = false},
+                                    {.offline_files_dir = "maps_compiled_arm64/28644/",
+                                     .arch = ARCH_ARM64,
+                                     .create_maps = false},
+                                    {.offline_files_dir = "maps_compiled_arm64/28648/",
+                                     .arch = ARCH_ARM64,
+                                     .create_maps = false},
+                                    {.offline_files_dir = "maps_compiled_arm64/28667/",
+                                     .arch = ARCH_ARM64,
+                                     .create_maps = false}});
+}
+BENCHMARK_REGISTER_F(OfflineUnwindBenchmark, BM_offline_profiler_like_single_process_multi_thread)
+    ->ArgNames({"is_steady_state_case", "resolve_names"})
+    ->Ranges({{false, true}, {false, true}});
+
+BENCHMARK_DEFINE_F(OfflineUnwindBenchmark, BM_offline_profiler_like_single_thread_diverse_pcs)
+(benchmark::State& state) {
+  ConsecutiveUnwindBenchmark(
+      state,
+      std::vector<UnwindSampleInfo>{
+          {.offline_files_dir = "bluetooth_arm64/pc_1/", .arch = ARCH_ARM64, .create_maps = false},
+          {.offline_files_dir = "bluetooth_arm64/pc_2/", .arch = ARCH_ARM64, .create_maps = false},
+          {.offline_files_dir = "bluetooth_arm64/pc_3/", .arch = ARCH_ARM64, .create_maps = false},
+          {.offline_files_dir = "bluetooth_arm64/pc_4/",
+           .arch = ARCH_ARM64,
+           .create_maps = false}});
+}
+BENCHMARK_REGISTER_F(OfflineUnwindBenchmark, BM_offline_profiler_like_single_thread_diverse_pcs)
+    ->ArgNames({"is_steady_state_case", "resolve_names"})
+    ->Ranges({{false, true}, {false, true}});
+
+}  // namespace
+}  // namespace unwindstack
diff --git a/libunwindstack/benchmarks/SymbolBenchmark.cpp b/libunwindstack/benchmarks/SymbolBenchmark.cpp
index 8b65b15..f181b57 100644
--- a/libunwindstack/benchmarks/SymbolBenchmark.cpp
+++ b/libunwindstack/benchmarks/SymbolBenchmark.cpp
@@ -47,7 +47,7 @@
 
     unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(elf_file, 0).release());
     if (!elf.Init() || !elf.valid()) {
-      errx(1, "Internal Error: Cannot open elf.");
+      errx(1, "Internal Error: Cannot open elf: %s", elf_file.c_str());
     }
 
     unwindstack::SharedString name;
@@ -129,3 +129,25 @@
                         GetSymbolSortedElfFile(), true);
 }
 BENCHMARK(BM_elf_and_symbol_find_multiple_from_sorted);
+
+void BM_elf_and_symbol_not_present_from_large_compressed_frame(benchmark::State& state) {
+  BenchmarkSymbolLookup(state, 0, GetLargeCompressedFrameElfFile(), false);
+}
+BENCHMARK(BM_elf_and_symbol_not_present_from_large_compressed_frame);
+
+void BM_elf_and_symbol_find_single_from_large_compressed_frame(benchmark::State& state) {
+  BenchmarkSymbolLookup(state, 0x202aec, GetLargeCompressedFrameElfFile(), true);
+}
+BENCHMARK(BM_elf_and_symbol_find_single_from_large_compressed_frame);
+
+void BM_elf_and_symbol_find_single_many_times_from_large_compressed_frame(benchmark::State& state) {
+  BenchmarkSymbolLookup(state, 0x202aec, GetLargeCompressedFrameElfFile(), true, 4096);
+}
+BENCHMARK(BM_elf_and_symbol_find_single_many_times_from_large_compressed_frame);
+
+void BM_elf_and_symbol_find_multiple_from_large_compressed_frame(benchmark::State& state) {
+  BenchmarkSymbolLookup(state,
+                        std::vector<uint64_t>{0x202aec, 0x23e74c, 0xd000c, 0x201b10, 0x183060},
+                        GetLargeCompressedFrameElfFile(), true);
+}
+BENCHMARK(BM_elf_and_symbol_find_multiple_from_large_compressed_frame);
diff --git a/libunwindstack/benchmarks/Utils.cpp b/libunwindstack/benchmarks/Utils.cpp
index c92f109..3750168 100644
--- a/libunwindstack/benchmarks/Utils.cpp
+++ b/libunwindstack/benchmarks/Utils.cpp
@@ -17,6 +17,9 @@
 #include <err.h>
 #include <stdint.h>
 
+#include <benchmark/benchmark.h>
+#include <malloc.h>
+
 #include <string>
 #include <vector>
 
@@ -27,17 +30,30 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/Memory.h>
 
+#include "utils/OfflineUnwindUtils.h"
+
+#include "Utils.h"
+
+std::string GetBenchmarkFilesDirectory() {
+  std::string path = android::base::GetExecutableDirectory() + "/benchmarks/files/";
+  unwindstack::DecompressFiles(path);
+  return path;
+}
+
 std::string GetElfFile() {
-  return android::base::GetExecutableDirectory() + "/benchmarks/files/libart_arm.so";
+  return GetBenchmarkFilesDirectory() + "libart_arm.so";
 }
 
 std::string GetSymbolSortedElfFile() {
-  return android::base::GetExecutableDirectory() + "/benchmarks/files/boot_arm.oat";
+  return GetBenchmarkFilesDirectory() + "boot_arm.oat";
 }
 
-std::string GetCompressedElfFile() {
-  // Both are the same right now.
-  return GetSymbolSortedElfFile();
+std::string GetLargeCompressedFrameElfFile() {
+  return GetBenchmarkFilesDirectory() + "libpac.so";
+}
+
+std::string GetLargeEhFrameElfFile() {
+  return GetBenchmarkFilesDirectory() + "libLLVM_android.so";
 }
 
 #if defined(__BIONIC__)
@@ -60,3 +76,42 @@
   }
 }
 #endif
+
+void MemoryTracker::SetBenchmarkCounters(benchmark::State& state) {
+  total_iterations_ += state.iterations();
+#if defined(__BIONIC__)
+  state.counters["MEAN_RSS_BYTES"] = total_rss_bytes_ / static_cast<double>(total_iterations_);
+  state.counters["MAX_RSS_BYTES"] = max_rss_bytes_;
+  state.counters["MIN_RSS_BYTES"] = min_rss_bytes_;
+#endif
+  state.counters["MEAN_ALLOCATED_BYTES"] =
+      total_alloc_bytes_ / static_cast<double>(total_iterations_);
+  state.counters["MAX_ALLOCATED_BYTES"] = max_alloc_bytes_;
+  state.counters["MIN_ALLOCATED_BYTES"] = min_alloc_bytes_;
+}
+
+void MemoryTracker::StartTrackingAllocations() {
+#if defined(__BIONIC__)
+  mallopt(M_PURGE, 0);
+  rss_bytes_before_ = 0;
+  GatherRss(&rss_bytes_before_);
+#endif
+  alloc_bytes_before_ = mallinfo().uordblks;
+}
+
+void MemoryTracker::StopTrackingAllocations() {
+#if defined(__BIONIC__)
+  mallopt(M_PURGE, 0);
+#endif
+  uint64_t bytes_alloced = mallinfo().uordblks - alloc_bytes_before_;
+  total_alloc_bytes_ += bytes_alloced;
+  if (bytes_alloced > max_alloc_bytes_) max_alloc_bytes_ = bytes_alloced;
+  if (bytes_alloced < min_alloc_bytes_) min_alloc_bytes_ = bytes_alloced;
+#if defined(__BIONIC__)
+  uint64_t rss_bytes = 0;
+  GatherRss(&rss_bytes);
+  total_rss_bytes_ += rss_bytes - rss_bytes_before_;
+  if (rss_bytes > max_rss_bytes_) max_rss_bytes_ = rss_bytes;
+  if (rss_bytes < min_rss_bytes_) min_rss_bytes_ = rss_bytes;
+#endif
+}
diff --git a/libunwindstack/benchmarks/Utils.h b/libunwindstack/benchmarks/Utils.h
index bee6efc..2d05af9 100644
--- a/libunwindstack/benchmarks/Utils.h
+++ b/libunwindstack/benchmarks/Utils.h
@@ -14,18 +14,25 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_UTILS_H
-#define _LIBUNWINDSTACK_UTILS_H
+#pragma once
 
+#include <benchmark/benchmark.h>
 #include <stdint.h>
 
 #include <string>
 
+std::string GetBenchmarkFilesDirectory();
+
 std::string GetElfFile();
 
 std::string GetSymbolSortedElfFile();
 
-std::string GetCompressedElfFile();
+// GetLargeCompressedFrameElfFile and GetLargeEhFrameElfFile were added to provide larger
+// ELF files for more representative benchmarks. Theses ELF files will enable validation
+// of optimizations to the unwindstack::Elf.
+std::string GetLargeCompressedFrameElfFile();
+
+std::string GetLargeEhFrameElfFile();
 
 #if defined(__BIONIC__)
 
@@ -36,4 +43,25 @@
 
 #endif
 
-#endif  // _LIBUNWINDSTACK_UTILS_h
+class MemoryTracker {
+ public:
+  void StartTrackingAllocations();
+  void StopTrackingAllocations();
+  void SetBenchmarkCounters(benchmark::State& state);
+
+ private:
+#if defined(__BIONIC__)
+  uint64_t total_rss_bytes_ = 0;
+  uint64_t min_rss_bytes_ = 0;
+  uint64_t max_rss_bytes_ = 0;
+  uint64_t rss_bytes_before_;
+#endif
+  uint64_t total_alloc_bytes_ = 0;
+  uint64_t min_alloc_bytes_ = std::numeric_limits<uint64_t>::max();
+  uint64_t max_alloc_bytes_ = 0;
+  uint64_t alloc_bytes_before_;
+  // Benchmarks may run multiple times (the whole benchmark not just what is in the ranged based
+  // for loop) but this instance is not destructed and re-constructed each time. So this holds the
+  // total number of iterations of the ranged for loop across all runs of a single benchmark.
+  size_t total_iterations_ = 0;
+};
diff --git a/libunwindstack/benchmarks/files/boot_arm.oat b/libunwindstack/benchmarks/files/boot_arm.oat
deleted file mode 100644
index 51188eb..0000000
--- a/libunwindstack/benchmarks/files/boot_arm.oat
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/benchmarks/files/boot_arm.oat.gz b/libunwindstack/benchmarks/files/boot_arm.oat.gz
new file mode 100644
index 0000000..4b236b8
--- /dev/null
+++ b/libunwindstack/benchmarks/files/boot_arm.oat.gz
Binary files differ
diff --git a/libunwindstack/benchmarks/files/libLLVM_android.so.gz b/libunwindstack/benchmarks/files/libLLVM_android.so.gz
new file mode 100644
index 0000000..5edc266
--- /dev/null
+++ b/libunwindstack/benchmarks/files/libLLVM_android.so.gz
Binary files differ
diff --git a/libunwindstack/benchmarks/files/libart_arm.so b/libunwindstack/benchmarks/files/libart_arm.so
deleted file mode 100644
index 2201faf..0000000
--- a/libunwindstack/benchmarks/files/libart_arm.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/benchmarks/files/libart_arm.so.gz b/libunwindstack/benchmarks/files/libart_arm.so.gz
new file mode 100644
index 0000000..963f453
--- /dev/null
+++ b/libunwindstack/benchmarks/files/libart_arm.so.gz
Binary files differ
diff --git a/libunwindstack/benchmarks/files/libpac.so.gz b/libunwindstack/benchmarks/files/libpac.so.gz
new file mode 100644
index 0000000..a5df2be
--- /dev/null
+++ b/libunwindstack/benchmarks/files/libpac.so.gz
Binary files differ
diff --git a/libunwindstack/benchmarks/local_unwind_benchmarks.cpp b/libunwindstack/benchmarks/local_unwind_benchmarks.cpp
index 69c6091..78c8c13 100644
--- a/libunwindstack/benchmarks/local_unwind_benchmarks.cpp
+++ b/libunwindstack/benchmarks/local_unwind_benchmarks.cpp
@@ -22,12 +22,13 @@
 
 #include <android-base/strings.h>
 
-#include <unwindstack/LocalUnwinder.h>
+#include <unwindstack/AndroidUnwinder.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
 #include <unwindstack/RegsGetLocal.h>
 #include <unwindstack/Unwinder.h>
+#include "MemoryLocalUnsafe.h"
 
 constexpr size_t kMaxFrames = 32;
 
@@ -75,13 +76,6 @@
   return unwinder.NumFrames();
 }
 
-static size_t LocalUnwind(void* unwind_ptr) {
-  unwindstack::LocalUnwinder* unwinder = reinterpret_cast<unwindstack::LocalUnwinder*>(unwind_ptr);
-  std::vector<unwindstack::LocalFrameData> frame_info;
-  unwinder->Unwind(&frame_info, kMaxFrames);
-  return frame_info.size();
-}
-
 static void BM_local_unwind_uncached_process_memory(benchmark::State& state) {
   auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
   unwindstack::LocalMaps maps;
@@ -106,6 +100,54 @@
 }
 BENCHMARK(BM_local_unwind_cached_process_memory);
 
+static void BM_local_android_unwind_uncached_process_memory(benchmark::State& state) {
+  auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
+  unwindstack::AndroidLocalUnwinder unwinder(process_memory);
+  unwindstack::ErrorData error;
+  if (!unwinder.Initialize(error)) {
+    state.SkipWithError("Failed to initialize.");
+  }
+
+  for (auto _ : state) {
+    if (LocalCall1(
+            [](void* u) -> size_t {
+              unwindstack::AndroidLocalUnwinder* unwinder =
+                  reinterpret_cast<unwindstack::AndroidLocalUnwinder*>(u);
+              unwindstack::AndroidUnwinderData data;
+              unwinder->Unwind(data);
+              return data.frames.size();
+            },
+            &unwinder) < 5) {
+      state.SkipWithError("Failed to unwind.");
+    }
+  }
+}
+BENCHMARK(BM_local_android_unwind_uncached_process_memory);
+
+static void BM_local_android_unwind_cached_process_memory(benchmark::State& state) {
+  auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
+  unwindstack::AndroidLocalUnwinder unwinder(process_memory);
+  unwindstack::ErrorData error;
+  if (!unwinder.Initialize(error)) {
+    state.SkipWithError("Failed to initialize.");
+  }
+
+  for (auto _ : state) {
+    if (LocalCall1(
+            [](void* u) -> size_t {
+              unwindstack::AndroidLocalUnwinder* unwinder =
+                  reinterpret_cast<unwindstack::AndroidLocalUnwinder*>(u);
+              unwindstack::AndroidUnwinderData data;
+              unwinder->Unwind(data);
+              return data.frames.size();
+            },
+            &unwinder) < 5) {
+      state.SkipWithError("Failed to unwind.");
+    }
+  }
+}
+BENCHMARK(BM_local_android_unwind_cached_process_memory);
+
 static void BM_local_unwind_local_updatable_maps_uncached(benchmark::State& state) {
   auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
   unwindstack::LocalUpdatableMaps maps;
@@ -142,16 +184,6 @@
 }
 BENCHMARK(BM_local_unwind_local_updatable_maps_thread_cached);
 
-static void BM_local_unwind_local_unwinder(benchmark::State& state) {
-  unwindstack::LocalUnwinder unwinder;
-  if (!unwinder.Init()) {
-    state.SkipWithError("Failed to init local unwinder.");
-  }
-
-  Run(state, LocalUnwind, &unwinder);
-}
-BENCHMARK(BM_local_unwind_local_unwinder);
-
 static void BM_local_unwind_uncached_process_memory_no_func_names(benchmark::State& state) {
   auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
   unwindstack::LocalMaps maps;
@@ -199,3 +231,15 @@
   Run(state, Unwind, &data);
 }
 BENCHMARK(BM_local_unwind_local_updatable_maps_cached_no_func_names);
+
+static void BM_local_unwind_uncached_process_memory_unsafe_reads(benchmark::State& state) {
+  std::shared_ptr<unwindstack::Memory> process_memory(new unwindstack::MemoryLocalUnsafe());
+  unwindstack::LocalMaps maps;
+  if (!maps.Parse()) {
+    state.SkipWithError("Failed to parse local maps.");
+  }
+
+  UnwindData data = {.process_memory = process_memory, .maps = &maps, .resolve_names = true};
+  Run(state, Unwind, &data);
+}
+BENCHMARK(BM_local_unwind_uncached_process_memory_unsafe_reads);
diff --git a/libunwindstack/benchmarks/main.cpp b/libunwindstack/benchmarks/main.cpp
index 02b2fdf..3fc1db9 100644
--- a/libunwindstack/benchmarks/main.cpp
+++ b/libunwindstack/benchmarks/main.cpp
@@ -16,4 +16,80 @@
 
 #include <benchmark/benchmark.h>
 
-BENCHMARK_MAIN();
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <android-base/strings.h>
+
+#include <string>
+#include <vector>
+
+#if defined(__APPLE__)
+
+// Darwin doesn't support this, so do nothing.
+bool LockToCPU(int) {
+  return false;
+}
+
+#else
+
+#include <errno.h>
+#include <sched.h>
+
+bool LockToCPU(int lock_cpu) {
+  cpu_set_t cpuset;
+
+  CPU_ZERO(&cpuset);
+  CPU_SET(lock_cpu, &cpuset);
+  if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) {
+    if (errno == EINVAL) {
+      printf("Invalid cpu %d\n", lock_cpu);
+    } else {
+      perror("sched_setaffinity failed");
+    }
+    return false;
+  }
+
+  printf("Locked to cpu %d\n", lock_cpu);
+  return true;
+}
+
+#endif
+
+int main(int argc, char** argv) {
+#if defined(__BIONIC__)
+  // Enable decay time option to allow frees to run faster at the cost of slightly increasing RSS.
+  // All applications on Android run with this option enabled.
+  mallopt(M_DECAY_TIME, 1);
+#endif
+  std::vector<char*> new_argv;
+  // The first argument is not an option, so add it as is.
+  new_argv.push_back(argv[0]);
+
+  // Look for the special option --benchmark_lock_cpu.
+  int lock_cpu = -1;
+  for (int i = 1; i < argc; i++) {
+    if (android::base::StartsWith(argv[i], "--benchmark_cpu=")) {
+      char* endptr;
+      long cpu = strtol(&argv[i][16], &endptr, 10);
+      if (endptr == nullptr || *endptr != '\0' || cpu > INT_MAX || cpu < 0) {
+        printf("Malformed value for --benchmark_cpu, requires a valid positive number.\n");
+        return 1;
+      }
+      lock_cpu = cpu;
+    } else {
+      new_argv.push_back(argv[i]);
+    }
+  }
+  new_argv.push_back(nullptr);
+
+  if (lock_cpu != -1 && !LockToCPU(lock_cpu)) {
+    return 1;
+  }
+
+  int new_argc = new_argv.size() - 1;
+  ::benchmark::Initialize(&new_argc, new_argv.data());
+  if (::benchmark::ReportUnrecognizedArguments(new_argc, new_argv.data())) return 1;
+  ::benchmark::RunSpecifiedBenchmarks();
+}
diff --git a/libunwindstack/benchmarks/remote_unwind_benchmarks.cpp b/libunwindstack/benchmarks/remote_unwind_benchmarks.cpp
index ef07e39..29c416d 100644
--- a/libunwindstack/benchmarks/remote_unwind_benchmarks.cpp
+++ b/libunwindstack/benchmarks/remote_unwind_benchmarks.cpp
@@ -24,35 +24,26 @@
 
 #include <benchmark/benchmark.h>
 
+#include <unwindstack/AndroidUnwinder.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
 #include <unwindstack/Unwinder.h>
 
 #include "MemoryRemote.h"
+#include "PidUtils.h"
 #include "tests/TestUtils.h"
 
 static bool WaitForRemote(pid_t pid, volatile bool* ready_ptr) {
-  usleep(1000);
-  for (size_t i = 0; i < 1000; i++) {
-    if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
-      unwindstack::TestQuiescePid(pid);
-
-      unwindstack::MemoryRemote memory(pid);
-      bool ready;
-      uint64_t ready_addr = reinterpret_cast<uint64_t>(ready_ptr);
-      if (memory.ReadFully(ready_addr, &ready, sizeof(ready)) && ready) {
-        return true;
-      }
-    } else if (errno != ESRCH) {
-      // Attach failed with unknown error.
-      perror("Ptrace failed:");
-      return false;
+  return unwindstack::RunWhenQuiesced(pid, true, [pid, ready_ptr]() {
+    unwindstack::MemoryRemote memory(pid);
+    bool ready;
+    uint64_t ready_addr = reinterpret_cast<uint64_t>(ready_ptr);
+    if (memory.ReadFully(ready_addr, &ready, sizeof(ready)) && ready) {
+      return unwindstack::PID_RUN_PASS;
     }
-    usleep(5000);
-  }
-  printf("Pid %d did not quiesce in a timely fashion.\n", pid);
-  return false;
+    return unwindstack::PID_RUN_KEEP_GOING;
+  });
 }
 
 size_t RemoteCall6(volatile bool* ready) {
@@ -141,3 +132,42 @@
   RemoteUnwind(state, true);
 }
 BENCHMARK(BM_remote_unwind_cached);
+
+static void RemoteAndroidUnwind(benchmark::State& state, bool cached) {
+  pid_t pid = StartRemoteRun();
+  if (pid == -1) {
+    state.SkipWithError("Failed to start remote process.");
+  }
+  unwindstack::TestScopedPidReaper reap(pid);
+
+  std::shared_ptr<unwindstack::Memory> process_memory;
+  if (cached) {
+    process_memory = unwindstack::Memory::CreateProcessMemoryCached(pid);
+  } else {
+    process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+  }
+  unwindstack::AndroidRemoteUnwinder unwinder(pid, process_memory);
+  unwindstack::ErrorData error;
+  if (!unwinder.Initialize(error)) {
+    state.SkipWithError("Failed to initialize unwinder.");
+  }
+
+  for (auto _ : state) {
+    unwindstack::AndroidUnwinderData data;
+    if (!unwinder.Unwind(data) || data.frames.size() < 5) {
+      state.SkipWithError("Failed to unwind properly.");
+    }
+  }
+
+  ptrace(PTRACE_DETACH, pid, 0, 0);
+}
+
+static void BM_remote_android_unwind_uncached(benchmark::State& state) {
+  RemoteAndroidUnwind(state, true);
+}
+BENCHMARK(BM_remote_android_unwind_uncached);
+
+static void BM_remote_android_unwind_cached(benchmark::State& state) {
+  RemoteAndroidUnwind(state, true);
+}
+BENCHMARK(BM_remote_android_unwind_cached);
diff --git a/libunwindstack/include/GlobalDebugInterface.h b/libunwindstack/include/GlobalDebugInterface.h
index 45eec63..b1e9106 100644
--- a/libunwindstack/include/GlobalDebugInterface.h
+++ b/libunwindstack/include/GlobalDebugInterface.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_GLOBAL_DEBUG_INTERFACE_H
-#define _LIBUNWINDSTACK_GLOBAL_DEBUG_INTERFACE_H
+#pragma once
 
 #include <stdint.h>
 #include <memory>
@@ -41,5 +40,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_GLOBAL_DEBUG_INTERFACE_H
diff --git a/libunwindstack/include/unwindstack/AndroidUnwinder.h b/libunwindstack/include/unwindstack/AndroidUnwinder.h
new file mode 100644
index 0000000..de5579d
--- /dev/null
+++ b/libunwindstack/include/unwindstack/AndroidUnwinder.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <unwindstack/Arch.h>
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/Error.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/SharedString.h>
+#include <unwindstack/Unwinder.h>
+
+namespace unwindstack {
+
+struct AndroidUnwinderData {
+  AndroidUnwinderData() = default;
+  explicit AndroidUnwinderData(const size_t max_frames) : max_frames(max_frames) {}
+  explicit AndroidUnwinderData(const bool show_all_frames) : show_all_frames(show_all_frames) {}
+
+  void DemangleFunctionNames();
+
+  std::string GetErrorString();
+
+  std::vector<FrameData> frames;
+  ErrorData error;
+  std::optional<std::unique_ptr<Regs>> saved_initial_regs;
+  const std::optional<size_t> max_frames;
+  const bool show_all_frames = false;
+};
+
+class AndroidUnwinder {
+ public:
+  AndroidUnwinder(pid_t pid) : pid_(pid) {}
+  AndroidUnwinder(pid_t pid, std::shared_ptr<Memory>& memory)
+      : pid_(pid), process_memory_(memory) {}
+  AndroidUnwinder(pid_t pid, ArchEnum arch) : pid_(pid), arch_(arch) {}
+  AndroidUnwinder(pid_t pid, const std::vector<std::string> initial_map_names_to_skip)
+      : pid_(pid), initial_map_names_to_skip_(std::move(initial_map_names_to_skip)) {}
+  AndroidUnwinder(pid_t pid, const std::vector<std::string> initial_map_names_to_skip,
+                  const std::vector<std::string> map_suffixes_to_ignore)
+      : pid_(pid),
+        initial_map_names_to_skip_(std::move(initial_map_names_to_skip)),
+        map_suffixes_to_ignore_(std::move(map_suffixes_to_ignore)) {}
+  virtual ~AndroidUnwinder() = default;
+
+  bool Initialize(ErrorData& error);
+
+  std::shared_ptr<Memory>& GetProcessMemory() { return process_memory_; }
+  unwindstack::Maps* GetMaps() { return maps_.get(); }
+
+  const JitDebug& GetJitDebug() { return *jit_debug_.get(); }
+  const DexFiles& GetDexFiles() { return *dex_files_.get(); }
+
+  std::string FormatFrame(const FrameData& frame) const;
+
+  bool Unwind(AndroidUnwinderData& data);
+  bool Unwind(std::optional<pid_t> tid, AndroidUnwinderData& data);
+  bool Unwind(void* ucontext, AndroidUnwinderData& data);
+  bool Unwind(Regs* initial_regs, AndroidUnwinderData& data);
+
+  FrameData BuildFrameFromPcOnly(uint64_t pc);
+
+  static AndroidUnwinder* Create(pid_t pid);
+
+ protected:
+  virtual bool InternalInitialize(ErrorData& error) = 0;
+
+  virtual bool InternalUnwind(std::optional<pid_t> tid, AndroidUnwinderData& data) = 0;
+
+  pid_t pid_;
+
+  size_t max_frames_ = kMaxNumFrames;
+  std::vector<std::string> initial_map_names_to_skip_;
+  std::vector<std::string> map_suffixes_to_ignore_;
+  std::once_flag initialize_;
+
+  ArchEnum arch_ = ARCH_UNKNOWN;
+
+  std::shared_ptr<Maps> maps_;
+  std::shared_ptr<Memory> process_memory_;
+  std::unique_ptr<JitDebug> jit_debug_;
+  std::unique_ptr<DexFiles> dex_files_;
+
+  static constexpr size_t kMaxNumFrames = 512;
+};
+
+class AndroidLocalUnwinder : public AndroidUnwinder {
+ public:
+  AndroidLocalUnwinder() : AndroidUnwinder(getpid()) {
+    initial_map_names_to_skip_.emplace_back(kUnwindstackLib);
+  }
+  AndroidLocalUnwinder(std::shared_ptr<Memory>& process_memory)
+      : AndroidUnwinder(getpid(), process_memory) {
+    initial_map_names_to_skip_.emplace_back(kUnwindstackLib);
+  }
+  AndroidLocalUnwinder(const std::vector<std::string>& initial_map_names_to_skip)
+      : AndroidUnwinder(getpid(), initial_map_names_to_skip) {
+    initial_map_names_to_skip_.emplace_back(kUnwindstackLib);
+  }
+  AndroidLocalUnwinder(const std::vector<std::string>& initial_map_names_to_skip,
+                       const std::vector<std::string>& map_suffixes_to_ignore)
+      : AndroidUnwinder(getpid(), initial_map_names_to_skip, map_suffixes_to_ignore) {
+    initial_map_names_to_skip_.emplace_back(kUnwindstackLib);
+  }
+  virtual ~AndroidLocalUnwinder() = default;
+
+ protected:
+  static constexpr const char* kUnwindstackLib = "libunwindstack.so";
+
+  bool InternalInitialize(ErrorData& error) override;
+
+  bool InternalUnwind(std::optional<pid_t> tid, AndroidUnwinderData& data) override;
+};
+
+class AndroidRemoteUnwinder : public AndroidUnwinder {
+ public:
+  AndroidRemoteUnwinder(pid_t pid) : AndroidUnwinder(pid) {}
+  AndroidRemoteUnwinder(pid_t pid, std::shared_ptr<Memory>& process_memory)
+      : AndroidUnwinder(pid, process_memory) {}
+  AndroidRemoteUnwinder(pid_t pid, ArchEnum arch) : AndroidUnwinder(pid, arch) {}
+  AndroidRemoteUnwinder(pid_t pid, const std::vector<std::string> initial_map_names_to_skip)
+      : AndroidUnwinder(pid, initial_map_names_to_skip) {}
+  AndroidRemoteUnwinder(pid_t pid, const std::vector<std::string> initial_map_names_to_skip,
+                        const std::vector<std::string> map_suffixes_to_ignore)
+      : AndroidUnwinder(pid, initial_map_names_to_skip, map_suffixes_to_ignore) {}
+  virtual ~AndroidRemoteUnwinder() = default;
+
+ protected:
+  bool InternalInitialize(ErrorData& error) override;
+
+  bool InternalUnwind(std::optional<pid_t> tid, AndroidUnwinderData& data) override;
+};
+
+}  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Arch.h b/libunwindstack/include/unwindstack/Arch.h
index 7060004..42180b4 100644
--- a/libunwindstack/include/unwindstack/Arch.h
+++ b/libunwindstack/include/unwindstack/Arch.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_ARCH_H
-#define _LIBUNWINDSTACK_ARCH_H
+#pragma once
 
 #include <stddef.h>
 
@@ -43,5 +42,3 @@
 }
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_ARCH_H
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
index 2a1b770..101d772 100644
--- a/libunwindstack/include/unwindstack/DexFiles.h
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_DEX_FILES_H
-#define _LIBUNWINDSTACK_DEX_FILES_H
+#pragma once
 
 #include <stdint.h>
 
@@ -36,5 +35,3 @@
                                          std::vector<std::string> search_libs = {});
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_DEX_FILES_H
diff --git a/libunwindstack/include/unwindstack/DwarfError.h b/libunwindstack/include/unwindstack/DwarfError.h
index 763e2cb..6143523 100644
--- a/libunwindstack/include/unwindstack/DwarfError.h
+++ b/libunwindstack/include/unwindstack/DwarfError.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_DWARF_ERROR_H
-#define _LIBUNWINDSTACK_DWARF_ERROR_H
+#pragma once
 
 #include <stdint.h>
 
@@ -40,5 +39,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/include/unwindstack/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h
index 22ed47a..9726f15 100644
--- a/libunwindstack/include/unwindstack/DwarfLocation.h
+++ b/libunwindstack/include/unwindstack/DwarfLocation.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_DWARF_LOCATION_H
-#define _LIBUNWINDSTACK_DWARF_LOCATION_H
+#pragma once
 
 #include <stdint.h>
 
@@ -49,5 +48,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_DWARF_LOCATION_H
diff --git a/libunwindstack/include/unwindstack/DwarfMemory.h b/libunwindstack/include/unwindstack/DwarfMemory.h
index c45699a..2ef3b30 100644
--- a/libunwindstack/include/unwindstack/DwarfMemory.h
+++ b/libunwindstack/include/unwindstack/DwarfMemory.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_DWARF_MEMORY_H
-#define _LIBUNWINDSTACK_DWARF_MEMORY_H
+#pragma once
 
 #include <stdint.h>
 
@@ -72,5 +71,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_DWARF_MEMORY_H
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index 71ca62e..33435b2 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_DWARF_SECTION_H
-#define _LIBUNWINDSTACK_DWARF_SECTION_H
+#pragma once
 
 #include <stdint.h>
 
@@ -179,5 +178,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_DWARF_SECTION_H
diff --git a/libunwindstack/include/unwindstack/DwarfStructs.h b/libunwindstack/include/unwindstack/DwarfStructs.h
index 3d8c2db..4c05360 100644
--- a/libunwindstack/include/unwindstack/DwarfStructs.h
+++ b/libunwindstack/include/unwindstack/DwarfStructs.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_DWARF_STRUCTS_H
-#define _LIBUNWINDSTACK_DWARF_STRUCTS_H
+#pragma once
 
 #include <stdint.h>
 
@@ -51,5 +50,3 @@
 constexpr uint16_t CFA_REG = static_cast<uint16_t>(-1);
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_DWARF_STRUCTS_H
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 01d9af9..0985042 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_ELF_H
-#define _LIBUNWINDSTACK_ELF_H
+#pragma once
 
 #include <stddef.h>
 
@@ -68,6 +67,8 @@
 
   std::string GetBuildID();
 
+  std::string GetPrintableBuildID();
+
   int64_t GetLoadBias() { return load_bias_; }
 
   bool IsValidPc(uint64_t pc);
@@ -100,14 +101,20 @@
 
   static std::string GetBuildID(Memory* memory);
 
+  // Caching cannot be enabled/disabled while unwinding. It is assumed
+  // that once enabled, it remains enabled while all unwinds are running.
+  // If the state of the caching changes while unwinding is occurring,
+  // it could cause crashes.
   static void SetCachingEnabled(bool enable);
+
   static bool CachingEnabled() { return cache_enabled_; }
 
   static void CacheLock();
   static void CacheUnlock();
   static void CacheAdd(MapInfo* info);
   static bool CacheGet(MapInfo* info);
-  static bool CacheAfterCreateMemory(MapInfo* info);
+
+  static std::string GetPrintableBuildID(std::string& build_id);
 
  protected:
   bool valid_ = false;
@@ -124,10 +131,9 @@
   std::unique_ptr<ElfInterface> gnu_debugdata_interface_;
 
   static bool cache_enabled_;
-  static std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* cache_;
+  static std::unordered_map<std::string, std::unordered_map<uint64_t, std::shared_ptr<Elf>>>*
+      cache_;
   static std::mutex* cache_lock_;
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_ELF_H
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 23b7256..a192450 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_ELF_INTERFACE_H
-#define _LIBUNWINDSTACK_ELF_INTERFACE_H
+#pragma once
 
 #include <elf.h>
 #include <stdint.h>
@@ -225,5 +224,3 @@
 using ElfInterface64 = ElfInterfaceImpl<ElfTypes64>;
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_ELF_INTERFACE_H
diff --git a/libunwindstack/include/unwindstack/Error.h b/libunwindstack/include/unwindstack/Error.h
index 1610d55..3a3c646 100644
--- a/libunwindstack/include/unwindstack/Error.h
+++ b/libunwindstack/include/unwindstack/Error.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_ERROR_H
-#define _LIBUNWINDSTACK_ERROR_H
+#pragma once
 
 #include <stdint.h>
 
@@ -41,7 +40,10 @@
                                 // not exist.
   ERROR_THREAD_TIMEOUT,         // Timeout trying to unwind a local thread.
   ERROR_SYSTEM_CALL,            // System call failed while unwinding.
-  ERROR_MAX = ERROR_SYSTEM_CALL,
+  ERROR_BAD_ARCH,               // Arch invalid (none, or mismatched).
+  ERROR_MAPS_PARSE,             // Failed to parse maps data.
+  ERROR_INVALID_PARAMETER,      // Invalid parameter passed to function.
+  ERROR_MAX = ERROR_INVALID_PARAMETER,
 };
 
 static inline const char* GetErrorCodeString(ErrorCode error) {
@@ -68,6 +70,12 @@
       return "Thread Timeout";
     case ERROR_SYSTEM_CALL:
       return "System Call Failed";
+    case ERROR_BAD_ARCH:
+      return "Invalid arch detected";
+    case ERROR_MAPS_PARSE:
+      return "Failed to parse maps";
+    case ERROR_INVALID_PARAMETER:
+      return "Invalid parameter";
   }
 }
 
@@ -78,5 +86,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_ERROR_H
diff --git a/libunwindstack/include/unwindstack/Global.h b/libunwindstack/include/unwindstack/Global.h
index b790832..d12ecb1 100644
--- a/libunwindstack/include/unwindstack/Global.h
+++ b/libunwindstack/include/unwindstack/Global.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_GLOBAL_H
-#define _LIBUNWINDSTACK_GLOBAL_H
+#pragma once
 
 #include <stdint.h>
 
@@ -59,5 +58,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_GLOBAL_H
diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h
index cfa8f7e..8653f94 100644
--- a/libunwindstack/include/unwindstack/JitDebug.h
+++ b/libunwindstack/include/unwindstack/JitDebug.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_JIT_DEBUG_H
-#define _LIBUNWINDSTACK_JIT_DEBUG_H
+#pragma once
 
 #include <stdint.h>
 
@@ -36,5 +35,3 @@
                                          std::vector<std::string> search_libs = {});
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_JIT_DEBUG_H
diff --git a/libunwindstack/include/unwindstack/LocalUnwinder.h b/libunwindstack/include/unwindstack/LocalUnwinder.h
deleted file mode 100644
index b9123b3..0000000
--- a/libunwindstack/include/unwindstack/LocalUnwinder.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _LIBUNWINDSTACK_LOCAL_UNWINDER_H
-#define _LIBUNWINDSTACK_LOCAL_UNWINDER_H
-
-#include <pthread.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <unwindstack/Error.h>
-#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
-
-namespace unwindstack {
-
-// Forward declarations.
-class Elf;
-class MapInfo;
-
-struct LocalFrameData {
-  LocalFrameData(MapInfo* map_info, uint64_t pc, uint64_t rel_pc, const std::string& function_name,
-                 uint64_t function_offset)
-      : map_info(map_info),
-        pc(pc),
-        rel_pc(rel_pc),
-        function_name(function_name),
-        function_offset(function_offset) {}
-
-  MapInfo* map_info;
-  uint64_t pc;
-  uint64_t rel_pc;
-  std::string function_name;
-  uint64_t function_offset;
-};
-
-// This is a specialized class that should only be used for doing local unwinds.
-// The Unwind call can be made as multiple times on the same object, and it can
-// be called by multiple threads at the same time.
-// It is designed to be used in debugging circumstances to get a stack trace
-// as fast as possible.
-class LocalUnwinder {
- public:
-  LocalUnwinder() = default;
-  LocalUnwinder(const std::vector<std::string>& skip_libraries) : skip_libraries_(skip_libraries) {}
-  ~LocalUnwinder() = default;
-
-  bool Init();
-
-  bool Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames);
-
-  bool ShouldSkipLibrary(const std::string& map_name);
-
-  MapInfo* GetMapInfo(uint64_t pc);
-
-  ErrorCode LastErrorCode() { return last_error_.code; }
-  uint64_t LastErrorAddress() { return last_error_.address; }
-
- private:
-  pthread_rwlock_t maps_rwlock_;
-  std::unique_ptr<LocalUpdatableMaps> maps_ = nullptr;
-  std::shared_ptr<Memory> process_memory_;
-  std::vector<std::string> skip_libraries_;
-  ErrorData last_error_;
-};
-
-}  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_LOCAL_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/Log.h b/libunwindstack/include/unwindstack/Log.h
index a4f763d..34eb218 100644
--- a/libunwindstack/include/unwindstack/Log.h
+++ b/libunwindstack/include/unwindstack/Log.h
@@ -14,17 +14,24 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_LOG_H
-#define _LIBUNWINDSTACK_LOG_H
+#pragma once
 
+#include <stdarg.h>
 #include <stdint.h>
 
+#if !defined(__printflike)
+#define __printflike(x, y) __attribute__((__format__(printf, x, y)))
+#endif
+
 namespace unwindstack {
 
-void log_to_stdout(bool enable);
-void log(uint8_t indent, const char* format, ...);
-void log_async_safe(const char* format, ...);
+namespace Log {
+
+void Error(const char* format, ...) __printflike(1, 2);
+void Info(const char* format, ...) __printflike(1, 2);
+void Info(uint8_t indent, const char* format, ...) __printflike(2, 3);
+void AsyncSafe(const char* format, ...) __printflike(1, 2);
+
+}  // namespace Log
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_LOG_H
diff --git a/libunwindstack/include/unwindstack/MachineArm.h b/libunwindstack/include/unwindstack/MachineArm.h
index 3f902b1..6b8198e 100644
--- a/libunwindstack/include/unwindstack/MachineArm.h
+++ b/libunwindstack/include/unwindstack/MachineArm.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MACHINE_ARM_H
-#define _LIBUNWINDSTACK_MACHINE_ARM_H
+#pragma once
 
 #include <stdint.h>
 
@@ -46,5 +45,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MACHINE_ARM_H
diff --git a/libunwindstack/include/unwindstack/MachineArm64.h b/libunwindstack/include/unwindstack/MachineArm64.h
index 358e3d9..f1b7c1d 100644
--- a/libunwindstack/include/unwindstack/MachineArm64.h
+++ b/libunwindstack/include/unwindstack/MachineArm64.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MACHINE_ARM64_H
-#define _LIBUNWINDSTACK_MACHINE_ARM64_H
+#pragma once
 
 #include <stdint.h>
 
@@ -70,5 +69,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MACHINE_ARM64_H
diff --git a/libunwindstack/include/unwindstack/MachineMips.h b/libunwindstack/include/unwindstack/MachineMips.h
index 2dfb1e9..6a96536 100644
--- a/libunwindstack/include/unwindstack/MachineMips.h
+++ b/libunwindstack/include/unwindstack/MachineMips.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MACHINE_MIPS_H
-#define _LIBUNWINDSTACK_MACHINE_MIPS_H
+#pragma once
 
 #include <stdint.h>
 
@@ -62,5 +61,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MACHINE_MIPS_H
\ No newline at end of file
diff --git a/libunwindstack/include/unwindstack/MachineMips64.h b/libunwindstack/include/unwindstack/MachineMips64.h
index 34addf2..e148efc 100644
--- a/libunwindstack/include/unwindstack/MachineMips64.h
+++ b/libunwindstack/include/unwindstack/MachineMips64.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MACHINE_MIPS64_H
-#define _LIBUNWINDSTACK_MACHINE_MIPS64_H
+#pragma once
 
 #include <stdint.h>
 
@@ -62,5 +61,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MACHINE_MIPS64_H
\ No newline at end of file
diff --git a/libunwindstack/include/unwindstack/MachineX86.h b/libunwindstack/include/unwindstack/MachineX86.h
index 02adb98..ff4fd4b 100644
--- a/libunwindstack/include/unwindstack/MachineX86.h
+++ b/libunwindstack/include/unwindstack/MachineX86.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MACHINE_X86_H
-#define _LIBUNWINDSTACK_MACHINE_X86_H
+#pragma once
 
 #include <stdint.h>
 
@@ -47,5 +46,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MACHINE_X86_H
diff --git a/libunwindstack/include/unwindstack/MachineX86_64.h b/libunwindstack/include/unwindstack/MachineX86_64.h
index af33fea..66670e3 100644
--- a/libunwindstack/include/unwindstack/MachineX86_64.h
+++ b/libunwindstack/include/unwindstack/MachineX86_64.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MACHINE_X86_64_H
-#define _LIBUNWINDSTACK_MACHINE_X86_64_H
+#pragma once
 
 #include <stdint.h>
 
@@ -48,5 +47,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MACHINE_X86_64_H
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 727dc68..12711f5 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MAP_INFO_H
-#define _LIBUNWINDSTACK_MAP_INFO_H
+#pragma once
 
 #include <stdint.h>
 
@@ -38,7 +37,7 @@
 // (for example, 400 process * 400 maps * 128 bytes = 20 MB + string data).
 class MapInfo {
  public:
-  MapInfo(MapInfo* prev_map, MapInfo* prev_real_map, uint64_t start, uint64_t end, uint64_t offset,
+  MapInfo(std::shared_ptr<MapInfo>& prev_map, uint64_t start, uint64_t end, uint64_t offset,
           uint64_t flags, SharedString name)
       : start_(start),
         end_(end),
@@ -46,16 +45,35 @@
         flags_(flags),
         name_(name),
         elf_fields_(nullptr),
-        prev_map_(prev_map),
-        prev_real_map_(prev_real_map) {
-    if (prev_real_map != nullptr) prev_real_map->next_real_map_ = this;
+        prev_map_(prev_map) {}
+  MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, SharedString name)
+      : start_(start),
+        end_(end),
+        offset_(offset),
+        flags_(flags),
+        name_(name),
+        elf_fields_(nullptr) {}
+
+  static inline std::shared_ptr<MapInfo> Create(std::shared_ptr<MapInfo>& prev_map,
+                                                uint64_t start, uint64_t end, uint64_t offset,
+                                                uint64_t flags, SharedString name) {
+    auto map_info = std::make_shared<MapInfo>(prev_map, start, end, offset, flags, name);
+    if (prev_map) {
+      prev_map->next_map_ = map_info;
+    }
+    return map_info;
   }
+  static inline std::shared_ptr<MapInfo> Create(uint64_t start, uint64_t end, uint64_t offset,
+                                                uint64_t flags, SharedString name) {
+    return std::make_shared<MapInfo>(start, end, offset, flags, name);
+  }
+
   ~MapInfo();
 
   // Cached data for mapped ELF files.
   // We allocate this structure lazily since there are much fewer ELFs than maps.
   struct ElfFields {
-    ElfFields() : load_bias_(INT64_MAX), build_id_(0) {}
+    ElfFields() : load_bias_(UINT64_MAX), build_id_(0) {}
 
     std::shared_ptr<Elf> elf_;
     // The offset of the beginning of this mapping to the beginning of the
@@ -69,7 +87,7 @@
     // shared libraries into a read-only and read-execute map.
     uint64_t elf_start_offset_ = 0;
 
-    std::atomic_int64_t load_bias_;
+    std::atomic_uint64_t load_bias_;
 
     // This is a pointer to a new'd std::string.
     // Using an atomic value means that we don't need to lock and will
@@ -83,6 +101,38 @@
     std::mutex elf_mutex_;
   };
 
+  // True if the file named by this map is not actually readable and the
+  // elf is using the data in memory.
+  bool ElfFileNotReadable();
+
+  // This is the previous map with the same name that is not empty and with
+  // a 0 offset. For example, this set of maps:
+  //  1000-2000  r--p 000000 00:00 0 libc.so
+  //  2000-3000  ---p 000000 00:00 0
+  //  3000-4000  r-xp 003000 00:00 0 libc.so
+  // The last map's prev_map would point to the 2000-3000 map, while
+  // GetPrevRealMap() would point to the 1000-2000 map.
+  // NOTE: If a map is encountered that has a non-zero offset, or has a
+  //       a name different from the current map, then GetPrevRealMap()
+  //       returns nullptr.
+  std::shared_ptr<MapInfo> GetPrevRealMap();
+  // This is the next map with the same name that is not empty and with
+  // a 0 offset. For the example above, the first map's GetNextRealMap()
+  // would be the 3000-4000 map.
+  // NOTE: If a map is encountered that has a non-zero offset, or has a
+  //       a name different from the current map, then GetNextRealMap()
+  //       returns nullptr.
+  std::shared_ptr<MapInfo> GetNextRealMap();
+
+  // This is guaranteed to give out the Elf object associated with the
+  // object. The invariant is that once the Elf object is set under the
+  // lock in a MapInfo object it never changes and is not freed until
+  // the MapInfo object is destructed.
+  inline Elf* GetElfObj() {
+    std::lock_guard<std::mutex> guard(elf_mutex());
+    return elf().get();
+  }
+
   inline uint64_t start() const { return start_; }
   inline void set_start(uint64_t value) { start_ = value; }
 
@@ -109,8 +159,8 @@
   inline uint64_t elf_start_offset() { return GetElfFields().elf_start_offset_; }
   inline void set_elf_start_offset(uint64_t value) { GetElfFields().elf_start_offset_ = value; }
 
-  inline std::atomic_int64_t& load_bias() { return GetElfFields().load_bias_; }
-  inline void set_load_bias(int64_t value) { GetElfFields().load_bias_ = value; }
+  inline std::atomic_uint64_t& load_bias() { return GetElfFields().load_bias_; }
+  inline void set_load_bias(uint64_t value) { GetElfFields().load_bias_ = value; }
 
   inline std::atomic<SharedString*>& build_id() { return GetElfFields().build_id_; }
   inline void set_build_id(SharedString* value) { GetElfFields().build_id_ = value; }
@@ -118,20 +168,28 @@
   inline bool memory_backed_elf() { return GetElfFields().memory_backed_elf_; }
   inline void set_memory_backed_elf(bool value) { GetElfFields().memory_backed_elf_ = value; }
 
-  inline MapInfo* prev_map() const { return prev_map_; }
-  inline void set_prev_map(MapInfo* value) { prev_map_ = value; }
+  inline std::shared_ptr<MapInfo> prev_map() const { return prev_map_.lock(); }
+  inline void set_prev_map(std::shared_ptr<MapInfo>& value) { prev_map_ = value; }
 
-  inline MapInfo* prev_real_map() const { return prev_real_map_; }
-  inline void set_prev_real_map(MapInfo* value) { prev_real_map_ = value; }
-
-  inline MapInfo* next_real_map() const { return next_real_map_; }
-  inline void set_next_real_map(MapInfo* value) { next_real_map_ = value; }
+  inline std::shared_ptr<MapInfo> next_map() const { return next_map_.lock(); }
+  inline void set_next_map(std::shared_ptr<MapInfo>& value) { next_map_ = value; }
 
   // This function guarantees it will never return nullptr.
   Elf* GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch);
 
+  // Guaranteed to return the proper value if GetElf() has been called.
+  uint64_t GetLoadBias();
+
+  // Will get the proper value even if GetElf() hasn't been called.
   uint64_t GetLoadBias(const std::shared_ptr<Memory>& process_memory);
 
+  // This returns the name of the map plus the soname if this particular
+  // map represents an elf file that is contained inside of another file.
+  // The format of this soname embedded name is:
+  //   file.apk!libutils.so
+  // Otherwise, this function only returns the name of the map.
+  std::string GetFullName();
+
   Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
 
   bool GetFunctionName(uint64_t addr, SharedString* name, uint64_t* func_offset);
@@ -168,20 +226,8 @@
 
   std::atomic<ElfFields*> elf_fields_;
 
-  MapInfo* prev_map_ = nullptr;
-  // This is the previous map that is not empty with a 0 offset. For
-  // example, this set of maps:
-  //  1000-2000  r--p 000000 00:00 0 libc.so
-  //  2000-3000  ---p 000000 00:00 0 libc.so
-  //  3000-4000  r-xp 003000 00:00 0 libc.so
-  // The last map's prev_map would point to the 2000-3000 map, while the
-  // prev_real_map would point to the 1000-2000 map.
-  MapInfo* prev_real_map_ = nullptr;
-
-  // Same as above but set to point to the next map.
-  MapInfo* next_real_map_ = nullptr;
+  std::weak_ptr<MapInfo> prev_map_;
+  std::weak_ptr<MapInfo> next_map_;
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index be013a4..d9e1383 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MAPS_H
-#define _LIBUNWINDSTACK_MAPS_H
+#pragma once
 
 #include <pthread.h>
 #include <sys/types.h>
@@ -50,34 +49,35 @@
   Maps(Maps&&) = default;
   Maps& operator=(Maps&&) = default;
 
-  virtual MapInfo* Find(uint64_t pc);
+  virtual std::shared_ptr<MapInfo> Find(uint64_t pc);
 
   virtual bool Parse();
 
   virtual const std::string GetMapsFile() const { return ""; }
 
+  void Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name);
   void Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name,
            uint64_t load_bias);
 
   void Sort();
 
-  typedef std::vector<std::unique_ptr<MapInfo>>::iterator iterator;
+  typedef std::vector<std::shared_ptr<MapInfo>>::iterator iterator;
   iterator begin() { return maps_.begin(); }
   iterator end() { return maps_.end(); }
 
-  typedef std::vector<std::unique_ptr<MapInfo>>::const_iterator const_iterator;
+  typedef std::vector<std::shared_ptr<MapInfo>>::const_iterator const_iterator;
   const_iterator begin() const { return maps_.begin(); }
   const_iterator end() const { return maps_.end(); }
 
   size_t Total() { return maps_.size(); }
 
-  MapInfo* Get(size_t index) {
+  std::shared_ptr<MapInfo> Get(size_t index) {
     if (index >= maps_.size()) return nullptr;
-    return maps_[index].get();
+    return maps_[index];
   }
 
  protected:
-  std::vector<std::unique_ptr<MapInfo>> maps_;
+  std::vector<std::shared_ptr<MapInfo>> maps_;
 };
 
 class RemoteMaps : public Maps {
@@ -102,7 +102,7 @@
   LocalUpdatableMaps();
   virtual ~LocalUpdatableMaps() = default;
 
-  MapInfo* Find(uint64_t pc) override;
+  std::shared_ptr<MapInfo> Find(uint64_t pc) override;
 
   bool Parse() override;
 
@@ -110,9 +110,7 @@
 
   bool Reparse(/*out*/ bool* any_changed = nullptr);
 
- protected:
-  std::vector<std::unique_ptr<MapInfo>> saved_maps_;
-
+ private:
   pthread_rwlock_t maps_rwlock_;
 };
 
@@ -139,5 +137,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MAPS_H
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index b7bede6..d6ca29e 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_MEMORY_H
-#define _LIBUNWINDSTACK_MEMORY_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -65,5 +64,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 45e96df..e49370d 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_REGS_H
-#define _LIBUNWINDSTACK_REGS_H
+#pragma once
 
 #include <stdint.h>
 #include <unistd.h>
@@ -82,6 +81,7 @@
   virtual Regs* Clone() = 0;
 
   static ArchEnum CurrentArch();
+  static ArchEnum RemoteGetArch(pid_t pid);
   static Regs* RemoteGet(pid_t pid);
   static Regs* CreateFromUcontext(ArchEnum arch, void* ucontext);
   static Regs* CreateFromLocal();
@@ -116,5 +116,3 @@
 uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf, ArchEnum arch);
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_REGS_H
diff --git a/libunwindstack/include/unwindstack/RegsArm.h b/libunwindstack/include/unwindstack/RegsArm.h
index fbb34e7..5596605 100644
--- a/libunwindstack/include/unwindstack/RegsArm.h
+++ b/libunwindstack/include/unwindstack/RegsArm.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_REGS_ARM_H
-#define _LIBUNWINDSTACK_REGS_ARM_H
+#pragma once
 
 #include <stdint.h>
 
@@ -56,5 +55,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_REGS_ARM_H
diff --git a/libunwindstack/include/unwindstack/RegsArm64.h b/libunwindstack/include/unwindstack/RegsArm64.h
index 508366e..d12a043 100644
--- a/libunwindstack/include/unwindstack/RegsArm64.h
+++ b/libunwindstack/include/unwindstack/RegsArm64.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_REGS_ARM64_H
-#define _LIBUNWINDSTACK_REGS_ARM64_H
+#pragma once
 
 #include <stdint.h>
 
@@ -73,5 +72,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_REGS_ARM64_H
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
index ac171b9..bf3f767 100644
--- a/libunwindstack/include/unwindstack/RegsGetLocal.h
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_REGS_GET_LOCAL_H
-#define _LIBUNWINDSTACK_REGS_GET_LOCAL_H
+#pragma once
 
 namespace unwindstack {
 
@@ -91,7 +90,4 @@
   AsmGetRegs(regs->RawData());
 }
 
-
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_REGS_GET_LOCAL_H
diff --git a/libunwindstack/include/unwindstack/RegsMips.h b/libunwindstack/include/unwindstack/RegsMips.h
index dc09b83..61e4dbf 100644
--- a/libunwindstack/include/unwindstack/RegsMips.h
+++ b/libunwindstack/include/unwindstack/RegsMips.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_REGS_MIPS_H
-#define _LIBUNWINDSTACK_REGS_MIPS_H
+#pragma once
 
 #include <stdint.h>
 
@@ -56,5 +55,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_REGS_MIPS_H
diff --git a/libunwindstack/include/unwindstack/RegsMips64.h b/libunwindstack/include/unwindstack/RegsMips64.h
index 64a80dc..012a8f0 100644
--- a/libunwindstack/include/unwindstack/RegsMips64.h
+++ b/libunwindstack/include/unwindstack/RegsMips64.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_REGS_MIPS64_H
-#define _LIBUNWINDSTACK_REGS_MIPS64_H
+#pragma once
 
 #include <stdint.h>
 
@@ -56,5 +55,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_REGS_MIPS64_H
diff --git a/libunwindstack/include/unwindstack/RegsX86.h b/libunwindstack/include/unwindstack/RegsX86.h
index cfbdda6..d8245ee 100644
--- a/libunwindstack/include/unwindstack/RegsX86.h
+++ b/libunwindstack/include/unwindstack/RegsX86.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_REGS_X86_H
-#define _LIBUNWINDSTACK_REGS_X86_H
+#pragma once
 
 #include <stdint.h>
 
@@ -59,5 +58,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_REGS_X86_H
diff --git a/libunwindstack/include/unwindstack/RegsX86_64.h b/libunwindstack/include/unwindstack/RegsX86_64.h
index a11aef0..90fee93 100644
--- a/libunwindstack/include/unwindstack/RegsX86_64.h
+++ b/libunwindstack/include/unwindstack/RegsX86_64.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_REGS_X86_64_H
-#define _LIBUNWINDSTACK_REGS_X86_64_H
+#pragma once
 
 #include <stdint.h>
 
@@ -59,5 +58,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_REGS_X86_64_H
diff --git a/libunwindstack/include/unwindstack/SharedString.h b/libunwindstack/include/unwindstack/SharedString.h
index a82c4fe..1eb4e21 100644
--- a/libunwindstack/include/unwindstack/SharedString.h
+++ b/libunwindstack/include/unwindstack/SharedString.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_SHAREDSTRING_H
-#define _LIBUNWINDSTACK_SHAREDSTRING_H
+#pragma once
 
 #include <memory>
 #include <string>
@@ -73,4 +72,3 @@
 }
 
 }  // namespace unwindstack
-#endif  // _LIBUNWINDSTACK_SHAREDSTRING_H
diff --git a/libunwindstack/include/unwindstack/UcontextArm.h b/libunwindstack/include/unwindstack/UcontextArm.h
index 7d1ec3b..0d7f90d 100644
--- a/libunwindstack/include/unwindstack/UcontextArm.h
+++ b/libunwindstack/include/unwindstack/UcontextArm.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_UCONTEXT_ARM_H
-#define _LIBUNWINDSTACK_UCONTEXT_ARM_H
+#pragma once
 
 #include <stdint.h>
 
@@ -59,5 +58,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_UCONTEXT_ARM_H
diff --git a/libunwindstack/include/unwindstack/UcontextArm64.h b/libunwindstack/include/unwindstack/UcontextArm64.h
index a68be3b..49278b3 100644
--- a/libunwindstack/include/unwindstack/UcontextArm64.h
+++ b/libunwindstack/include/unwindstack/UcontextArm64.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_UCONTEXT_ARM64_H
-#define _LIBUNWINDSTACK_UCONTEXT_ARM64_H
+#pragma once
 
 #include <stdint.h>
 
@@ -65,5 +64,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_UCONTEXT_ARM64_H
diff --git a/libunwindstack/include/unwindstack/UcontextMips.h b/libunwindstack/include/unwindstack/UcontextMips.h
index 02e33b6..c75e469 100644
--- a/libunwindstack/include/unwindstack/UcontextMips.h
+++ b/libunwindstack/include/unwindstack/UcontextMips.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_UCONTEXT_MIPS_H
-#define _LIBUNWINDSTACK_UCONTEXT_MIPS_H
+#pragma once
 
 #include <stdint.h>
 
@@ -58,5 +57,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_UCONTEXT_MIPS_H
diff --git a/libunwindstack/include/unwindstack/UcontextMips64.h b/libunwindstack/include/unwindstack/UcontextMips64.h
index 5b92a55..c63218f 100644
--- a/libunwindstack/include/unwindstack/UcontextMips64.h
+++ b/libunwindstack/include/unwindstack/UcontextMips64.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
-#define _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
+#pragma once
 
 #include <stdint.h>
 
@@ -65,5 +64,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
diff --git a/libunwindstack/include/unwindstack/UcontextX86.h b/libunwindstack/include/unwindstack/UcontextX86.h
index c96ebb7..9dfdf2a 100644
--- a/libunwindstack/include/unwindstack/UcontextX86.h
+++ b/libunwindstack/include/unwindstack/UcontextX86.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_UCONTEXT_X86_H
-#define _LIBUNWINDSTACK_UCONTEXT_X86_H
+#pragma once
 
 #include <stdint.h>
 
@@ -73,5 +72,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_UCONTEXT_X86_H
diff --git a/libunwindstack/include/unwindstack/UcontextX86_64.h b/libunwindstack/include/unwindstack/UcontextX86_64.h
index 4e163e5..2801217 100644
--- a/libunwindstack/include/unwindstack/UcontextX86_64.h
+++ b/libunwindstack/include/unwindstack/UcontextX86_64.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_UCONTEXT_X86_64_H
-#define _LIBUNWINDSTACK_UCONTEXT_X86_64_H
+#pragma once
 
 #include <stdint.h>
 
@@ -78,5 +77,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_UCONTEXT_X86_64_H
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index bcd0d42..d78da5c 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_UNWINDER_H
-#define _LIBUNWINDSTACK_UNWINDER_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -49,18 +48,7 @@
   SharedString function_name;
   uint64_t function_offset = 0;
 
-  SharedString map_name;
-  // The offset from the first map representing the frame. When there are
-  // two maps (read-only and read-execute) this will be the offset from
-  // the read-only map. When there is only one map, this will be the
-  // same as the actual offset of the map and match map_exact_offset.
-  uint64_t map_elf_start_offset = 0;
-  // The actual offset from the map where the pc lies.
-  uint64_t map_exact_offset = 0;
-  uint64_t map_start = 0;
-  uint64_t map_end = 0;
-  uint64_t map_load_bias = 0;
-  int map_flags = 0;
+  std::shared_ptr<MapInfo> map_info;
 };
 
 class Unwinder {
@@ -94,6 +82,9 @@
   std::string FormatFrame(size_t frame_num) const;
   std::string FormatFrame(const FrameData& frame) const;
 
+  static std::string FormatFrame(ArchEnum arch, const FrameData& frame,
+                                 bool display_build_id = true);
+
   void SetArch(ArchEnum arch) { arch_ = arch; };
 
   void SetJitDebug(JitDebug* jit_debug);
@@ -109,17 +100,11 @@
   // set to an empty string and the function offset being set to zero.
   void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
 
-  // Enable/disable soname printing the soname for a map name if the elf is
-  // embedded in a file. This is enabled by default.
-  // NOTE: This does nothing unless resolving names is enabled.
-  void SetEmbeddedSoname(bool embedded_soname) { embedded_soname_ = embedded_soname; }
-
   void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; }
 
   void SetDexFiles(DexFiles* dex_files);
 
-  bool elf_from_memory_not_file() { return elf_from_memory_not_file_; }
-
+  const ErrorData& LastError() { return last_error_; }
   ErrorCode LastErrorCode() { return last_error_.code; }
   const char* LastErrorCodeString() { return GetErrorCodeString(last_error_.code); }
   uint64_t LastErrorAddress() { return last_error_.address; }
@@ -138,6 +123,8 @@
   Unwinder(size_t max_frames, Maps* maps = nullptr) : max_frames_(max_frames), maps_(maps) {}
   Unwinder(size_t max_frames, ArchEnum arch, Maps* maps = nullptr)
       : max_frames_(max_frames), maps_(maps), arch_(arch) {}
+  Unwinder(size_t max_frames, ArchEnum arch, Maps* maps, std::shared_ptr<Memory>& process_memory)
+      : max_frames_(max_frames), maps_(maps), process_memory_(process_memory), arch_(arch) {}
 
   void ClearErrors() {
     warnings_ = WARNING_NONE;
@@ -146,21 +133,18 @@
   }
 
   void FillInDexFrame();
-  FrameData* FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t pc_adjustment);
+  FrameData* FillInFrame(std::shared_ptr<MapInfo>& map_info, Elf* elf, uint64_t rel_pc,
+                         uint64_t pc_adjustment);
 
   size_t max_frames_;
-  Maps* maps_;
+  Maps* maps_ = nullptr;
   Regs* regs_;
   std::vector<FrameData> frames_;
   std::shared_ptr<Memory> process_memory_;
   JitDebug* jit_debug_ = nullptr;
   DexFiles* dex_files_ = nullptr;
   bool resolve_names_ = true;
-  bool embedded_soname_ = true;
   bool display_build_id_ = false;
-  // True if at least one elf file is coming from memory and not the related
-  // file. This is only true if there is an actual file backing up the elf.
-  bool elf_from_memory_not_file_ = false;
   ErrorData last_error_;
   uint64_t warnings_;
   ArchEnum arch_ = ARCH_UNKNOWN;
@@ -170,14 +154,15 @@
  public:
   UnwinderFromPid(size_t max_frames, pid_t pid, Maps* maps = nullptr)
       : Unwinder(max_frames, maps), pid_(pid) {}
+  UnwinderFromPid(size_t max_frames, pid_t pid, std::shared_ptr<Memory>& process_memory)
+      : Unwinder(max_frames, nullptr, process_memory), pid_(pid) {}
   UnwinderFromPid(size_t max_frames, pid_t pid, ArchEnum arch, Maps* maps = nullptr)
       : Unwinder(max_frames, arch, maps), pid_(pid) {}
+  UnwinderFromPid(size_t max_frames, pid_t pid, ArchEnum arch, Maps* maps,
+                  std::shared_ptr<Memory>& process_memory)
+      : Unwinder(max_frames, arch, maps, process_memory), pid_(pid) {}
   virtual ~UnwinderFromPid() = default;
 
-  void SetProcessMemory(std::shared_ptr<Memory>& process_memory) {
-    process_memory_ = process_memory;
-  }
-
   bool Init();
 
   void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
@@ -194,6 +179,7 @@
 class ThreadUnwinder : public UnwinderFromPid {
  public:
   ThreadUnwinder(size_t max_frames, Maps* maps = nullptr);
+  ThreadUnwinder(size_t max_frames, Maps* maps, std::shared_ptr<Memory>& process_memory);
   ThreadUnwinder(size_t max_frames, const ThreadUnwinder* unwinder);
   virtual ~ThreadUnwinder() = default;
 
@@ -201,7 +187,7 @@
 
   void Unwind(const std::vector<std::string>*, const std::vector<std::string>*) override {}
 
-  void UnwindWithSignal(int signal, pid_t tid,
+  void UnwindWithSignal(int signal, pid_t tid, std::unique_ptr<Regs>* initial_regs = nullptr,
                         const std::vector<std::string>* initial_map_names_to_skip = nullptr,
                         const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
 
@@ -210,5 +196,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/UserArm.h b/libunwindstack/include/unwindstack/UserArm.h
index 7388c03..725a35b 100644
--- a/libunwindstack/include/unwindstack/UserArm.h
+++ b/libunwindstack/include/unwindstack/UserArm.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_USER_ARM_H
-#define _LIBUNWINDSTACK_USER_ARM_H
+#pragma once
 
 namespace unwindstack {
 
@@ -36,5 +35,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_USER_ARM_H
diff --git a/libunwindstack/include/unwindstack/UserArm64.h b/libunwindstack/include/unwindstack/UserArm64.h
index d74983f..0e16cd6 100644
--- a/libunwindstack/include/unwindstack/UserArm64.h
+++ b/libunwindstack/include/unwindstack/UserArm64.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_USER_ARM64_H
-#define _LIBUNWINDSTACK_USER_ARM64_H
+#pragma once
 
 namespace unwindstack {
 
@@ -39,5 +38,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_USER_ARM64_H
diff --git a/libunwindstack/include/unwindstack/UserMips.h b/libunwindstack/include/unwindstack/UserMips.h
index 184be4f..148ef93 100644
--- a/libunwindstack/include/unwindstack/UserMips.h
+++ b/libunwindstack/include/unwindstack/UserMips.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_USER_MIPS_H
-#define _LIBUNWINDSTACK_USER_MIPS_H
+#pragma once
 
 namespace unwindstack {
 
@@ -41,5 +40,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_USER_MIPS_H
diff --git a/libunwindstack/include/unwindstack/UserMips64.h b/libunwindstack/include/unwindstack/UserMips64.h
index c46befd..9844307 100644
--- a/libunwindstack/include/unwindstack/UserMips64.h
+++ b/libunwindstack/include/unwindstack/UserMips64.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_USER_MIPS64_H
-#define _LIBUNWINDSTACK_USER_MIPS64_H
+#pragma once
 
 namespace unwindstack {
 
@@ -41,5 +40,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_USER_MIPS64_H
diff --git a/libunwindstack/include/unwindstack/UserX86.h b/libunwindstack/include/unwindstack/UserX86.h
index a040560..9508010 100644
--- a/libunwindstack/include/unwindstack/UserX86.h
+++ b/libunwindstack/include/unwindstack/UserX86.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_USER_X86_H
-#define _LIBUNWINDSTACK_USER_X86_H
+#pragma once
 
 namespace unwindstack {
 
@@ -52,5 +51,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_USER_X86_H
diff --git a/libunwindstack/include/unwindstack/UserX86_64.h b/libunwindstack/include/unwindstack/UserX86_64.h
index b80d201..d7ff2e2 100644
--- a/libunwindstack/include/unwindstack/UserX86_64.h
+++ b/libunwindstack/include/unwindstack/UserX86_64.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_USER_X86_64_H
-#define _LIBUNWINDSTACK_USER_X86_64_H
+#pragma once
 
 namespace unwindstack {
 
@@ -62,5 +61,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_USER_X86_64_H
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex b/libunwindstack/offline_files/apk_rorx_arm64/fake.apk
similarity index 66%
copy from libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
copy to libunwindstack/offline_files/apk_rorx_arm64/fake.apk
index 35a6bc5..9bd1f85 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
+++ b/libunwindstack/offline_files/apk_rorx_arm64/fake.apk
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rorx_arm64/links.txt b/libunwindstack/offline_files/apk_rorx_arm64/links.txt
new file mode 100644
index 0000000..42da529
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_arm64/links.txt
@@ -0,0 +1 @@
+../common/libc.so_3106fb6eab1b2dc6704157e908a50eba libc.so
diff --git a/libunwindstack/offline_files/apk_rorx_arm64/maps.txt b/libunwindstack/offline_files/apk_rorx_arm64/maps.txt
new file mode 100644
index 0000000..863c13e
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_arm64/maps.txt
@@ -0,0 +1,6 @@
+603b0c4000-603b0c5000 r--p 0 00:00 0   run
+603b0c5000-603b0c6000 r-xp 1000 00:00 0   run
+7426d2d000-7426d2e000 r--p 4000 00:00 0   fake.apk
+7426d2e000-7426d2f000 r-xp 5000 00:00 0   fake.apk
+76b6da8000-76b6de4000 r--p 0 00:00 0   libc.so
+76b6de4000-76b6e65000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/apk_rorx_arm64/output.txt b/libunwindstack/offline_files/apk_rorx_arm64/output.txt
new file mode 100644
index 0000000..0636f7f
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_arm64/output.txt
@@ -0,0 +1,6 @@
+  #00 pc 0000000000001030  fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel4+28)
+  #01 pc 000000000000108c  fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel3+28)
+  #02 pc 00000000000010b8  fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel2+28)
+  #03 pc 00000000000010e4  fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel1+28)
+  #04 pc 0000000000001154  run (main+256)
+  #05 pc 0000000000048b10  libc.so (__libc_init+96)
diff --git a/libunwindstack/offline_files/apk_rorx_arm64/regs.txt b/libunwindstack/offline_files/apk_rorx_arm64/regs.txt
new file mode 100644
index 0000000..da2ceb5
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 0
+x1: 0
+x2: 76bc49ee88
+x3: 11b030000
+x4: 800000
+x5: 5462727542000000
+x6: 804275726254
+x7: 5678727440ff306b
+x8: 0
+x9: e41
+x10: e41
+x11: 10
+x12: 76bc385f80
+x13: f66dca96235a549b
+x14: a83626c39758e9a
+x15: fabaf3feaa5334a
+x16: 7426d2f1e0
+x17: 7426d2e014
+x18: 76bb4bc000
+x19: 7fe740cd61
+x20: 3
+x21: 7fe740ce28
+x22: 76bb091000
+x23: 0
+x24: 0
+x25: 0
+x26: 0
+x27: 0
+x28: 0
+x29: 7fe740ccc0
+lr: 7426d2e090
+sp: 7fe740cc90
+pc: 7426d2e030
+pst: 80001000
diff --git a/libunwindstack/offline_files/apk_rorx_arm64/run b/libunwindstack/offline_files/apk_rorx_arm64/run
new file mode 100644
index 0000000..b984d44
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_arm64/run
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rorx_arm64/stack.data b/libunwindstack/offline_files/apk_rorx_arm64/stack.data
new file mode 100644
index 0000000..5a609c7
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/fake.apk
similarity index 66%
copy from libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
copy to libunwindstack/offline_files/apk_rorx_unreadable_arm64/fake.apk
index 35a6bc5..9bd1f85 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
+++ b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/fake.apk
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rorx_unreadable_arm64/links.txt b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/links.txt
new file mode 100644
index 0000000..42da529
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/links.txt
@@ -0,0 +1 @@
+../common/libc.so_3106fb6eab1b2dc6704157e908a50eba libc.so
diff --git a/libunwindstack/offline_files/apk_rorx_unreadable_arm64/maps.txt b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/maps.txt
new file mode 100644
index 0000000..d0fd187
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/maps.txt
@@ -0,0 +1,6 @@
+603b0c4000-603b0c5000 r--p 0 00:00 0   run
+603b0c5000-603b0c6000 r-xp 1000 00:00 0   run
+7426d2d000-7426d2e000 r--p 4000 00:00 0   unreadable.apk
+7426d2e000-7426d2f000 r-xp 5000 00:00 0   unreadable.apk
+76b6da8000-76b6de4000 r--p 0 00:00 0   libc.so
+76b6de4000-76b6e65000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/apk_rorx_unreadable_arm64/output.txt b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/output.txt
new file mode 100644
index 0000000..dddf4da
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/output.txt
@@ -0,0 +1,6 @@
+  #00 pc 0000000000001030  unreadable.apk (offset 0x4000)
+  #01 pc 000000000000108c  unreadable.apk (offset 0x4000)
+  #02 pc 00000000000010b8  unreadable.apk (offset 0x4000)
+  #03 pc 00000000000010e4  unreadable.apk (offset 0x4000)
+  #04 pc 0000000000001154  run (main+256)
+  #05 pc 0000000000048b10  libc.so (__libc_init+96)
diff --git a/libunwindstack/offline_files/apk_rorx_unreadable_arm64/regs.txt b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/regs.txt
new file mode 100644
index 0000000..da2ceb5
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 0
+x1: 0
+x2: 76bc49ee88
+x3: 11b030000
+x4: 800000
+x5: 5462727542000000
+x6: 804275726254
+x7: 5678727440ff306b
+x8: 0
+x9: e41
+x10: e41
+x11: 10
+x12: 76bc385f80
+x13: f66dca96235a549b
+x14: a83626c39758e9a
+x15: fabaf3feaa5334a
+x16: 7426d2f1e0
+x17: 7426d2e014
+x18: 76bb4bc000
+x19: 7fe740cd61
+x20: 3
+x21: 7fe740ce28
+x22: 76bb091000
+x23: 0
+x24: 0
+x25: 0
+x26: 0
+x27: 0
+x28: 0
+x29: 7fe740ccc0
+lr: 7426d2e090
+sp: 7fe740cc90
+pc: 7426d2e030
+pst: 80001000
diff --git a/libunwindstack/offline_files/apk_rorx_unreadable_arm64/run b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/run
new file mode 100644
index 0000000..b984d44
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/run
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rorx_unreadable_arm64/stack.data b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/stack.data
new file mode 100644
index 0000000..5a609c7
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex b/libunwindstack/offline_files/apk_rx_arm64/fake.apk
similarity index 78%
rename from libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
rename to libunwindstack/offline_files/apk_rx_arm64/fake.apk
index 35a6bc5..1f45187 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
+++ b/libunwindstack/offline_files/apk_rx_arm64/fake.apk
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rx_arm64/links.txt b/libunwindstack/offline_files/apk_rx_arm64/links.txt
new file mode 100644
index 0000000..42da529
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_arm64/links.txt
@@ -0,0 +1 @@
+../common/libc.so_3106fb6eab1b2dc6704157e908a50eba libc.so
diff --git a/libunwindstack/offline_files/apk_rx_arm64/maps.txt b/libunwindstack/offline_files/apk_rx_arm64/maps.txt
new file mode 100644
index 0000000..7e23cb0
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_arm64/maps.txt
@@ -0,0 +1,5 @@
+5e004ef000-5e004f0000 r--p 0 00:00 0   run
+5e004f0000-5e004f1000 r-xp 1000 00:00 0   run
+7cb0e62000-7cb0e63000 r-xp 4000 00:00 0   fake.apk
+7f410dc000-7f41118000 r--p 0 00:00 0   libc.so
+7f41118000-7f41199000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/apk_rx_arm64/output.txt b/libunwindstack/offline_files/apk_rx_arm64/output.txt
new file mode 100644
index 0000000..c033076
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_arm64/output.txt
@@ -0,0 +1,6 @@
+  #00 pc 000000000000066c  fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel4+36)
+  #01 pc 00000000000006c0  fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel3+28)
+  #02 pc 00000000000006ec  fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel2+28)
+  #03 pc 0000000000000718  fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel1+28)
+  #04 pc 0000000000001154  run (main+256)
+  #05 pc 0000000000048b10  libc.so (__libc_init+96)
diff --git a/libunwindstack/offline_files/apk_rx_arm64/regs.txt b/libunwindstack/offline_files/apk_rx_arm64/regs.txt
new file mode 100644
index 0000000..1f6e4c6
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 0
+x1: 0
+x2: 7f42f67e88
+x3: 11b030000
+x4: 800000
+x5: 5462727542000000
+x6: 804275726254
+x7: 5678727440ff306b
+x8: 0
+x9: 1014
+x10: 1014
+x11: 10
+x12: 7f42e4ef80
+x13: 2f75fd4dd1b37121
+x14: 7
+x15: fabaf3feaa5334a
+x16: 7cb0e631e0
+x17: 7cb0e62648
+x18: 7f426ea000
+x19: 7fe563bf61
+x20: 3
+x21: 7fe563c028
+x22: 7f41b5a000
+x23: 0
+x24: 0
+x25: 0
+x26: 0
+x27: 0
+x28: 0
+x29: 7fe563bec0
+lr: 7cb0e626c4
+sp: 7fe563be90
+pc: 7cb0e6266c
+pst: 80001000
diff --git a/libunwindstack/offline_files/apk_rx_arm64/run b/libunwindstack/offline_files/apk_rx_arm64/run
new file mode 100644
index 0000000..b984d44
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_arm64/run
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rx_arm64/stack.data b/libunwindstack/offline_files/apk_rx_arm64/stack.data
new file mode 100644
index 0000000..5ce83d4
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex b/libunwindstack/offline_files/apk_rx_unreadable_arm64/fake.apk
similarity index 78%
copy from libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
copy to libunwindstack/offline_files/apk_rx_unreadable_arm64/fake.apk
index 35a6bc5..1f45187 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
+++ b/libunwindstack/offline_files/apk_rx_unreadable_arm64/fake.apk
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rx_unreadable_arm64/links.txt b/libunwindstack/offline_files/apk_rx_unreadable_arm64/links.txt
new file mode 100644
index 0000000..42da529
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_unreadable_arm64/links.txt
@@ -0,0 +1 @@
+../common/libc.so_3106fb6eab1b2dc6704157e908a50eba libc.so
diff --git a/libunwindstack/offline_files/apk_rx_unreadable_arm64/maps.txt b/libunwindstack/offline_files/apk_rx_unreadable_arm64/maps.txt
new file mode 100644
index 0000000..696c397
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_unreadable_arm64/maps.txt
@@ -0,0 +1,5 @@
+5e004ef000-5e004f0000 r--p 0 00:00 0   run
+5e004f0000-5e004f1000 r-xp 1000 00:00 0   run
+7cb0e62000-7cb0e63000 r-xp 4000 00:00 0   unreadable.apk
+7f410dc000-7f41118000 r--p 0 00:00 0   libc.so
+7f41118000-7f41199000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/apk_rx_unreadable_arm64/output.txt b/libunwindstack/offline_files/apk_rx_unreadable_arm64/output.txt
new file mode 100644
index 0000000..c984b58
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_unreadable_arm64/output.txt
@@ -0,0 +1,6 @@
+  #00 pc 000000000000066c  unreadable.apk (offset 0x4000)
+  #01 pc 00000000000006c0  unreadable.apk (offset 0x4000)
+  #02 pc 00000000000006ec  unreadable.apk (offset 0x4000)
+  #03 pc 0000000000000718  unreadable.apk (offset 0x4000)
+  #04 pc 0000000000001154  run (main+256)
+  #05 pc 0000000000048b10  libc.so (__libc_init+96)
diff --git a/libunwindstack/offline_files/apk_rx_unreadable_arm64/regs.txt b/libunwindstack/offline_files/apk_rx_unreadable_arm64/regs.txt
new file mode 100644
index 0000000..1f6e4c6
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_unreadable_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 0
+x1: 0
+x2: 7f42f67e88
+x3: 11b030000
+x4: 800000
+x5: 5462727542000000
+x6: 804275726254
+x7: 5678727440ff306b
+x8: 0
+x9: 1014
+x10: 1014
+x11: 10
+x12: 7f42e4ef80
+x13: 2f75fd4dd1b37121
+x14: 7
+x15: fabaf3feaa5334a
+x16: 7cb0e631e0
+x17: 7cb0e62648
+x18: 7f426ea000
+x19: 7fe563bf61
+x20: 3
+x21: 7fe563c028
+x22: 7f41b5a000
+x23: 0
+x24: 0
+x25: 0
+x26: 0
+x27: 0
+x28: 0
+x29: 7fe563bec0
+lr: 7cb0e626c4
+sp: 7fe563be90
+pc: 7cb0e6266c
+pst: 80001000
diff --git a/libunwindstack/offline_files/apk_rx_unreadable_arm64/run b/libunwindstack/offline_files/apk_rx_unreadable_arm64/run
new file mode 100644
index 0000000..b984d44
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_unreadable_arm64/run
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rx_unreadable_arm64/stack.data b/libunwindstack/offline_files/apk_rx_unreadable_arm64/stack.data
new file mode 100644
index 0000000..5ce83d4
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_unreadable_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/descriptor.data b/libunwindstack/offline_files/art_quick_osr_stub_arm/descriptor.data
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/descriptor.data
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry0.data b/libunwindstack/offline_files/art_quick_osr_stub_arm/entry0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry0.data
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry1.data b/libunwindstack/offline_files/art_quick_osr_stub_arm/entry1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry1.data
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit0.data b/libunwindstack/offline_files/art_quick_osr_stub_arm/jit0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit0.data
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit1.data b/libunwindstack/offline_files/art_quick_osr_stub_arm/jit1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit1.data
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/jit1.data
Binary files differ
diff --git a/libunwindstack/offline_files/art_quick_osr_stub_arm/libart.so.gz b/libunwindstack/offline_files/art_quick_osr_stub_arm/libart.so.gz
new file mode 100644
index 0000000..f0d1e92
--- /dev/null
+++ b/libunwindstack/offline_files/art_quick_osr_stub_arm/libart.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/art_quick_osr_stub_arm/libc.so.gz b/libunwindstack/offline_files/art_quick_osr_stub_arm/libc.so.gz
new file mode 100644
index 0000000..a7e4170
--- /dev/null
+++ b/libunwindstack/offline_files/art_quick_osr_stub_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt b/libunwindstack/offline_files/art_quick_osr_stub_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/maps.txt
diff --git a/libunwindstack/offline_files/art_quick_osr_stub_arm/output.txt b/libunwindstack/offline_files/art_quick_osr_stub_arm/output.txt
new file mode 100644
index 0000000..76b288f
--- /dev/null
+++ b/libunwindstack/offline_files/art_quick_osr_stub_arm/output.txt
@@ -0,0 +1,25 @@
+  #00 pc 0000c788  <anonymous:d0250000> (com.example.simpleperf.simpleperfexamplewithnative.MixActivity.access$000)
+  #01 pc 0000cdd5  <anonymous:d0250000> (com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)
+  #02 pc 004135bb  libart.so (art_quick_osr_stub+42)
+  #03 pc 002657a5  libart.so (art::jit::Jit::MaybeDoOnStackReplacement(art::Thread*, art::ArtMethod*, unsigned int, int, art::JValue*)+876)
+  #04 pc 004021a7  libart.so (MterpMaybeDoOnStackReplacement+86)
+  #05 pc 00412474  libart.so (ExecuteMterpImpl+66164)
+  #06 pc cd8365b0  <unknown>
+  #07 pc 001d7f1b  libart.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+374)
+  #08 pc 001dc593  libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+154)
+  #09 pc 001f4d01  libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+732)
+  #10 pc 003fe427  libart.so (MterpInvokeInterface+1354)
+  #11 pc 00405b94  libart.so (ExecuteMterpImpl+14740)
+  #12 pc 7004873e  <unknown>
+  #13 pc 001d7f1b  libart.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+374)
+  #14 pc 001dc4d5  libart.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+92)
+  #15 pc 003f25ab  libart.so (artQuickToInterpreterBridge+970)
+  #16 pc 00417aff  libart.so (art_quick_to_interpreter_bridge+30)
+  #17 pc 00413575  libart.so (art_quick_invoke_stub_internal+68)
+  #18 pc 00418531  libart.so (art_quick_invoke_stub+236)
+  #19 pc 000b468d  libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+136)
+  #20 pc 00362f49  libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+52)
+  #21 pc 00363cd9  libart.so (art::InvokeVirtualOrInterfaceWithJValues(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, jvalue*)+332)
+  #22 pc 003851dd  libart.so (art::Thread::CreateCallback(void*)+868)
+  #23 pc 00062925  libc.so (__pthread_start(void*)+22)
+  #24 pc 0001de39  libc.so (__start_thread+24)
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/regs.txt b/libunwindstack/offline_files/art_quick_osr_stub_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/regs.txt
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/stack.data b/libunwindstack/offline_files/art_quick_osr_stub_arm/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/stack.data
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/libc.so.gz b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/libc.so.gz
new file mode 100644
index 0000000..d9c3705
--- /dev/null
+++ b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/maps.txt b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/maps.txt
rename to libunwindstack/offline_files/bad_eh_frame_hdr_arm64/maps.txt
diff --git a/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/output.txt b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/output.txt
new file mode 100644
index 0000000..cdec0e4
--- /dev/null
+++ b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/output.txt
@@ -0,0 +1,5 @@
+  #00 pc 0000000000000550  waiter64
+  #01 pc 0000000000000568  waiter64
+  #02 pc 000000000000057c  waiter64
+  #03 pc 0000000000000590  waiter64
+  #04 pc 00000000000a8e98  libc.so (__libc_init+88)
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/regs.txt b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/regs.txt
rename to libunwindstack/offline_files/bad_eh_frame_hdr_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/stack.data b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/stack.data
rename to libunwindstack/offline_files/bad_eh_frame_hdr_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/waiter64.gz b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/waiter64.gz
new file mode 100644
index 0000000..7039b60
--- /dev/null
+++ b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/waiter64.gz
Binary files differ
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_1/links.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_1/links.txt
new file mode 100644
index 0000000..29aa2e4
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_1/links.txt
@@ -0,0 +1,2 @@
+../../common/libbluetooth.so_41de80f38623dac3c221b849566fb858 libbluetooth.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_1/maps.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_1/maps.txt
new file mode 100644
index 0000000..60ce7c7
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_1/maps.txt
@@ -0,0 +1,4 @@
+7b5e40e000-7b5e5d5000 r--p 0 00:00 0   libbluetooth.so
+7b5e5d5000-7b5ec43000 r-xp 1c7000 00:00 0   libbluetooth.so
+7e86814000-7e86850000 r--p 0 00:00 0   libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_1/output.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_1/output.txt
new file mode 100644
index 0000000..50852d6
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_1/output.txt
@@ -0,0 +1,13 @@
+  #00 pc 00000000007cbbdc  libbluetooth.so (bluetooth::packet::Iterator<true>::operator*() const+368)
+  #01 pc 0000000000756ab0  libbluetooth.so (bluetooth::hci::LeExtendedAdvertisingReport::Parse(bluetooth::hci::LeExtendedAdvertisingReport*, bluetooth::packet::Iterator<true>)+1228)
+  #02 pc 0000000000756344  libbluetooth.so (bluetooth::hci::LeExtendedAdvertisingReportView::GetAdvertisingReports()+872)
+  #03 pc 0000000000750ec8  libbluetooth.so (bluetooth::hci::LeScanningManager::impl::handle_extended_advertising_report(bluetooth::hci::LeExtendedAdvertisingReportView)+116)
+  #04 pc 000000000074f48c  libbluetooth.so (bluetooth::hci::LeScanningManager::impl::handle_scan_results(bluetooth::hci::LeMetaEventView)+760)
+  #05 pc 0000000000750284  libbluetooth.so (base::internal::Invoker<base::internal::BindState<void (bluetooth::hci::LeScanningManager::impl::*)(bluetooth::hci::LeMetaEventView), base::internal::UnretainedWrapper<bluetooth::hci::LeScanningManager::impl> >, void (bluetooth::hci::LeMetaEventView)>::Run(base::internal::BindStateBase*, bluetooth::hci::LeMetaEventView&&)+312)
+  #06 pc 00000000006f9908  libbluetooth.so (void base::internal::FunctorTraits<base::RepeatingCallback<void (bluetooth::hci::LeMetaEventView)>, void>::Invoke<base::RepeatingCallback<void (bluetooth::hci::LeMetaEventView)>, bluetooth::hci::LeMetaEventView>(base::RepeatingCallback<void (bluetooth::hci::LeMetaEventView)>&&, bluetooth::hci::LeMetaEventView&&)+296)
+  #07 pc 000000000081ac88  libbluetooth.so (bluetooth::os::Handler::handle_next_event()+244)
+  #08 pc 000000000081b580  libbluetooth.so (bluetooth::os::Reactor::Run()+608)
+  #09 pc 000000000081bec0  libbluetooth.so (bluetooth::os::Thread::run(bluetooth::os::Thread::Priority)+172)
+  #10 pc 000000000081c028  libbluetooth.so (void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (bluetooth::os::Thread::*)(bluetooth::os::Thread::Priority), bluetooth::os::Thread*, bluetooth::os::Thread::Priority> >(void*)+64)
+  #11 pc 00000000000b1920  libc.so (__pthread_start(void*)+264)
+  #12 pc 00000000000513f0  libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_1/regs.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_1/regs.txt
new file mode 100644
index 0000000..31d33d6
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_1/regs.txt
@@ -0,0 +1,34 @@
+x0: 1
+x1: d
+x2: 7c0f6d5110
+x3: 7b5ae05f58
+x4: 4
+x5: 33
+x6: 4025000fe2c160c
+x7: eb0a02e0114040c1
+x8: 87fc4d8789a28136
+x9: 87fc4d8789a28136
+x10: a
+x11: 0
+x12: 8
+x13: 106b9c07
+x14: 0
+x15: 0
+x16: 7e831617d0
+x17: 7e86850b40
+x18: 7b59516000
+x19: 7b5ae06138
+x20: 7b5ae06160
+x21: 7b5ae06210
+x22: 2f
+x23: 0
+x24: 0
+x25: 7b5ae07000
+x26: 7b5ec4ad78
+x27: 7b5ae05f88
+x28: 7b5ec4ad98
+x29: 7b5ae05fb0
+lr: 7b5ebd9b2c
+sp: 7b5ae05f70
+pc: 7b5ebd9bdc
+pst: 60000000
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_1/stack.data b/libunwindstack/offline_files/bluetooth_arm64/pc_1/stack.data
new file mode 100644
index 0000000..7395be3
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_1/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_2/links.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_2/links.txt
new file mode 100644
index 0000000..29aa2e4
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_2/links.txt
@@ -0,0 +1,2 @@
+../../common/libbluetooth.so_41de80f38623dac3c221b849566fb858 libbluetooth.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_2/maps.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_2/maps.txt
new file mode 100644
index 0000000..88dffe8
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_2/maps.txt
@@ -0,0 +1,4 @@
+7b6021f000-7b603e6000 r--p 0 00:00 0   libbluetooth.so
+7b603e6000-7b60a54000 r-xp 1c7000 00:00 0   libbluetooth.so
+7e86814000-7e86850000 r--p 0 00:00 0   libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_2/output.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_2/output.txt
new file mode 100644
index 0000000..76f4825
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_2/output.txt
@@ -0,0 +1,7 @@
+  #00 pc 0000000000832188  libbluetooth.so
+  #01 pc 000000000081aca4  libbluetooth.so (bluetooth::os::Handler::handle_next_event()+272)
+  #02 pc 000000000081b580  libbluetooth.so (bluetooth::os::Reactor::Run()+608)
+  #03 pc 000000000081bec0  libbluetooth.so (bluetooth::os::Thread::run(bluetooth::os::Thread::Priority)+172)
+  #04 pc 000000000081c028  libbluetooth.so (void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (bluetooth::os::Thread::*)(bluetooth::os::Thread::Priority), bluetooth::os::Thread*, bluetooth::os::Thread::Priority> >(void*)+64)
+  #05 pc 00000000000b1920  libc.so (__pthread_start(void*)+264)
+  #06 pc 00000000000513f0  libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_2/regs.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_2/regs.txt
new file mode 100644
index 0000000..364f927
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_2/regs.txt
@@ -0,0 +1,34 @@
+x0: 7b5cb42718
+x1: 7e868e244c
+x2: 7c2f7039d0
+x3: 7b5cb426d8
+x4: 48
+x5: b
+x6: 7cbf6a4290
+x7: 7b5cb41ff9
+x8: 0
+x9: 0
+x10: 7c2f69d000
+x11: 60
+x12: 11
+x13: 106b9c07
+x14: 70742a85
+x15: 20d43150fc510e
+x16: 7b60a8a000
+x17: 7b63298604
+x18: 7b5c1d6000
+x19: 7c2f6aaf58
+x20: 7c0f6cd810
+x21: 7b5cb43000
+x22: 1
+x23: 0
+x24: ffffffff
+x25: 7c2f6ab4d0
+x26: 7c2f6ab4ec
+x27: 1
+x28: 7c7f6d7a60
+x29: 7b5cb42730
+lr: 7b60a39ca8
+sp: 7b5cb42710
+pc: 7b60a51188
+pst: 60000000
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_2/stack.data b/libunwindstack/offline_files/bluetooth_arm64/pc_2/stack.data
new file mode 100644
index 0000000..9bea330
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_2/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_3/links.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_3/links.txt
new file mode 100644
index 0000000..29aa2e4
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_3/links.txt
@@ -0,0 +1,2 @@
+../../common/libbluetooth.so_41de80f38623dac3c221b849566fb858 libbluetooth.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_3/maps.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_3/maps.txt
new file mode 100644
index 0000000..3acc483
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_3/maps.txt
@@ -0,0 +1,4 @@
+7b56f20000-7b570e7000 r--p 0 00:00 0   libbluetooth.so
+7b570e7000-7b57755000 r-xp 1c7000 00:00 0   libbluetooth.so
+7e86814000-7e86850000 r--p 0 00:00 0   libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_3/output.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_3/output.txt
new file mode 100644
index 0000000..e9d42c9
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_3/output.txt
@@ -0,0 +1,11 @@
+  #00 pc 00000000003f0698  libbluetooth.so (allocation_tracker_notify_free(unsigned char, void*)+592)
+  #01 pc 00000000003f0d40  libbluetooth.so (osi_free(void*)+16)
+  #02 pc 00000000003f4598  libbluetooth.so (list_free_node_(list_t*, list_node_t*)+84)
+  #03 pc 00000000003f44e4  libbluetooth.so (list_remove(list_t*, void*)+356)
+  #04 pc 00000000003ef738  libbluetooth.so (callback_dispatch(void*)+188)
+  #05 pc 00000000003f6a70  libbluetooth.so (work_queue_read_cb(void*)+92)
+  #06 pc 00000000003f4cc4  libbluetooth.so (run_reactor(reactor_t*, int)+380)
+  #07 pc 00000000003f4b1c  libbluetooth.so (reactor_start(reactor_t*)+84)
+  #08 pc 00000000003f65dc  libbluetooth.so (run_thread(void*)+196)
+  #09 pc 00000000000b1920  libc.so (__pthread_start(void*)+264)
+  #10 pc 00000000000513f0  libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_3/regs.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_3/regs.txt
new file mode 100644
index 0000000..e8b9437
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_3/regs.txt
@@ -0,0 +1,34 @@
+x0: 0
+x1: 7bff6daf18
+x2: 15f199c2
+x3: 0
+x4: 7db0c8a
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 10
+x9: 124d6
+x10: 20
+x11: 7b577e6000
+x12: 1
+x13: 7bff6daf18
+x14: 341555ac
+x15: 18
+x16: 7e83161730
+x17: 7e868c6698
+x18: 7b415a2000
+x19: 7bff6daf18
+x20: 7bff6d8ae0
+x21: 7bff6daf10
+x22: 7b56fc8897
+x23: 7b56f8dcc7
+x24: 7b56f7870a
+x25: 7bff6daf28
+x26: 766310a6006816c5
+x27: 7b577e6758
+x28: 0
+x29: 7b41ab2f90
+lr: 7b57310488
+sp: 7b41ab2e50
+pc: 7b57310698
+pst: 20000000
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/137-cfi.odex b/libunwindstack/offline_files/bluetooth_arm64/pc_3/stack.data
similarity index 61%
rename from libunwindstack/tests/files/offline/jit_debug_x86/137-cfi.odex
rename to libunwindstack/offline_files/bluetooth_arm64/pc_3/stack.data
index 870ac0a..cfc8a86 100644
--- a/libunwindstack/tests/files/offline/jit_debug_x86/137-cfi.odex
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_3/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_4/links.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_4/links.txt
new file mode 100644
index 0000000..29aa2e4
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_4/links.txt
@@ -0,0 +1,2 @@
+../../common/libbluetooth.so_41de80f38623dac3c221b849566fb858 libbluetooth.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_4/maps.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_4/maps.txt
new file mode 100644
index 0000000..3acc483
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_4/maps.txt
@@ -0,0 +1,4 @@
+7b56f20000-7b570e7000 r--p 0 00:00 0   libbluetooth.so
+7b570e7000-7b57755000 r-xp 1c7000 00:00 0   libbluetooth.so
+7e86814000-7e86850000 r--p 0 00:00 0   libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_4/output.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_4/output.txt
new file mode 100644
index 0000000..9dc4dd9
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_4/output.txt
@@ -0,0 +1,4 @@
+  #00 pc 00000000003f5b24  libbluetooth.so (semaphore_post(semaphore_t*))
+  #01 pc 000000000005c3b8  libc.so (__timer_thread_start(void*)+136)
+  #02 pc 00000000000b1920  libc.so (__pthread_start(void*)+264)
+  #03 pc 00000000000513f0  libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_4/regs.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_4/regs.txt
new file mode 100644
index 0000000..1d080e6
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_4/regs.txt
@@ -0,0 +1,34 @@
+x0: 7bff6f4a78
+x1: 7b43cafba0
+x2: 0
+x3: 8
+x4: ffffffff
+x5: 0
+x6: 36313031
+x7: 33363130
+x8: 7b577e6000
+x9: 87fc4d8789a28136
+x10: 0
+x11: 0
+x12: 1
+x13: 100000000
+x14: 0
+x15: 30
+x16: 7e868d3f28
+x17: 7e86863adc
+x18: 7b4330e000
+x19: 7c0f6c0750
+x20: 7b43cb0000
+x21: 7b43cafcb0
+x22: 2777
+x23: 2794
+x24: 7b43cafcb0
+x25: 7b43cafcb0
+x26: 7b43cafff8
+x27: fc000
+x28: 7b43bb7000
+x29: 7b43cafc30
+lr: 7e868703bc
+sp: 7b43cafba0
+pc: 7b57315b24
+pst: 60000000
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_4/stack.data b/libunwindstack/offline_files/bluetooth_arm64/pc_4/stack.data
new file mode 100644
index 0000000..96d17cc
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_4/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/common/base.odex_maps_compiled_arm64.gz b/libunwindstack/offline_files/common/base.odex_maps_compiled_arm64.gz
new file mode 100644
index 0000000..e06cbbe
--- /dev/null
+++ b/libunwindstack/offline_files/common/base.odex_maps_compiled_arm64.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/boot-core-libart.oat_8368d55c916dc1224e76017186edfceee88a9955.gz b/libunwindstack/offline_files/common/boot-core-libart.oat_8368d55c916dc1224e76017186edfceee88a9955.gz
new file mode 100644
index 0000000..be140f3
--- /dev/null
+++ b/libunwindstack/offline_files/common/boot-core-libart.oat_8368d55c916dc1224e76017186edfceee88a9955.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/boot-framework.oat_6da45a084bf1f153be922249096389b66d69b6e6.gz b/libunwindstack/offline_files/common/boot-framework.oat_6da45a084bf1f153be922249096389b66d69b6e6.gz
new file mode 100644
index 0000000..0348933
--- /dev/null
+++ b/libunwindstack/offline_files/common/boot-framework.oat_6da45a084bf1f153be922249096389b66d69b6e6.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c.gz b/libunwindstack/offline_files/common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c.gz
new file mode 100644
index 0000000..1afbe4a
--- /dev/null
+++ b/libunwindstack/offline_files/common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/libandroid_runtime.so_7d88088666db374aecde2fbe51bff2f4.gz b/libunwindstack/offline_files/common/libandroid_runtime.so_7d88088666db374aecde2fbe51bff2f4.gz
new file mode 100644
index 0000000..63d2c2d
--- /dev/null
+++ b/libunwindstack/offline_files/common/libandroid_runtime.so_7d88088666db374aecde2fbe51bff2f4.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/libart.so_82c0556f4b66528e4a608c100a63b712.gz b/libunwindstack/offline_files/common/libart.so_82c0556f4b66528e4a608c100a63b712.gz
new file mode 100644
index 0000000..8936a9c
--- /dev/null
+++ b/libunwindstack/offline_files/common/libart.so_82c0556f4b66528e4a608c100a63b712.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/libbluetooth.so_41de80f38623dac3c221b849566fb858.gz b/libunwindstack/offline_files/common/libbluetooth.so_41de80f38623dac3c221b849566fb858.gz
new file mode 100644
index 0000000..2c5cc32
--- /dev/null
+++ b/libunwindstack/offline_files/common/libbluetooth.so_41de80f38623dac3c221b849566fb858.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/libc.so_3106fb6eab1b2dc6704157e908a50eba.gz b/libunwindstack/offline_files/common/libc.so_3106fb6eab1b2dc6704157e908a50eba.gz
new file mode 100644
index 0000000..abb334f
--- /dev/null
+++ b/libunwindstack/offline_files/common/libc.so_3106fb6eab1b2dc6704157e908a50eba.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/libc.so_f3791c53da47e6e72151dcc8088b9048.gz b/libunwindstack/offline_files/common/libc.so_f3791c53da47e6e72151dcc8088b9048.gz
new file mode 100644
index 0000000..c1ce2cc
--- /dev/null
+++ b/libunwindstack/offline_files/common/libc.so_f3791c53da47e6e72151dcc8088b9048.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/libjavacore.so_1cda9a31939d0b05577a62e79f44fc40.gz b/libunwindstack/offline_files/common/libjavacore.so_1cda9a31939d0b05577a62e79f44fc40.gz
new file mode 100644
index 0000000..6749740
--- /dev/null
+++ b/libunwindstack/offline_files/common/libjavacore.so_1cda9a31939d0b05577a62e79f44fc40.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/libutils.so_b8aa8db7e6895d0ba92398ca5d3ed2d4.gz b/libunwindstack/offline_files/common/libutils.so_b8aa8db7e6895d0ba92398ca5d3ed2d4.gz
new file mode 100644
index 0000000..5154821
--- /dev/null
+++ b/libunwindstack/offline_files/common/libutils.so_b8aa8db7e6895d0ba92398ca5d3ed2d4.gz
Binary files differ
diff --git a/libunwindstack/offline_files/debug_frame_first_x86/libc.so.gz b/libunwindstack/offline_files/debug_frame_first_x86/libc.so.gz
new file mode 100644
index 0000000..33e085f
--- /dev/null
+++ b/libunwindstack/offline_files/debug_frame_first_x86/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/maps.txt b/libunwindstack/offline_files/debug_frame_first_x86/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/debug_frame_first_x86/maps.txt
rename to libunwindstack/offline_files/debug_frame_first_x86/maps.txt
diff --git a/libunwindstack/offline_files/debug_frame_first_x86/output.txt b/libunwindstack/offline_files/debug_frame_first_x86/output.txt
new file mode 100644
index 0000000..887e908
--- /dev/null
+++ b/libunwindstack/offline_files/debug_frame_first_x86/output.txt
@@ -0,0 +1,5 @@
+  #00 pc 00000685  waiter (call_level3+53)
+  #01 pc 000006b7  waiter (call_level2+23)
+  #02 pc 000006d7  waiter (call_level1+23)
+  #03 pc 000006f7  waiter (main+23)
+  #04 pc 00018275  libc.so
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/regs.txt b/libunwindstack/offline_files/debug_frame_first_x86/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/debug_frame_first_x86/regs.txt
rename to libunwindstack/offline_files/debug_frame_first_x86/regs.txt
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/stack.data b/libunwindstack/offline_files/debug_frame_first_x86/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/debug_frame_first_x86/stack.data
rename to libunwindstack/offline_files/debug_frame_first_x86/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/debug_frame_first_x86/waiter.gz b/libunwindstack/offline_files/debug_frame_first_x86/waiter.gz
new file mode 100644
index 0000000..4b84cd9
--- /dev/null
+++ b/libunwindstack/offline_files/debug_frame_first_x86/waiter.gz
Binary files differ
diff --git a/libunwindstack/offline_files/debug_frame_load_bias_arm/libbinder.so.gz b/libunwindstack/offline_files/debug_frame_load_bias_arm/libbinder.so.gz
new file mode 100644
index 0000000..c9469fa
--- /dev/null
+++ b/libunwindstack/offline_files/debug_frame_load_bias_arm/libbinder.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/debug_frame_load_bias_arm/libc.so.gz b/libunwindstack/offline_files/debug_frame_load_bias_arm/libc.so.gz
new file mode 100644
index 0000000..9063e51
--- /dev/null
+++ b/libunwindstack/offline_files/debug_frame_load_bias_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt b/libunwindstack/offline_files/debug_frame_load_bias_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt
rename to libunwindstack/offline_files/debug_frame_load_bias_arm/maps.txt
diff --git a/libunwindstack/offline_files/debug_frame_load_bias_arm/mediaserver.gz b/libunwindstack/offline_files/debug_frame_load_bias_arm/mediaserver.gz
new file mode 100644
index 0000000..719fea0
--- /dev/null
+++ b/libunwindstack/offline_files/debug_frame_load_bias_arm/mediaserver.gz
Binary files differ
diff --git a/libunwindstack/offline_files/debug_frame_load_bias_arm/output.txt b/libunwindstack/offline_files/debug_frame_load_bias_arm/output.txt
new file mode 100644
index 0000000..0860ec9
--- /dev/null
+++ b/libunwindstack/offline_files/debug_frame_load_bias_arm/output.txt
@@ -0,0 +1,8 @@
+  #00 pc 0005138c  libc.so (__ioctl+8)
+  #01 pc 0002140f  libc.so (ioctl+30)
+  #02 pc 00039535  libbinder.so (android::IPCThreadState::talkWithDriver(bool)+204)
+  #03 pc 00039633  libbinder.so (android::IPCThreadState::getAndExecuteCommand()+10)
+  #04 pc 00039b57  libbinder.so (android::IPCThreadState::joinThreadPool(bool)+38)
+  #05 pc 00000c21  mediaserver (main+104)
+  #06 pc 00084b89  libc.so (__libc_init+48)
+  #07 pc 00000b77  mediaserver (_start_main+38)
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt b/libunwindstack/offline_files/debug_frame_load_bias_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt
rename to libunwindstack/offline_files/debug_frame_load_bias_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data b/libunwindstack/offline_files/debug_frame_load_bias_arm/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data
rename to libunwindstack/offline_files/debug_frame_load_bias_arm/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/eh_frame_bias_x86/libc.so.gz b/libunwindstack/offline_files/eh_frame_bias_x86/libc.so.gz
new file mode 100644
index 0000000..f26eb69
--- /dev/null
+++ b/libunwindstack/offline_files/eh_frame_bias_x86/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/maps.txt b/libunwindstack/offline_files/eh_frame_bias_x86/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/eh_frame_bias_x86/maps.txt
rename to libunwindstack/offline_files/eh_frame_bias_x86/maps.txt
diff --git a/libunwindstack/offline_files/eh_frame_bias_x86/output.txt b/libunwindstack/offline_files/eh_frame_bias_x86/output.txt
new file mode 100644
index 0000000..e073809
--- /dev/null
+++ b/libunwindstack/offline_files/eh_frame_bias_x86/output.txt
@@ -0,0 +1,11 @@
+  #00 pc ffffe430  vdso.so (__kernel_vsyscall+16)
+  #01 pc 00082a4b  libc.so (__epoll_pwait+43)
+  #02 pc 000303a3  libc.so (epoll_pwait+115)
+  #03 pc 000303ed  libc.so (epoll_wait+45)
+  #04 pc 00010ea2  tombstoned (epoll_dispatch+226)
+  #05 pc 0000c5e7  tombstoned (event_base_loop+1095)
+  #06 pc 0000c193  tombstoned (event_base_dispatch+35)
+  #07 pc 00005c77  tombstoned (main+884)
+  #08 pc 00015f66  libc.so (__libc_init+102)
+  #09 pc 0000360e  tombstoned (_start+98)
+  #10 pc 00000001  <unknown>
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/regs.txt b/libunwindstack/offline_files/eh_frame_bias_x86/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/eh_frame_bias_x86/regs.txt
rename to libunwindstack/offline_files/eh_frame_bias_x86/regs.txt
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/stack.data b/libunwindstack/offline_files/eh_frame_bias_x86/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/eh_frame_bias_x86/stack.data
rename to libunwindstack/offline_files/eh_frame_bias_x86/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/eh_frame_bias_x86/tombstoned.gz b/libunwindstack/offline_files/eh_frame_bias_x86/tombstoned.gz
new file mode 100644
index 0000000..35d6b39
--- /dev/null
+++ b/libunwindstack/offline_files/eh_frame_bias_x86/tombstoned.gz
Binary files differ
diff --git a/libunwindstack/offline_files/eh_frame_bias_x86/vdso.so.gz b/libunwindstack/offline_files/eh_frame_bias_x86/vdso.so.gz
new file mode 100644
index 0000000..93490de
--- /dev/null
+++ b/libunwindstack/offline_files/eh_frame_bias_x86/vdso.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/libc.so.gz b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/libc.so.gz
new file mode 100644
index 0000000..e64a160
--- /dev/null
+++ b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt
rename to libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/maps.txt
diff --git a/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/output.txt b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/output.txt
new file mode 100644
index 0000000..ea75136
--- /dev/null
+++ b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/output.txt
@@ -0,0 +1,5 @@
+  #00 pc 0000000000000a80  unwind_test64 (calling3)
+  #01 pc 0000000000000dd9  unwind_test64 (calling2+633)
+  #02 pc 000000000000121e  unwind_test64 (calling1+638)
+  #03 pc 00000000000013ed  unwind_test64 (main+13)
+  #04 pc 00000000000202b0  libc.so
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt
rename to libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/regs.txt
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data
rename to libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/unwind_test64.gz b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/unwind_test64.gz
new file mode 100644
index 0000000..5f8f954
--- /dev/null
+++ b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/unwind_test64.gz
Binary files differ
diff --git a/libunwindstack/offline_files/empty_arm64/libbinder.so.gz b/libunwindstack/offline_files/empty_arm64/libbinder.so.gz
new file mode 100644
index 0000000..e752f7d
--- /dev/null
+++ b/libunwindstack/offline_files/empty_arm64/libbinder.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/empty_arm64/libc.so.gz b/libunwindstack/offline_files/empty_arm64/libc.so.gz
new file mode 100644
index 0000000..8bd14b5
--- /dev/null
+++ b/libunwindstack/offline_files/empty_arm64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/maps.txt b/libunwindstack/offline_files/empty_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/empty_arm64/maps.txt
rename to libunwindstack/offline_files/empty_arm64/maps.txt
diff --git a/libunwindstack/offline_files/empty_arm64/netd.gz b/libunwindstack/offline_files/empty_arm64/netd.gz
new file mode 100644
index 0000000..ba6efa1
--- /dev/null
+++ b/libunwindstack/offline_files/empty_arm64/netd.gz
Binary files differ
diff --git a/libunwindstack/offline_files/empty_arm64/output.txt b/libunwindstack/offline_files/empty_arm64/output.txt
new file mode 100644
index 0000000..486508a
--- /dev/null
+++ b/libunwindstack/offline_files/empty_arm64/output.txt
@@ -0,0 +1,7 @@
+  #00 pc 00000000000963a4  libc.so (__ioctl+4)
+  #01 pc 000000000005344c  libc.so (ioctl+140)
+  #02 pc 0000000000050ce4  libbinder.so (android::IPCThreadState::talkWithDriver(bool)+308)
+  #03 pc 0000000000050e98  libbinder.so (android::IPCThreadState::getAndExecuteCommand()+24)
+  #04 pc 00000000000516ac  libbinder.so (android::IPCThreadState::joinThreadPool(bool)+60)
+  #05 pc 00000000000443b0  netd (main+1056)
+  #06 pc 0000000000045594  libc.so (__libc_init+108)
diff --git a/libunwindstack/tests/files/offline/empty_arm64/regs.txt b/libunwindstack/offline_files/empty_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/empty_arm64/regs.txt
rename to libunwindstack/offline_files/empty_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/empty_arm64/stack.data b/libunwindstack/offline_files/empty_arm64/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/empty_arm64/stack.data
rename to libunwindstack/offline_files/empty_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/gnu_debugdata_arm/libandroid_runtime.so.gz b/libunwindstack/offline_files/gnu_debugdata_arm/libandroid_runtime.so.gz
new file mode 100644
index 0000000..199c4b2
--- /dev/null
+++ b/libunwindstack/offline_files/gnu_debugdata_arm/libandroid_runtime.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/maps.txt b/libunwindstack/offline_files/gnu_debugdata_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/gnu_debugdata_arm/maps.txt
rename to libunwindstack/offline_files/gnu_debugdata_arm/maps.txt
diff --git a/libunwindstack/offline_files/gnu_debugdata_arm/output.txt b/libunwindstack/offline_files/gnu_debugdata_arm/output.txt
new file mode 100644
index 0000000..3b6fe65
--- /dev/null
+++ b/libunwindstack/offline_files/gnu_debugdata_arm/output.txt
@@ -0,0 +1,2 @@
+  #00 pc 0006dc49  libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+80)
+  #01 pc 0006dce5  libandroid_runtime.so (android::AndroidRuntime::javaCreateThreadEtc(int (*)(void*), void*, char const*, int, unsigned int, void**))
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/regs.txt b/libunwindstack/offline_files/gnu_debugdata_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/gnu_debugdata_arm/regs.txt
rename to libunwindstack/offline_files/gnu_debugdata_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/stack.data b/libunwindstack/offline_files/gnu_debugdata_arm/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/gnu_debugdata_arm/stack.data
rename to libunwindstack/offline_files/gnu_debugdata_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt b/libunwindstack/offline_files/invalid_elf_offset_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt
rename to libunwindstack/offline_files/invalid_elf_offset_arm/maps.txt
diff --git a/libunwindstack/offline_files/invalid_elf_offset_arm/output.txt b/libunwindstack/offline_files/invalid_elf_offset_arm/output.txt
new file mode 100644
index 0000000..fe7e485
--- /dev/null
+++ b/libunwindstack/offline_files/invalid_elf_offset_arm/output.txt
@@ -0,0 +1 @@
+  #00 pc 00aa7508  invalid.apk (offset 0x12e4000)
diff --git a/libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt b/libunwindstack/offline_files/invalid_elf_offset_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt
rename to libunwindstack/offline_files/invalid_elf_offset_arm/regs.txt
diff --git a/libunwindstack/offline_files/jit_debug_arm/137-cfi.odex.gz b/libunwindstack/offline_files/jit_debug_arm/137-cfi.odex.gz
new file mode 100644
index 0000000..7150613
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_arm/137-cfi.odex.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_arm/dalvikvm32.gz b/libunwindstack/offline_files/jit_debug_arm/dalvikvm32.gz
new file mode 100644
index 0000000..868440f
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_arm/dalvikvm32.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/descriptor.data b/libunwindstack/offline_files/jit_debug_arm/descriptor.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/descriptor.data
rename to libunwindstack/offline_files/jit_debug_arm/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/descriptor1.data b/libunwindstack/offline_files/jit_debug_arm/descriptor1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/descriptor1.data
rename to libunwindstack/offline_files/jit_debug_arm/descriptor1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry0.data b/libunwindstack/offline_files/jit_debug_arm/entry0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/entry0.data
rename to libunwindstack/offline_files/jit_debug_arm/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry1.data b/libunwindstack/offline_files/jit_debug_arm/entry1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/entry1.data
rename to libunwindstack/offline_files/jit_debug_arm/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry2.data b/libunwindstack/offline_files/jit_debug_arm/entry2.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/entry2.data
rename to libunwindstack/offline_files/jit_debug_arm/entry2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry3.data b/libunwindstack/offline_files/jit_debug_arm/entry3.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/entry3.data
rename to libunwindstack/offline_files/jit_debug_arm/entry3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry4.data b/libunwindstack/offline_files/jit_debug_arm/entry4.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/entry4.data
rename to libunwindstack/offline_files/jit_debug_arm/entry4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry5.data b/libunwindstack/offline_files/jit_debug_arm/entry5.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/entry5.data
rename to libunwindstack/offline_files/jit_debug_arm/entry5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry6.data b/libunwindstack/offline_files/jit_debug_arm/entry6.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/entry6.data
rename to libunwindstack/offline_files/jit_debug_arm/entry6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit0.data b/libunwindstack/offline_files/jit_debug_arm/jit0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/jit0.data
rename to libunwindstack/offline_files/jit_debug_arm/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit1.data b/libunwindstack/offline_files/jit_debug_arm/jit1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/jit1.data
rename to libunwindstack/offline_files/jit_debug_arm/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit2.data b/libunwindstack/offline_files/jit_debug_arm/jit2.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/jit2.data
rename to libunwindstack/offline_files/jit_debug_arm/jit2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit3.data b/libunwindstack/offline_files/jit_debug_arm/jit3.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/jit3.data
rename to libunwindstack/offline_files/jit_debug_arm/jit3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit4.data b/libunwindstack/offline_files/jit_debug_arm/jit4.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/jit4.data
rename to libunwindstack/offline_files/jit_debug_arm/jit4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit5.data b/libunwindstack/offline_files/jit_debug_arm/jit5.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/jit5.data
rename to libunwindstack/offline_files/jit_debug_arm/jit5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit6.data b/libunwindstack/offline_files/jit_debug_arm/jit6.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/jit6.data
rename to libunwindstack/offline_files/jit_debug_arm/jit6.data
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_arm/libart.so.gz b/libunwindstack/offline_files/jit_debug_arm/libart.so.gz
new file mode 100644
index 0000000..f23b6fd
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_arm/libart.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_arm/libartd.so.gz b/libunwindstack/offline_files/jit_debug_arm/libartd.so.gz
new file mode 100644
index 0000000..4f9d860
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_arm/libartd.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_arm/libarttestd.so.gz b/libunwindstack/offline_files/jit_debug_arm/libarttestd.so.gz
new file mode 100644
index 0000000..49c9933
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_arm/libarttestd.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_arm/libc.so.gz b/libunwindstack/offline_files/jit_debug_arm/libc.so.gz
new file mode 100644
index 0000000..65f3cf2
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt b/libunwindstack/offline_files/jit_debug_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
rename to libunwindstack/offline_files/jit_debug_arm/maps.txt
diff --git a/libunwindstack/offline_files/jit_debug_arm/output.txt b/libunwindstack/offline_files/jit_debug_arm/output.txt
new file mode 100644
index 0000000..094168c
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_arm/output.txt
@@ -0,0 +1,76 @@
+  #00 pc 00018a5e  libarttestd.so (Java_Main_unwindInProcess+866)
+  #01 pc 0000212d  137-cfi.odex (boolean Main.unwindInProcess(boolean, int, boolean)+92)
+  #02 pc 00011cb1  anonymous:e2796000 (boolean Main.bar(boolean)+72)
+  #03 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)
+  #04 pc 00467129  libartd.so (art_quick_invoke_stub+228)
+  #05 pc 000bf7a9  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+864)
+  #06 pc 00247833  libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+382)
+  #07 pc 0022e935  libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+244)
+  #08 pc 0022f71d  libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+128)
+  #09 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)
+  #10 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)
+  #11 pc 00011c31  anonymous:e2796000 (int Main.compare(Main, Main)+64)
+  #12 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)
+  #13 pc 00467129  libartd.so (art_quick_invoke_stub+228)
+  #14 pc 000bf7a9  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+864)
+  #15 pc 00247833  libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+382)
+  #16 pc 0022e935  libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+244)
+  #17 pc 0022f71d  libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+128)
+  #18 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)
+  #19 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)
+  #20 pc 00011b77  anonymous:e2796000 (int Main.compare(java.lang.Object, java.lang.Object)+118)
+  #21 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)
+  #22 pc 00467129  libartd.so (art_quick_invoke_stub+228)
+  #23 pc 000bf7a9  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+864)
+  #24 pc 00247833  libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+382)
+  #25 pc 0022e935  libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+244)
+  #26 pc 0022f71d  libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+128)
+  #27 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)
+  #28 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)
+  #29 pc 00011a29  anonymous:e2796000 (int java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)+304)
+  #30 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)
+  #31 pc 0046722f  libartd.so (art_quick_invoke_static_stub+226)
+  #32 pc 000bf7bb  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+882)
+  #33 pc 00247833  libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+382)
+  #34 pc 0022e935  libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+244)
+  #35 pc 0022f71d  libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+128)
+  #36 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)
+  #37 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)
+  #38 pc 0001139b  anonymous:e2796000 (boolean Main.foo()+178)
+  #39 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)
+  #40 pc 00467129  libartd.so (art_quick_invoke_stub+228)
+  #41 pc 000bf7a9  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+864)
+  #42 pc 00247833  libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+382)
+  #43 pc 0022e935  libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+244)
+  #44 pc 0022f71d  libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+128)
+  #45 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)
+  #46 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)
+  #47 pc 00010aa7  anonymous:e2796000 (void Main.runPrimary()+70)
+  #48 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)
+  #49 pc 00467129  libartd.so (art_quick_invoke_stub+228)
+  #50 pc 000bf7a9  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+864)
+  #51 pc 00247833  libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+382)
+  #52 pc 0022e935  libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+244)
+  #53 pc 0022f71d  libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+128)
+  #54 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)
+  #55 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)
+  #56 pc 0000ba99  anonymous:e2796000 (void Main.main(java.lang.String[])+144)
+  #57 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)
+  #58 pc 0046722f  libartd.so (art_quick_invoke_static_stub+226)
+  #59 pc 000bf7bb  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+882)
+  #60 pc 00247833  libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+382)
+  #61 pc 0022e935  libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+244)
+  #62 pc 0022f71d  libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+128)
+  #63 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)
+  #64 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)
+  #65 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)
+  #66 pc 0046722f  libartd.so (art_quick_invoke_static_stub+226)
+  #67 pc 000bf7bb  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+882)
+  #68 pc 003b292d  libartd.so (art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::ArgArray*, art::JValue*, char const*)+52)
+  #69 pc 003b26c3  libartd.so (art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+210)
+  #70 pc 00308411  libartd.so (art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+76)
+  #71 pc 000e6a9f  libartd.so (art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, std::__va_list, art::Primitive::Type, art::InvokeType)+1486)
+  #72 pc 000e19b9  libartd.so (art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+40)
+  #73 pc 0000159f  dalvikvm32 (_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+30)
+  #74 pc 00001349  dalvikvm32 (main+896)
+  #75 pc 000850c9  libc.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/regs.txt b/libunwindstack/offline_files/jit_debug_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/regs.txt
rename to libunwindstack/offline_files/jit_debug_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/stack.data b/libunwindstack/offline_files/jit_debug_arm/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/stack.data
rename to libunwindstack/offline_files/jit_debug_arm/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_x86/137-cfi.odex.gz b/libunwindstack/offline_files/jit_debug_x86/137-cfi.odex.gz
new file mode 100644
index 0000000..6b7a055
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_x86/137-cfi.odex.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_x86/dalvikvm32.gz b/libunwindstack/offline_files/jit_debug_x86/dalvikvm32.gz
new file mode 100644
index 0000000..e9306c6
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_x86/dalvikvm32.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/descriptor.data b/libunwindstack/offline_files/jit_debug_x86/descriptor.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/descriptor.data
rename to libunwindstack/offline_files/jit_debug_x86/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry0.data b/libunwindstack/offline_files/jit_debug_x86/entry0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/entry0.data
rename to libunwindstack/offline_files/jit_debug_x86/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry1.data b/libunwindstack/offline_files/jit_debug_x86/entry1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/entry1.data
rename to libunwindstack/offline_files/jit_debug_x86/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry2.data b/libunwindstack/offline_files/jit_debug_x86/entry2.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/entry2.data
rename to libunwindstack/offline_files/jit_debug_x86/entry2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry3.data b/libunwindstack/offline_files/jit_debug_x86/entry3.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/entry3.data
rename to libunwindstack/offline_files/jit_debug_x86/entry3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry4.data b/libunwindstack/offline_files/jit_debug_x86/entry4.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/entry4.data
rename to libunwindstack/offline_files/jit_debug_x86/entry4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry5.data b/libunwindstack/offline_files/jit_debug_x86/entry5.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/entry5.data
rename to libunwindstack/offline_files/jit_debug_x86/entry5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry6.data b/libunwindstack/offline_files/jit_debug_x86/entry6.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/entry6.data
rename to libunwindstack/offline_files/jit_debug_x86/entry6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit0.data b/libunwindstack/offline_files/jit_debug_x86/jit0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/jit0.data
rename to libunwindstack/offline_files/jit_debug_x86/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit1.data b/libunwindstack/offline_files/jit_debug_x86/jit1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/jit1.data
rename to libunwindstack/offline_files/jit_debug_x86/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit2.data b/libunwindstack/offline_files/jit_debug_x86/jit2.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/jit2.data
rename to libunwindstack/offline_files/jit_debug_x86/jit2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit3.data b/libunwindstack/offline_files/jit_debug_x86/jit3.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/jit3.data
rename to libunwindstack/offline_files/jit_debug_x86/jit3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit4.data b/libunwindstack/offline_files/jit_debug_x86/jit4.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/jit4.data
rename to libunwindstack/offline_files/jit_debug_x86/jit4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit5.data b/libunwindstack/offline_files/jit_debug_x86/jit5.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/jit5.data
rename to libunwindstack/offline_files/jit_debug_x86/jit5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit6.data b/libunwindstack/offline_files/jit_debug_x86/jit6.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/jit6.data
rename to libunwindstack/offline_files/jit_debug_x86/jit6.data
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_x86/libartd.so.gz b/libunwindstack/offline_files/jit_debug_x86/libartd.so.gz
new file mode 100644
index 0000000..83716b1
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_x86/libartd.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_x86/libarttestd.so.gz b/libunwindstack/offline_files/jit_debug_x86/libarttestd.so.gz
new file mode 100644
index 0000000..097eeb0
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_x86/libarttestd.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_x86/libc.so.gz b/libunwindstack/offline_files/jit_debug_x86/libc.so.gz
new file mode 100644
index 0000000..30230f7
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_x86/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt b/libunwindstack/offline_files/jit_debug_x86/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
rename to libunwindstack/offline_files/jit_debug_x86/maps.txt
diff --git a/libunwindstack/offline_files/jit_debug_x86/output.txt b/libunwindstack/offline_files/jit_debug_x86/output.txt
new file mode 100644
index 0000000..08b2401
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_x86/output.txt
@@ -0,0 +1,69 @@
+  #00 pc 00068fb8  libarttestd.so (art::CauseSegfault()+72)
+  #01 pc 00067f00  libarttestd.so (Java_Main_unwindInProcess+10032)
+  #02 pc 000021a8  137-cfi.odex (boolean Main.unwindInProcess(boolean, int, boolean)+136)
+  #03 pc 0000fe80  anonymous:ee74c000 (boolean Main.bar(boolean)+64)
+  #04 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)
+  #05 pc 00146ab5  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+885)
+  #06 pc 0039cf0d  libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+653)
+  #07 pc 00392552  libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+354)
+  #08 pc 0039399a  libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+234)
+  #09 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)
+  #10 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)
+  #11 pc 0000fe03  anonymous:ee74c000 (int Main.compare(Main, Main)+51)
+  #12 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)
+  #13 pc 00146ab5  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+885)
+  #14 pc 0039cf0d  libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+653)
+  #15 pc 00392552  libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+354)
+  #16 pc 0039399a  libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+234)
+  #17 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)
+  #18 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)
+  #19 pc 0000fd3b  anonymous:ee74c000 (int Main.compare(java.lang.Object, java.lang.Object)+107)
+  #20 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)
+  #21 pc 00146ab5  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+885)
+  #22 pc 0039cf0d  libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+653)
+  #23 pc 00392552  libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+354)
+  #24 pc 0039399a  libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+234)
+  #25 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)
+  #26 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)
+  #27 pc 0000fbdb  anonymous:ee74c000 (int java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)+331)
+  #28 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)
+  #29 pc 00146acb  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+907)
+  #30 pc 0039cf0d  libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+653)
+  #31 pc 00392552  libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+354)
+  #32 pc 0039399a  libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+234)
+  #33 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)
+  #34 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)
+  #35 pc 0000f624  anonymous:ee74c000 (boolean Main.foo()+164)
+  #36 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)
+  #37 pc 00146ab5  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+885)
+  #38 pc 0039cf0d  libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+653)
+  #39 pc 00392552  libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+354)
+  #40 pc 0039399a  libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+234)
+  #41 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)
+  #42 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)
+  #43 pc 0000eedb  anonymous:ee74c000 (void Main.runPrimary()+59)
+  #44 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)
+  #45 pc 00146ab5  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+885)
+  #46 pc 0039cf0d  libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+653)
+  #47 pc 00392552  libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+354)
+  #48 pc 0039399a  libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+234)
+  #49 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)
+  #50 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)
+  #51 pc 0000ac21  anonymous:ee74c000 (void Main.main(java.lang.String[])+97)
+  #52 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)
+  #53 pc 00146acb  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+907)
+  #54 pc 0039cf0d  libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+653)
+  #55 pc 00392552  libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+354)
+  #56 pc 0039399a  libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+234)
+  #57 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)
+  #58 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)
+  #59 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)
+  #60 pc 00146acb  libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+907)
+  #61 pc 005aac95  libartd.so (art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::ArgArray*, art::JValue*, char const*)+85)
+  #62 pc 005aab5a  libartd.so (art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, char*)+362)
+  #63 pc 0048a3dd  libartd.so (art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+125)
+  #64 pc 0018448c  libartd.so (art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, char*, art::Primitive::Type, art::InvokeType)+1964)
+  #65 pc 0017cf06  libartd.so (art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+70)
+  #66 pc 00001d8c  dalvikvm32 (_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+60)
+  #67 pc 00001a80  dalvikvm32 (main+1312)
+  #68 pc 00018275  libc.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/regs.txt b/libunwindstack/offline_files/jit_debug_x86/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/regs.txt
rename to libunwindstack/offline_files/jit_debug_x86/regs.txt
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/stack.data b/libunwindstack/offline_files/jit_debug_x86/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/stack.data
rename to libunwindstack/offline_files/jit_debug_x86/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/jit_map_arm/jit_map0.so.gz b/libunwindstack/offline_files/jit_map_arm/jit_map0.so.gz
new file mode 100644
index 0000000..2a332c9
--- /dev/null
+++ b/libunwindstack/offline_files/jit_map_arm/jit_map0.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_map_arm/jit_map1.so.gz b/libunwindstack/offline_files/jit_map_arm/jit_map1.so.gz
new file mode 100644
index 0000000..c1341db
--- /dev/null
+++ b/libunwindstack/offline_files/jit_map_arm/jit_map1.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_map_arm/libart.so.gz b/libunwindstack/offline_files/jit_map_arm/libart.so.gz
new file mode 100644
index 0000000..bacf50b
--- /dev/null
+++ b/libunwindstack/offline_files/jit_map_arm/libart.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_map_arm/libc.so.gz b/libunwindstack/offline_files/jit_map_arm/libc.so.gz
new file mode 100644
index 0000000..a33f276
--- /dev/null
+++ b/libunwindstack/offline_files/jit_map_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/maps.txt b/libunwindstack/offline_files/jit_map_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_map_arm/maps.txt
rename to libunwindstack/offline_files/jit_map_arm/maps.txt
diff --git a/libunwindstack/offline_files/jit_map_arm/output.txt b/libunwindstack/offline_files/jit_map_arm/output.txt
new file mode 100644
index 0000000..f9eb2ef
--- /dev/null
+++ b/libunwindstack/offline_files/jit_map_arm/output.txt
@@ -0,0 +1,6 @@
+  #00 pc 00000000  jit_map0.so (com.example.simpleperf.simpleperfexamplewithnative.MixActivity.access$000)
+  #01 pc 0000003d  jit_map1.so (com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)
+  #02 pc 004135bb  libart.so (art_quick_osr_stub+42)
+  #03 pc 003851dd  libart.so (art::Thread::CreateCallback(void*)+868)
+  #04 pc 00062925  libc.so (__pthread_start(void*)+22)
+  #05 pc 0001de39  libc.so (__start_thread+24)
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/regs.txt b/libunwindstack/offline_files/jit_map_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_map_arm/regs.txt
rename to libunwindstack/offline_files/jit_map_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/stack.data b/libunwindstack/offline_files/jit_map_arm/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_map_arm/stack.data
rename to libunwindstack/offline_files/jit_map_arm/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/load_bias_different_section_bias_arm64/libc.so.gz b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/libc.so.gz
new file mode 100644
index 0000000..ad5f4e5
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/load_bias_different_section_bias_arm64/linker64.gz b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/linker64.gz
new file mode 100644
index 0000000..0224762
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/linker64.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/maps.txt b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/maps.txt
rename to libunwindstack/offline_files/load_bias_different_section_bias_arm64/maps.txt
diff --git a/libunwindstack/offline_files/load_bias_different_section_bias_arm64/output.txt b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/output.txt
new file mode 100644
index 0000000..d34c97e
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/output.txt
@@ -0,0 +1,12 @@
+  #00 pc 00000000000d59bc  linker64 (__dl_syscall+28)
+  #01 pc 00000000000554e8  linker64 (__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1148)
+  #02 pc 00000000000008c0  vdso (__kernel_rt_sigreturn)
+  #03 pc 000000000007f3e8  libc.so (abort+168)
+  #04 pc 00000000000459fc  test (std::__ndk1::__throw_bad_cast()+4)
+  #05 pc 0000000000056d80  test (testing::Test::Run()+88)
+  #06 pc 000000000005724c  test (testing::TestInfo::Run()+112)
+  #07 pc 0000000000057558  test (testing::TestSuite::Run()+116)
+  #08 pc 000000000005bffc  test (testing::internal::UnitTestImpl::RunAllTests()+464)
+  #09 pc 000000000005bd9c  test (testing::UnitTest::Run()+116)
+  #10 pc 00000000000464e4  test (main+144)
+  #11 pc 000000000007aa34  libc.so (__libc_init+108)
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/regs.txt b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/regs.txt
rename to libunwindstack/offline_files/load_bias_different_section_bias_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/stack0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data
rename to libunwindstack/offline_files/load_bias_different_section_bias_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack1.data b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/stack1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack1.data
rename to libunwindstack/offline_files/load_bias_different_section_bias_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/offline_files/load_bias_different_section_bias_arm64/test.gz b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/test.gz
new file mode 100644
index 0000000..5a49557
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/test.gz
Binary files differ
diff --git a/libunwindstack/offline_files/load_bias_different_section_bias_arm64/vdso.gz b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/vdso.gz
new file mode 100644
index 0000000..99a8b7f
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/vdso.gz
Binary files differ
diff --git a/libunwindstack/offline_files/load_bias_ro_rx_x86_64/libc.so.gz b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/libc.so.gz
new file mode 100644
index 0000000..b63a853
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt
rename to libunwindstack/offline_files/load_bias_ro_rx_x86_64/maps.txt
diff --git a/libunwindstack/offline_files/load_bias_ro_rx_x86_64/output.txt b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/output.txt
new file mode 100644
index 0000000..592fb89
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/output.txt
@@ -0,0 +1,17 @@
+  #00 pc 00000000000e9dd4  libc.so (__write+20)
+  #01 pc 000000000007ab9c  libc.so (_IO_file_write+44)
+  #02 pc 0000000000079f3e  libc.so
+  #03 pc 000000000007bce8  libc.so (_IO_do_write+24)
+  #04 pc 000000000007b26e  libc.so (_IO_file_xsputn+270)
+  #05 pc 000000000004f7f9  libc.so (_IO_vfprintf+1945)
+  #06 pc 0000000000057cb5  libc.so (_IO_printf+165)
+  #07 pc 0000000000ed1796  perfetto_unittests (testing::internal::PrettyUnitTestResultPrinter::OnTestIterationStart(testing::UnitTest const&, int)+374)
+  #08 pc 0000000000ed30fd  perfetto_unittests (testing::internal::TestEventRepeater::OnTestIterationStart(testing::UnitTest const&, int)+125)
+  #09 pc 0000000000ed5e25  perfetto_unittests (testing::internal::UnitTestImpl::RunAllTests()+581)
+  #10 pc 0000000000ef63f3  perfetto_unittests (bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*)+131)
+  #11 pc 0000000000ee2a21  perfetto_unittests (bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*)+113)
+  #12 pc 0000000000ed5bb9  perfetto_unittests (testing::UnitTest::Run()+185)
+  #13 pc 0000000000e900f0  perfetto_unittests (RUN_ALL_TESTS()+16)
+  #14 pc 0000000000e900d8  perfetto_unittests (main+56)
+  #15 pc 000000000002352a  libc.so (__libc_start_main+234)
+  #16 pc 0000000000919029  perfetto_unittests (_start+41)
diff --git a/libunwindstack/offline_files/load_bias_ro_rx_x86_64/perfetto_unittests.gz b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/perfetto_unittests.gz
new file mode 100644
index 0000000..1172e25
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/perfetto_unittests.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt
rename to libunwindstack/offline_files/load_bias_ro_rx_x86_64/regs.txt
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data
rename to libunwindstack/offline_files/load_bias_ro_rx_x86_64/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/app_process64.gz b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/app_process64.gz
new file mode 100644
index 0000000..a2dc750
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/app_process64.gz
Binary files differ
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/links.txt b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/links.txt
new file mode 100644
index 0000000..27beb3e
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/links.txt
@@ -0,0 +1,6 @@
+../../common/base.odex_maps_compiled_arm64 base.odex_maps_compiled_arm64
+../../common/boot-framework.oat_6da45a084bf1f153be922249096389b66d69b6e6 boot-framework.oat
+../../common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c boot.oat
+../../common/libandroid_runtime.so_7d88088666db374aecde2fbe51bff2f4 libandroid_runtime.so
+../../common/libart.so_82c0556f4b66528e4a608c100a63b712 libart.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/maps.txt b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/maps.txt
new file mode 100644
index 0000000..7bcd383
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/maps.txt
@@ -0,0 +1,14 @@
+6fcf3000-6fd9e000 r--p 0 00:00 0   boot.oat
+6fd9e000-700fe000 r-xp ab000 00:00 0   boot.oat
+71227000-713aa000 r--p 0 00:00 0   boot-framework.oat
+713aa000-71ab4000 r-xp 183000 00:00 0   boot-framework.oat
+6193c7e000-6193c80000 r--p 0 00:00 0   app_process64
+6193c80000-6193c81000 r-xp 2000 00:00 0   app_process64
+7b590f4000-7b5a2aa000 r--p 0 00:00 0   base.odex_maps_compiled_arm64
+7b5a2aa000-7b6061c000 r-xp 11b6000 00:00 0   base.odex_maps_compiled_arm64
+7bdd800000-7bdda00000 r--p 0 00:00 0   libart.so
+7bdda00000-7bddf4a000 r-xp 200000 00:00 0   libart.so
+7e7c400000-7e7c4af000 r--p 0 00:00 0   libandroid_runtime.so
+7e7c4af000-7e7c5df000 r-xp af000 00:00 0   libandroid_runtime.so
+7e86814000-7e86850000 r--p 0 00:00 0   libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/output.txt b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/output.txt
new file mode 100644
index 0000000..97e939a
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/output.txt
@@ -0,0 +1,50 @@
+  #00 pc 000000000023b1e0  libart.so (art::ThreadList::RunCheckpoint(art::Closure*, art::Closure*)+2980)
+  #01 pc 0000000000244698  libart.so (art::ClassLinker::InitializeClass(art::Thread*, art::Handle<art::mirror::Class>, bool, bool)+2348)
+  #02 pc 0000000000243fd0  libart.so (art::ClassLinker::InitializeClass(art::Thread*, art::Handle<art::mirror::Class>, bool, bool)+612)
+  #03 pc 0000000000359730  libart.so (artAllocObjectFromCodeResolvedRegionTLAB+668)
+  #04 pc 000000000029ba88  libart.so (art_quick_alloc_object_resolved_region_tlab+104)
+  #05 pc 0000000005b4ba38  base.odex_maps_compiled_arm64 (ffs.h+4600)
+  #06 pc 0000000005b8a620  base.odex_maps_compiled_arm64 (ffs.a+528)
+  #07 pc 0000000005a1fb18  base.odex_maps_compiled_arm64 (ebhk.a+184)
+  #08 pc 00000000023600d4  base.odex_maps_compiled_arm64 (bdvg.<init>+6356)
+  #09 pc 000000000236678c  base.odex_maps_compiled_arm64 (bdvh.a+2652)
+  #10 pc 0000000002a59b00  base.odex_maps_compiled_arm64 (bdux.O+816)
+  #11 pc 0000000005afc2a4  base.odex_maps_compiled_arm64 (er.tO+244)
+  #12 pc 00000000058a6444  base.odex_maps_compiled_arm64 (gi.c+1396)
+  #13 pc 00000000058a8104  base.odex_maps_compiled_arm64 (gi.e+4708)
+  #14 pc 0000000005893ce0  base.odex_maps_compiled_arm64 (fy.aB+5952)
+  #15 pc 0000000005894e4c  base.odex_maps_compiled_arm64 (fy.aD+492)
+  #16 pc 000000000589dde0  base.odex_maps_compiled_arm64 (fy.at+544)
+  #17 pc 0000000005f09288  base.odex_maps_compiled_arm64 (hfn.F+2040)
+  #18 pc 0000000005f0a550  base.odex_maps_compiled_arm64 (hfn.K+480)
+  #19 pc 0000000005f09d3c  base.odex_maps_compiled_arm64 (hfn.J+364)
+  #20 pc 0000000005f09b48  base.odex_maps_compiled_arm64 (hfn.H+56)
+  #21 pc 00000000028b9e98  base.odex_maps_compiled_arm64 (bdtd.o+3528)
+  #22 pc 0000000003367630  base.odex_maps_compiled_arm64 (bqyr.q+4368)
+  #23 pc 0000000003354ad8  base.odex_maps_compiled_arm64 (bqye.b+3144)
+  #24 pc 00000000033828fc  base.odex_maps_compiled_arm64 (brna.e+332)
+  #25 pc 0000000003382a80  base.odex_maps_compiled_arm64 (brna.b+96)
+  #26 pc 000000000338f35c  base.odex_maps_compiled_arm64 (brsh.e+236)
+  #27 pc 000000000338f214  base.odex_maps_compiled_arm64 (brsh.d+212)
+  #28 pc 000000000338dafc  base.odex_maps_compiled_arm64 (brsf.a+76)
+  #29 pc 000000000339173c  base.odex_maps_compiled_arm64 (brsp.d+316)
+  #30 pc 000000000338f560  base.odex_maps_compiled_arm64 (brsj.xI+224)
+  #31 pc 0000000002eb4f64  base.odex_maps_compiled_arm64 (buzf.b+1380)
+  #32 pc 0000000004ce2d98  base.odex_maps_compiled_arm64 (dgvq.run+440)
+  #33 pc 00000000004eef60  boot-framework.oat (android.os.Handler.dispatchMessage+80)
+  #34 pc 00000000004f1dfc  boot-framework.oat (android.os.Looper.loopOnce+1036)
+  #35 pc 00000000004f1954  boot-framework.oat (android.os.Looper.loop+516)
+  #36 pc 00000000002ce21c  boot-framework.oat (android.app.ActivityThread.main+732)
+  #37 pc 00000000002937e8  libart.so (art_quick_invoke_static_stub+568)
+  #38 pc 00000000003026c0  libart.so (_jobject* art::InvokeMethod<(art::PointerSize)8>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long)+608)
+  #39 pc 0000000000302438  libart.so (art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*)+52)
+  #40 pc 00000000000b2f74  boot.oat (art_jni_trampoline+132)
+  #41 pc 000000000081aadc  boot-framework.oat (com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run+140)
+  #42 pc 0000000000822e18  boot-framework.oat (com.android.internal.os.ZygoteInit.main+2472)
+  #43 pc 00000000002937e8  libart.so (art_quick_invoke_static_stub+568)
+  #44 pc 00000000003dff34  libart.so (art::JValue art::InvokeWithVarArgs<_jmethodID*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+464)
+  #45 pc 00000000005c43f0  libart.so (art::JNI<true>::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+268)
+  #46 pc 00000000000b0ac4  libandroid_runtime.so (_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+120)
+  #47 pc 00000000000bc078  libandroid_runtime.so (android::AndroidRuntime::start(char const*, android::Vector<android::String8> const&, bool)+836)
+  #48 pc 000000000000258c  app_process64 (main+1336)
+  #49 pc 00000000000488d8  libc.so (__libc_init+96)
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/regs.txt b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/regs.txt
new file mode 100644
index 0000000..44b1c3d
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/regs.txt
@@ -0,0 +1,34 @@
+x0: 7e868e244c
+x1: 7e868e244c
+x2: 7bff757440
+x3: 7fe2362a48
+x4: 20
+x5: 7c1f7185a0
+x6: 7d4f6ba140
+x7: 7d4f6a6f50
+x8: 7c1f7185c0
+x9: 0
+x10: 7bff69b000
+x11: 30
+x12: f
+x13: 7d4f6cb760
+x14: 1
+x15: 16b54cbc
+x16: 7e77ab57d0
+x17: 7e86850b40
+x18: 7e8a6b6000
+x19: 7d4f6a1be0
+x20: 7c6f6de2d0
+x21: 8
+x22: 7c1f7185b8
+x23: 7d4f6d26a0
+x24: 7bff6e28c0
+x25: 1
+x26: 7bde215000
+x27: 7c1f718580
+x28: 7d5f69d220
+x29: 7fe2362b00
+lr: 7bdda3b300
+sp: 7fe2362a80
+pc: 7bdda3b1e0
+pst: 80000000
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/stack.data b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/stack.data
new file mode 100644
index 0000000..45170f3
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28644/links.txt b/libunwindstack/offline_files/maps_compiled_arm64/28644/links.txt
new file mode 100644
index 0000000..03ba1a5
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28644/links.txt
@@ -0,0 +1,4 @@
+../../common/base.odex_maps_compiled_arm64 base.odex_maps_compiled_arm64
+../../common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c boot.oat
+../../common/libart.so_82c0556f4b66528e4a608c100a63b712 libart.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28644/maps.txt b/libunwindstack/offline_files/maps_compiled_arm64/28644/maps.txt
new file mode 100644
index 0000000..b624632
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28644/maps.txt
@@ -0,0 +1,8 @@
+6fcf3000-6fd9e000 r--p 0 00:00 0   boot.oat
+6fd9e000-700fe000 r-xp ab000 00:00 0   boot.oat
+7b590f4000-7b5a2aa000 r--p 0 00:00 0   base.odex_maps_compiled_arm64
+7b5a2aa000-7b6061c000 r-xp 11b6000 00:00 0   base.odex_maps_compiled_arm64
+7bdd800000-7bdda00000 r--p 0 00:00 0   libart.so
+7bdda00000-7bddf4a000 r-xp 200000 00:00 0   libart.so
+7e86814000-7e86850000 r--p 0 00:00 0   libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28644/output.txt b/libunwindstack/offline_files/maps_compiled_arm64/28644/output.txt
new file mode 100644
index 0000000..cd61268
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28644/output.txt
@@ -0,0 +1,24 @@
+  #00 pc 000000000004c35c  libc.so (syscall+28)
+  #01 pc 0000000000411604  libart.so (art::Thread::Park(bool, long)+2300)
+  #02 pc 0000000000410790  libart.so (art::Unsafe_park(_JNIEnv*, _jobject*, unsigned char, long)+280)
+  #03 pc 00000000000adf4c  boot.oat (art_jni_trampoline+108)
+  #04 pc 00000000002f9ccc  boot.oat (java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos+1228)
+  #05 pc 00000000001235b0  boot.oat (java.util.concurrent.CountDownLatch.await+480)
+  #06 pc 0000000003e2e390  base.odex_maps_compiled_arm64 (cqqp.e+448)
+  #07 pc 0000000003ac39a4  base.odex_maps_compiled_arm64 (clys.e+1060)
+  #08 pc 0000000003ac3e4c  base.odex_maps_compiled_arm64 (clys.c+44)
+  #09 pc 0000000002b8ebb0  base.odex_maps_compiled_arm64 (bmob.call+64)
+  #10 pc 00000000054c72d8  base.odex_maps_compiled_arm64 (dgxq.a+72)
+  #11 pc 0000000004ce4d54  base.odex_maps_compiled_arm64 (dgwi.run+148)
+  #12 pc 0000000005672604  base.odex_maps_compiled_arm64 (dgxr.run+68)
+  #13 pc 0000000002f064d4  base.odex_maps_compiled_arm64 (bwkg.run+148)
+  #14 pc 000000000037b75c  boot.oat (java.util.concurrent.ThreadPoolExecutor.runWorker+988)
+  #15 pc 0000000000374f84  boot.oat (java.util.concurrent.ThreadPoolExecutor$Worker.run+68)
+  #16 pc 0000000002f041f4  base.odex_maps_compiled_arm64 (bwjw.run+564)
+  #17 pc 00000000001bf0bc  boot.oat (java.lang.Thread.run+76)
+  #18 pc 0000000000293564  libart.so (art_quick_invoke_stub+548)
+  #19 pc 00000000002c6558  libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+156)
+  #20 pc 0000000000367ea8  libart.so (art::JValue art::InvokeVirtualOrInterfaceWithJValues<art::ArtMethod*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, jvalue const*)+380)
+  #21 pc 00000000003e9b6c  libart.so (art::Thread::CreateCallback(void*)+1004)
+  #22 pc 00000000000b1920  libc.so (__pthread_start(void*)+264)
+  #23 pc 00000000000513f0  libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28644/regs.txt b/libunwindstack/offline_files/maps_compiled_arm64/28644/regs.txt
new file mode 100644
index 0000000..6ed344d
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28644/regs.txt
@@ -0,0 +1,34 @@
+x0: 7d4f6e9068
+x1: 80
+x2: 2
+x3: 7b58af6368
+x4: 0
+x5: 0
+x6: 0
+x7: 20
+x8: 62
+x9: 3b9ac0a4
+x10: 3b
+x11: 87fc4d8789a28136
+x12: 18
+x13: 9271d475358
+x14: bb1f91d69b7d5
+x15: 15f199c2
+x16: 7bde011728
+x17: 7e86860340
+x18: 7b4d7ea000
+x19: 7d4f6e9068
+x20: 7d4f6e9030
+x21: df8474ea4
+x22: 0
+x23: 44
+x24: 7bde215000
+x25: 1
+x26: 0
+x27: 7bde217000
+x28: 7b58af7000
+x29: 7b58af6390
+lr: 7bddc11608
+sp: 7b58af6360
+pc: 7e8686035c
+pst: 40000000
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28644/stack.data b/libunwindstack/offline_files/maps_compiled_arm64/28644/stack.data
new file mode 100644
index 0000000..4662d5f
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28644/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28648/links.txt b/libunwindstack/offline_files/maps_compiled_arm64/28648/links.txt
new file mode 100644
index 0000000..03ba1a5
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28648/links.txt
@@ -0,0 +1,4 @@
+../../common/base.odex_maps_compiled_arm64 base.odex_maps_compiled_arm64
+../../common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c boot.oat
+../../common/libart.so_82c0556f4b66528e4a608c100a63b712 libart.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28648/maps.txt b/libunwindstack/offline_files/maps_compiled_arm64/28648/maps.txt
new file mode 100644
index 0000000..b624632
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28648/maps.txt
@@ -0,0 +1,8 @@
+6fcf3000-6fd9e000 r--p 0 00:00 0   boot.oat
+6fd9e000-700fe000 r-xp ab000 00:00 0   boot.oat
+7b590f4000-7b5a2aa000 r--p 0 00:00 0   base.odex_maps_compiled_arm64
+7b5a2aa000-7b6061c000 r-xp 11b6000 00:00 0   base.odex_maps_compiled_arm64
+7bdd800000-7bdda00000 r--p 0 00:00 0   libart.so
+7bdda00000-7bddf4a000 r-xp 200000 00:00 0   libart.so
+7e86814000-7e86850000 r--p 0 00:00 0   libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28648/output.txt b/libunwindstack/offline_files/maps_compiled_arm64/28648/output.txt
new file mode 100644
index 0000000..e0fff1f
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28648/output.txt
@@ -0,0 +1,18 @@
+  #00 pc 000000000004c35c  libc.so (syscall+28)
+  #01 pc 0000000000411148  libart.so (art::Thread::Park(bool, long)+1088)
+  #02 pc 0000000000410790  libart.so (art::Unsafe_park(_JNIEnv*, _jobject*, unsigned char, long)+280)
+  #03 pc 00000000000adf4c  boot.oat (art_jni_trampoline+108)
+  #04 pc 000000000023c924  boot.oat (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await+756)
+  #05 pc 00000000003d966c  boot.oat (java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take+524)
+  #06 pc 0000000000394ab8  boot.oat ([DEDUPED]+40)
+  #07 pc 0000000000377574  boot.oat (java.util.concurrent.ThreadPoolExecutor.getTask+484)
+  #08 pc 000000000037b46c  boot.oat (java.util.concurrent.ThreadPoolExecutor.runWorker+236)
+  #09 pc 0000000000374f84  boot.oat (java.util.concurrent.ThreadPoolExecutor$Worker.run+68)
+  #10 pc 00000000049a8668  base.odex_maps_compiled_arm64 (czkj.run+120)
+  #11 pc 00000000001bf0bc  boot.oat (java.lang.Thread.run+76)
+  #12 pc 0000000000293564  libart.so (art_quick_invoke_stub+548)
+  #13 pc 00000000002c6558  libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+156)
+  #14 pc 0000000000367ea8  libart.so (art::JValue art::InvokeVirtualOrInterfaceWithJValues<art::ArtMethod*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, jvalue const*)+380)
+  #15 pc 00000000003e9b6c  libart.so (art::Thread::CreateCallback(void*)+1004)
+  #16 pc 00000000000b1920  libc.so (__pthread_start(void*)+264)
+  #17 pc 00000000000513f0  libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28648/regs.txt b/libunwindstack/offline_files/maps_compiled_arm64/28648/regs.txt
new file mode 100644
index 0000000..19fdcdb
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28648/regs.txt
@@ -0,0 +1,34 @@
+x0: 7d4f6effa8
+x1: 80
+x2: 2
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 20
+x8: 62
+x9: 7bff6b7e40
+x10: 430000
+x11: 10
+x12: 13758080
+x13: 10
+x14: 0
+x15: 177d30dc
+x16: 7bde011728
+x17: 7e86860340
+x18: 7b4bc6a000
+x19: 7d4f6effa8
+x20: 7d4f6eff70
+x21: 47
+x22: 0
+x23: 7bde215000
+x24: 0
+x25: 1
+x26: 0
+x27: 7bde217000
+x28: 7b586cf000
+x29: 7b586ce500
+lr: 7bddc1114c
+sp: 7b586ce4d0
+pc: 7e8686035c
+pst: 40000000
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28648/stack.data b/libunwindstack/offline_files/maps_compiled_arm64/28648/stack.data
new file mode 100644
index 0000000..dbf4731
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28648/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/links.txt b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/links.txt
new file mode 100644
index 0000000..03ba1a5
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/links.txt
@@ -0,0 +1,4 @@
+../../common/base.odex_maps_compiled_arm64 base.odex_maps_compiled_arm64
+../../common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c boot.oat
+../../common/libart.so_82c0556f4b66528e4a608c100a63b712 libart.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/maps.txt b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/maps.txt
new file mode 100644
index 0000000..c3ff7aa
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/maps.txt
@@ -0,0 +1,9 @@
+6fcf3000-6fd9e000 r--p 0 00:00 0   boot.oat
+6fd9e000-700fe000 r-xp ab000 00:00 0   boot.oat
+7b590f4000-7b5a2aa000 r--p 0 00:00 0   base.odex_maps_compiled_arm64
+7b5a2aa000-7b6061c000 r-xp 11b6000 00:00 0   base.odex_maps_compiled_arm64
+7bdd326000-7bdd800000 r--p 0 00:00 0   core-oj.jar
+7bdd800000-7bdda00000 r--p 0 00:00 0   libart.so
+7bdda00000-7bddf4a000 r-xp 200000 00:00 0   libart.so
+7e86814000-7e86850000 r--p 0 00:00 0   libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/output.txt b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/output.txt
new file mode 100644
index 0000000..3ca316f
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/output.txt
@@ -0,0 +1,20 @@
+  #00 pc 000000000004c35c  libc.so (syscall+28)
+  #01 pc 0000000000411148  libart.so (art::Thread::Park(bool, long)+1088)
+  #02 pc 0000000000410790  libart.so (art::Unsafe_park(_JNIEnv*, _jobject*, unsigned char, long)+280)
+  #03 pc 00000000000adf4c  boot.oat (art_jni_trampoline+108)
+  #04 pc 000000000023c924  boot.oat (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await+756)
+  #05 pc 000000000020aec4  libart.so (nterp_helper+7636)
+  #06 pc 00000000001faf02  core-oj.jar
+  #07 pc 000000000020a044  libart.so (nterp_helper+3924)
+  #08 pc 00000000001fab38  core-oj.jar
+  #09 pc 0000000000377574  boot.oat (java.util.concurrent.ThreadPoolExecutor.getTask+484)
+  #10 pc 000000000037b46c  boot.oat (java.util.concurrent.ThreadPoolExecutor.runWorker+236)
+  #11 pc 0000000000374f84  boot.oat (java.util.concurrent.ThreadPoolExecutor$Worker.run+68)
+  #12 pc 0000000002f041f4  base.odex_maps_compiled_arm64 (bwjw.run+564)
+  #13 pc 00000000001bf0bc  boot.oat (java.lang.Thread.run+76)
+  #14 pc 0000000000293564  libart.so (art_quick_invoke_stub+548)
+  #15 pc 00000000002c6558  libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+156)
+  #16 pc 0000000000367ea8  libart.so (art::JValue art::InvokeVirtualOrInterfaceWithJValues<art::ArtMethod*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, jvalue const*)+380)
+  #17 pc 00000000003e9b6c  libart.so (art::Thread::CreateCallback(void*)+1004)
+  #18 pc 00000000000b1920  libc.so (__pthread_start(void*)+264)
+  #19 pc 00000000000513f0  libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/regs.txt b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/regs.txt
new file mode 100644
index 0000000..6428f11
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/regs.txt
@@ -0,0 +1,34 @@
+x0: 7d4f6f8ab8
+x1: 80
+x2: 2
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 20
+x8: 62
+x9: 7bff6b7e40
+x10: 430000
+x11: 10
+x12: 4
+x13: 1ac8703d
+x14: 7b57dcf610
+x15: 177d143c
+x16: 7bde011728
+x17: 7e86860340
+x18: 7b44f5e000
+x19: 7d4f6f8ab8
+x20: 7d4f6f8a80
+x21: 47
+x22: 0
+x23: 7bde215000
+x24: 0
+x25: 1
+x26: 0
+x27: 7bde217000
+x28: 7b57dd0000
+x29: 7b57dcf3c0
+lr: 7bddc1114c
+sp: 7b57dcf390
+pc: 7e8686035c
+pst: 40000000
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/stack.data b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/stack.data
new file mode 100644
index 0000000..a44fcdd
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28667/links.txt b/libunwindstack/offline_files/maps_compiled_arm64/28667/links.txt
new file mode 100644
index 0000000..3f9febc
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28667/links.txt
@@ -0,0 +1 @@
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28667/maps.txt b/libunwindstack/offline_files/maps_compiled_arm64/28667/maps.txt
new file mode 100644
index 0000000..b9a4bb8
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28667/maps.txt
@@ -0,0 +1,3 @@
+7b38a95000-7b38ef0000 r-xp 1000 00:00 0   CronetDynamite.apk
+7e86814000-7e86850000 r--p 0 00:00 0   libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28667/output.txt b/libunwindstack/offline_files/maps_compiled_arm64/28667/output.txt
new file mode 100644
index 0000000..7a65fc6
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28667/output.txt
@@ -0,0 +1,4 @@
+  #00 pc 000000000004c35c  libc.so (syscall+28)
+  #01 pc 0000000000050980  libc.so (__futex_wait_ex(void volatile*, bool, int, bool, timespec const*)+144)
+  #02 pc 00000000000b0c64  libc.so (pthread_cond_timedwait+140)
+  #03 pc 000000000027550c  CronetDynamite.apk (offset 0x1000)
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28667/regs.txt b/libunwindstack/offline_files/maps_compiled_arm64/28667/regs.txt
new file mode 100644
index 0000000..9416bb2
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28667/regs.txt
@@ -0,0 +1,34 @@
+x0: 7b35894a10
+x1: 89
+x2: 2
+x3: 7b358948e0
+x4: 0
+x5: ffffffff
+x6: ffffffff
+x7: 2e65746163696669
+x8: 62
+x9: 89
+x10: 9
+x11: 0
+x12: 0
+x13: 92708000f58
+x14: 23ef2f8bd6c2ce
+x15: 15f199c2
+x16: 7e868d40a8
+x17: 7e86860340
+x18: 7b34300000
+x19: 2
+x20: 7b358948e0
+x21: 7b35894a10
+x22: 89
+x23: 7b35894fb0
+x24: 7b35895000
+x25: 4b
+x26: 7c2f6b0000
+x27: 7b38b4a5b0
+x28: 1
+x29: 7b35894840
+lr: 7e86864984
+sp: 7b35894820
+pc: 7e8686035c
+pst: 40000000
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28667/stack.data b/libunwindstack/offline_files/maps_compiled_arm64/28667/stack.data
new file mode 100644
index 0000000..5f8f82c
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28667/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/offset_arm/libc.so.gz b/libunwindstack/offline_files/offset_arm/libc.so.gz
new file mode 100644
index 0000000..e592dfb
--- /dev/null
+++ b/libunwindstack/offline_files/offset_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/offset_arm/libunwindstack_test.gz b/libunwindstack/offline_files/offset_arm/libunwindstack_test.gz
new file mode 100644
index 0000000..c27ce1a
--- /dev/null
+++ b/libunwindstack/offline_files/offset_arm/libunwindstack_test.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/maps.txt b/libunwindstack/offline_files/offset_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/offset_arm/maps.txt
rename to libunwindstack/offline_files/offset_arm/maps.txt
diff --git a/libunwindstack/offline_files/offset_arm/output.txt b/libunwindstack/offline_files/offset_arm/output.txt
new file mode 100644
index 0000000..36bd388
--- /dev/null
+++ b/libunwindstack/offline_files/offset_arm/output.txt
@@ -0,0 +1,19 @@
+  #00 pc 0032bfa0  libunwindstack_test (SignalInnerFunction+40)
+  #01 pc 0032bfeb  libunwindstack_test (SignalMiddleFunction+2)
+  #02 pc 0032bff3  libunwindstack_test (SignalOuterFunction+2)
+  #03 pc 0032fed3  libunwindstack_test (unwindstack::SignalCallerHandler(int, siginfo*, void*)+26)
+  #04 pc 0002652c  libc.so (__restore)
+  #05 pc 00000000  <unknown>
+  #06 pc 0032c2d9  libunwindstack_test (InnerFunction+736)
+  #07 pc 0032cc4f  libunwindstack_test (MiddleFunction+42)
+  #08 pc 0032cc81  libunwindstack_test (OuterFunction+42)
+  #09 pc 0032e547  libunwindstack_test (unwindstack::RemoteThroughSignal(int, unsigned int)+270)
+  #10 pc 0032ed99  libunwindstack_test (unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+16)
+  #11 pc 00354453  libunwindstack_test (testing::Test::Run()+154)
+  #12 pc 00354de7  libunwindstack_test (testing::TestInfo::Run()+194)
+  #13 pc 00355105  libunwindstack_test (testing::TestCase::Run()+180)
+  #14 pc 0035a215  libunwindstack_test (testing::internal::UnitTestImpl::RunAllTests()+664)
+  #15 pc 00359f4f  libunwindstack_test (testing::UnitTest::Run()+110)
+  #16 pc 0034d3db  libunwindstack_test (main+38)
+  #17 pc 00092c0d  libc.so (__libc_init+48)
+  #18 pc 0004202f  libunwindstack_test (_start_main+38)
diff --git a/libunwindstack/tests/files/offline/offset_arm/regs.txt b/libunwindstack/offline_files/offset_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/offset_arm/regs.txt
rename to libunwindstack/offline_files/offset_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack0.data b/libunwindstack/offline_files/offset_arm/stack0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/offset_arm/stack0.data
rename to libunwindstack/offline_files/offset_arm/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack1.data b/libunwindstack/offline_files/offset_arm/stack1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/offset_arm/stack1.data
rename to libunwindstack/offline_files/offset_arm/stack1.data
Binary files differ
diff --git a/libunwindstack/offline_files/pauth_pc_arm64/libc.so.gz b/libunwindstack/offline_files/pauth_pc_arm64/libc.so.gz
new file mode 100644
index 0000000..48397a0
--- /dev/null
+++ b/libunwindstack/offline_files/pauth_pc_arm64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/pauth_pc_arm64/maps.txt b/libunwindstack/offline_files/pauth_pc_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/pauth_pc_arm64/maps.txt
rename to libunwindstack/offline_files/pauth_pc_arm64/maps.txt
diff --git a/libunwindstack/offline_files/pauth_pc_arm64/output.txt b/libunwindstack/offline_files/pauth_pc_arm64/output.txt
new file mode 100644
index 0000000..a41d36a
--- /dev/null
+++ b/libunwindstack/offline_files/pauth_pc_arm64/output.txt
@@ -0,0 +1,26 @@
+  #00 pc 00000000000404a8  toybox (do_print+28)
+  #01 pc 0000000000040270  toybox (do_find+5072)
+  #02 pc 000000000002c640  toybox (dirtree_handle_callback+40)
+  #03 pc 000000000002c588  toybox (dirtree_recurse+200)
+  #04 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)
+  #05 pc 000000000002c588  toybox (dirtree_recurse+200)
+  #06 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)
+  #07 pc 000000000002c588  toybox (dirtree_recurse+200)
+  #08 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)
+  #09 pc 000000000002c588  toybox (dirtree_recurse+200)
+  #10 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)
+  #11 pc 000000000002c588  toybox (dirtree_recurse+200)
+  #12 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)
+  #13 pc 000000000002c588  toybox (dirtree_recurse+200)
+  #14 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)
+  #15 pc 000000000002c588  toybox (dirtree_recurse+200)
+  #16 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)
+  #17 pc 000000000002c588  toybox (dirtree_recurse+200)
+  #18 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)
+  #19 pc 000000000002c588  toybox (dirtree_recurse+200)
+  #20 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)
+  #21 pc 000000000003ee54  toybox (find_main+272)
+  #22 pc 0000000000034834  toybox (toy_exec_which+88)
+  #23 pc 00000000000342cc  toybox (toybox_main+148)
+  #24 pc 00000000000348b4  toybox (main+120)
+  #25 pc 00000000000499d8  libc.so (__libc_init+112)
diff --git a/libunwindstack/tests/files/offline/pauth_pc_arm64/regs.txt b/libunwindstack/offline_files/pauth_pc_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/pauth_pc_arm64/regs.txt
rename to libunwindstack/offline_files/pauth_pc_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/pauth_pc_arm64/stack.data b/libunwindstack/offline_files/pauth_pc_arm64/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/pauth_pc_arm64/stack.data
rename to libunwindstack/offline_files/pauth_pc_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/pauth_pc_arm64/toybox.gz b/libunwindstack/offline_files/pauth_pc_arm64/toybox.gz
new file mode 100644
index 0000000..a8edec9
--- /dev/null
+++ b/libunwindstack/offline_files/pauth_pc_arm64/toybox.gz
Binary files differ
diff --git a/libunwindstack/offline_files/photos_reset_arm64/links.txt b/libunwindstack/offline_files/photos_reset_arm64/links.txt
new file mode 100644
index 0000000..aadfa08
--- /dev/null
+++ b/libunwindstack/offline_files/photos_reset_arm64/links.txt
@@ -0,0 +1,3 @@
+../common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c boot.oat
+../common/libart.so_82c0556f4b66528e4a608c100a63b712 libart.so
+../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/photos_reset_arm64/maps.txt b/libunwindstack/offline_files/photos_reset_arm64/maps.txt
new file mode 100644
index 0000000..49237cc
--- /dev/null
+++ b/libunwindstack/offline_files/photos_reset_arm64/maps.txt
@@ -0,0 +1,8 @@
+6fcf3000-6fd9e000 r--p 0 00:00 0   boot.oat
+6fd9e000-700fe000 r-xp ab000 00:00 0   boot.oat
+7b64f9d000-7b65286000 r--p 0 00:00 0   base.apk!classes4.dex
+7b65c00000-7b66307000 r--p 0 00:00 0   base.apk!classes2.dex
+7bdd800000-7bdda00000 r--p 0 00:00 0   libart.so
+7bdda00000-7bddf4a000 r-xp 200000 00:00 0   libart.so
+7e86814000-7e86850000 r--p 0 00:00 0   libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/photos_reset_arm64/output.txt b/libunwindstack/offline_files/photos_reset_arm64/output.txt
new file mode 100644
index 0000000..d046c00
--- /dev/null
+++ b/libunwindstack/offline_files/photos_reset_arm64/output.txt
@@ -0,0 +1,20 @@
+  #00 pc 000000000004c35c  libc.so (syscall+28)
+  #01 pc 0000000000353010  libart.so (art::ConditionVariable::WaitHoldingLocks(art::Thread*)+148)
+  #02 pc 000000000031d7dc  libart.so (art::Monitor::Wait(art::Thread*, long, int, bool, art::ThreadState)+1228)
+  #03 pc 000000000031ce90  libart.so (art::Monitor::Wait(art::Thread*, art::ObjPtr<art::mirror::Object>, long, int, bool, art::ThreadState)+524)
+  #04 pc 00000000000ab35c  boot.oat (art_jni_trampoline+108)
+  #05 pc 00000000000cc278  boot.oat (java.lang.ref.ReferenceQueue.remove+360)
+  #06 pc 00000000000cc0ec  boot.oat (java.lang.ref.ReferenceQueue.remove+44)
+  #07 pc 000000000020a0a0  libart.so (nterp_helper+4016)
+  #08 pc 0000000000404264  base.apk!classes2.dex
+  #09 pc 000000000037b75c  boot.oat (java.util.concurrent.ThreadPoolExecutor.runWorker+988)
+  #10 pc 0000000000374f84  boot.oat (java.util.concurrent.ThreadPoolExecutor$Worker.run+68)
+  #11 pc 000000000020aec4  libart.so (nterp_helper+7636)
+  #12 pc 00000000000f2a24  base.apk!classes4.dex
+  #13 pc 00000000001bf0bc  boot.oat (java.lang.Thread.run+76)
+  #14 pc 0000000000293564  libart.so (art_quick_invoke_stub+548)
+  #15 pc 00000000002c6558  libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+156)
+  #16 pc 0000000000367ea8  libart.so (art::JValue art::InvokeVirtualOrInterfaceWithJValues<art::ArtMethod*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, jvalue const*)+380)
+  #17 pc 00000000003e9b6c  libart.so (art::Thread::CreateCallback(void*)+1004)
+  #18 pc 00000000000b1920  libc.so (__pthread_start(void*)+264)
+  #19 pc 00000000000513f0  libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/photos_reset_arm64/regs.txt b/libunwindstack/offline_files/photos_reset_arm64/regs.txt
new file mode 100644
index 0000000..77c2e78
--- /dev/null
+++ b/libunwindstack/offline_files/photos_reset_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 7bff6e4bb0
+x1: 80
+x2: 0
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 3030303030303034
+x8: 62
+x9: 87fc4d8789a28136
+x10: 0
+x11: ff6fd6f8
+x12: ffff00000eff
+x13: 106b9c07
+x14: 7b5d33e730
+x15: 0
+x16: 7bde011728
+x17: 7e86860340
+x18: 7b5ce14000
+x19: 7bff6e4ba0
+x20: 7d4f6eac00
+x21: 0
+x22: 7bff6e4bb0
+x23: 0
+x24: 7c0f6ccb50
+x25: 7b5d33f000
+x26: 1
+x27: 7d2f6b2f30
+x28: 7d2f6b2f18
+x29: 7b5d33e090
+lr: 7bddb53014
+sp: 7b5d33e080
+pc: 7e8686035c
+pst: 60000000
diff --git a/libunwindstack/offline_files/photos_reset_arm64/stack.data b/libunwindstack/offline_files/photos_reset_arm64/stack.data
new file mode 100644
index 0000000..0f576e3
--- /dev/null
+++ b/libunwindstack/offline_files/photos_reset_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk b/libunwindstack/offline_files/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
rename to libunwindstack/offline_files/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
Binary files differ
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_arm64/libc.so.gz b/libunwindstack/offline_files/shared_lib_in_apk_arm64/libc.so.gz
new file mode 100644
index 0000000..259534f
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_arm64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_arm64/linker64.gz b/libunwindstack/offline_files/shared_lib_in_apk_arm64/linker64.gz
new file mode 100644
index 0000000..abcaa33
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_arm64/linker64.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt b/libunwindstack/offline_files/shared_lib_in_apk_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt
rename to libunwindstack/offline_files/shared_lib_in_apk_arm64/maps.txt
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_arm64/output.txt b/libunwindstack/offline_files/shared_lib_in_apk_arm64/output.txt
new file mode 100644
index 0000000..d7bbf39
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_arm64/output.txt
@@ -0,0 +1,7 @@
+  #00 pc 000000000014ccbc  linker64 (__dl_syscall+28)
+  #01 pc 000000000005426c  linker64 (__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)
+  #02 pc 00000000000008c0  vdso.so (__kernel_rt_sigreturn)
+  #03 pc 00000000000846f4  libc.so (abort+172)
+  #04 pc 0000000000084ad4  libc.so (__assert2+36)
+  #05 pc 000000000003d5b4  ANGLEPrebuilt.apk!libfeature_support_angle.so (offset 0x4000) (ANGLEGetUtilityAPI+56)
+  #06 pc 000000000007fe68  libc.so (__libc_init)
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt b/libunwindstack/offline_files/shared_lib_in_apk_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt
rename to libunwindstack/offline_files/shared_lib_in_apk_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data b/libunwindstack/offline_files/shared_lib_in_apk_arm64/stack0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data
rename to libunwindstack/offline_files/shared_lib_in_apk_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data b/libunwindstack/offline_files/shared_lib_in_apk_arm64/stack1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data
rename to libunwindstack/offline_files/shared_lib_in_apk_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_arm64/vdso.so.gz b/libunwindstack/offline_files/shared_lib_in_apk_arm64/vdso.so.gz
new file mode 100644
index 0000000..e08b7cc
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_arm64/vdso.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/lib_mem.data
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data
rename to libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/lib_mem.data
Binary files differ
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/libc.so.gz b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/libc.so.gz
new file mode 100644
index 0000000..31e71ca
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/linker64.gz b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/linker64.gz
new file mode 100644
index 0000000..0f1424e
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/linker64.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt
rename to libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/maps.txt
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/output.txt b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/output.txt
new file mode 100644
index 0000000..c733893
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/output.txt
@@ -0,0 +1,7 @@
+  #00 pc 000000000014ccbc  linker64 (__dl_syscall+28)
+  #01 pc 000000000005426c  linker64 (__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)
+  #02 pc 00000000000008c0  vdso.so (__kernel_rt_sigreturn)
+  #03 pc 00000000000846f4  libc.so (abort+172)
+  #04 pc 0000000000084ad4  libc.so (__assert2+36)
+  #05 pc 000000000003d5b4  ANGLEPrebuilt.apk (offset 0x21d5000)
+  #06 pc 000000000007fe68  libc.so (__libc_init)
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt
rename to libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/stack0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data
rename to libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/stack1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data
rename to libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/vdso.so.gz b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/vdso.so.gz
new file mode 100644
index 0000000..bf06ecf
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/vdso.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/libc.so.gz b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/libc.so.gz
new file mode 100644
index 0000000..cac2635
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt
rename to libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/maps.txt
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/output.txt b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/output.txt
new file mode 100644
index 0000000..cb1978b
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/output.txt
@@ -0,0 +1,13 @@
+  #00 pc 00000000000814bc  libc.so (syscall+28)
+  #01 pc 00000000008cdf5c  test.apk (offset 0x5000)
+  #02 pc 00000000008cde9c  test.apk (offset 0x5000)
+  #03 pc 00000000008cdd70  test.apk (offset 0x5000)
+  #04 pc 00000000008ce408  test.apk (offset 0x5000)
+  #05 pc 00000000008ce8d8  test.apk (offset 0x5000)
+  #06 pc 00000000008ce814  test.apk (offset 0x5000)
+  #07 pc 00000000008bcf60  test.apk (offset 0x5000)
+  #08 pc 0000000000133024  test.apk (offset 0x5000)
+  #09 pc 0000000000134ad0  test.apk (offset 0x5000)
+  #10 pc 0000000000134b64  test.apk (offset 0x5000)
+  #11 pc 00000000000e406c  libc.so (__pthread_start(void*)+36)
+  #12 pc 0000000000085e18  libc.so (__start_thread+64)
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt
rename to libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data
rename to libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/test.apk
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk
rename to libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/test.apk
Binary files differ
diff --git a/libunwindstack/offline_files/signal_fde_x86/libc.so.gz b/libunwindstack/offline_files/signal_fde_x86/libc.so.gz
new file mode 100644
index 0000000..af44763
--- /dev/null
+++ b/libunwindstack/offline_files/signal_fde_x86/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/signal_fde_x86/libunwindstack_test.gz b/libunwindstack/offline_files/signal_fde_x86/libunwindstack_test.gz
new file mode 100644
index 0000000..92e59ea
--- /dev/null
+++ b/libunwindstack/offline_files/signal_fde_x86/libunwindstack_test.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/maps.txt b/libunwindstack/offline_files/signal_fde_x86/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86/maps.txt
rename to libunwindstack/offline_files/signal_fde_x86/maps.txt
diff --git a/libunwindstack/offline_files/signal_fde_x86/output.txt b/libunwindstack/offline_files/signal_fde_x86/output.txt
new file mode 100644
index 0000000..eb1a0ec
--- /dev/null
+++ b/libunwindstack/offline_files/signal_fde_x86/output.txt
@@ -0,0 +1,20 @@
+  #00 pc 007914d9  libunwindstack_test (SignalInnerFunction+25)
+  #01 pc 007914fc  libunwindstack_test (SignalMiddleFunction+28)
+  #02 pc 0079152c  libunwindstack_test (SignalOuterFunction+28)
+  #03 pc 0079af62  libunwindstack_test (unwindstack::SignalCallerHandler(int, siginfo*, void*)+50)
+  #04 pc 00058fb0  libc.so (__restore)
+  #05 pc 00000000  <unknown>
+  #06 pc 0079161a  libunwindstack_test (InnerFunction+218)
+  #07 pc 007923aa  libunwindstack_test (MiddleFunction+42)
+  #08 pc 007923ea  libunwindstack_test (OuterFunction+42)
+  #09 pc 00797444  libunwindstack_test (unwindstack::RemoteThroughSignal(int, unsigned int)+868)
+  #10 pc 007985b8  libunwindstack_test (unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+56)
+  #11 pc 00817a19  libunwindstack_test
+  #12 pc 008178c5  libunwindstack_test (testing::Test::Run()+277)
+  #13 pc 00818d3e  libunwindstack_test (testing::TestInfo::Run()+318)
+  #14 pc 008198b4  libunwindstack_test (testing::TestSuite::Run()+436)
+  #15 pc 00828cb0  libunwindstack_test (testing::internal::UnitTestImpl::RunAllTests()+1216)
+  #16 pc 0082870f  libunwindstack_test (testing::UnitTest::Run()+367)
+  #17 pc 0084031e  libunwindstack_test (IsolateMain+2334)
+  #18 pc 0083f9e9  libunwindstack_test (main+41)
+  #19 pc 00050646  libc.so (__libc_init+118)
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/regs.txt b/libunwindstack/offline_files/signal_fde_x86/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86/regs.txt
rename to libunwindstack/offline_files/signal_fde_x86/regs.txt
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/stack0.data b/libunwindstack/offline_files/signal_fde_x86/stack0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86/stack0.data
rename to libunwindstack/offline_files/signal_fde_x86/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/stack1.data b/libunwindstack/offline_files/signal_fde_x86/stack1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86/stack1.data
rename to libunwindstack/offline_files/signal_fde_x86/stack1.data
Binary files differ
diff --git a/libunwindstack/offline_files/signal_fde_x86_64/libc.so.gz b/libunwindstack/offline_files/signal_fde_x86_64/libc.so.gz
new file mode 100644
index 0000000..048cb54
--- /dev/null
+++ b/libunwindstack/offline_files/signal_fde_x86_64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/signal_fde_x86_64/libunwindstack_test.gz b/libunwindstack/offline_files/signal_fde_x86_64/libunwindstack_test.gz
new file mode 100644
index 0000000..e60eb34
--- /dev/null
+++ b/libunwindstack/offline_files/signal_fde_x86_64/libunwindstack_test.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/maps.txt b/libunwindstack/offline_files/signal_fde_x86_64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86_64/maps.txt
rename to libunwindstack/offline_files/signal_fde_x86_64/maps.txt
diff --git a/libunwindstack/offline_files/signal_fde_x86_64/output.txt b/libunwindstack/offline_files/signal_fde_x86_64/output.txt
new file mode 100644
index 0000000..6ac6e3a
--- /dev/null
+++ b/libunwindstack/offline_files/signal_fde_x86_64/output.txt
@@ -0,0 +1,18 @@
+  #00 pc 000000000058415b  libunwindstack_test (SignalInnerFunction+11)
+  #01 pc 0000000000584168  libunwindstack_test (SignalMiddleFunction+8)
+  #02 pc 0000000000584178  libunwindstack_test (SignalOuterFunction+8)
+  #03 pc 000000000058ac77  libunwindstack_test (unwindstack::SignalCallerHandler(int, siginfo*, void*)+23)
+  #04 pc 0000000000057d10  libc.so (__restore_rt)
+  #05 pc 0000000000000000  <unknown>
+  #06 pc 0000000000584244  libunwindstack_test (InnerFunction+196)
+  #07 pc 0000000000584b44  libunwindstack_test (MiddleFunction+20)
+  #08 pc 0000000000584b64  libunwindstack_test (OuterFunction+20)
+  #09 pc 0000000000588457  libunwindstack_test (unwindstack::RemoteThroughSignal(int, unsigned int)+583)
+  #10 pc 0000000000588f67  libunwindstack_test (unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+23)
+  #11 pc 00000000005d9c38  libunwindstack_test (testing::Test::Run()+216)
+  #12 pc 00000000005daf9a  libunwindstack_test (testing::TestInfo::Run()+266)
+  #13 pc 00000000005dba46  libunwindstack_test (testing::TestSuite::Run()+390)
+  #14 pc 00000000005ea4c6  libunwindstack_test (testing::internal::UnitTestImpl::RunAllTests()+1190)
+  #15 pc 00000000005e9f61  libunwindstack_test (testing::UnitTest::Run()+337)
+  #16 pc 0000000000600155  libunwindstack_test (IsolateMain+2037)
+  #17 pc 000000000004e405  libc.so (__libc_init+101)
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/regs.txt b/libunwindstack/offline_files/signal_fde_x86_64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86_64/regs.txt
rename to libunwindstack/offline_files/signal_fde_x86_64/regs.txt
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/stack0.data b/libunwindstack/offline_files/signal_fde_x86_64/stack0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86_64/stack0.data
rename to libunwindstack/offline_files/signal_fde_x86_64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/stack1.data b/libunwindstack/offline_files/signal_fde_x86_64/stack1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86_64/stack1.data
rename to libunwindstack/offline_files/signal_fde_x86_64/stack1.data
Binary files differ
diff --git a/libunwindstack/offline_files/signal_load_bias_arm/libc.so.gz b/libunwindstack/offline_files/signal_load_bias_arm/libc.so.gz
new file mode 100644
index 0000000..8b9d517
--- /dev/null
+++ b/libunwindstack/offline_files/signal_load_bias_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/signal_load_bias_arm/libunwindstack_unit_test.gz b/libunwindstack/offline_files/signal_load_bias_arm/libunwindstack_unit_test.gz
new file mode 100644
index 0000000..bd28d05
--- /dev/null
+++ b/libunwindstack/offline_files/signal_load_bias_arm/libunwindstack_unit_test.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/maps.txt b/libunwindstack/offline_files/signal_load_bias_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_load_bias_arm/maps.txt
rename to libunwindstack/offline_files/signal_load_bias_arm/maps.txt
diff --git a/libunwindstack/offline_files/signal_load_bias_arm/output.txt b/libunwindstack/offline_files/signal_load_bias_arm/output.txt
new file mode 100644
index 0000000..a4e2d6c
--- /dev/null
+++ b/libunwindstack/offline_files/signal_load_bias_arm/output.txt
@@ -0,0 +1,17 @@
+  #00 pc 0029ef9e  libunwindstack_unit_test (SignalInnerFunction+10)
+  #01 pc 0029efa7  libunwindstack_unit_test (SignalMiddleFunction+2)
+  #02 pc 0029efaf  libunwindstack_unit_test (SignalOuterFunction+2)
+  #03 pc 002a280b  libunwindstack_unit_test (unwindstack::SignalCallerHandler(int, siginfo*, void*)+10)
+  #04 pc 00058bd4  libc.so (__restore)
+  #05 pc 0029f01e  libunwindstack_unit_test (InnerFunction+106)
+  #06 pc 0029f633  libunwindstack_unit_test (MiddleFunction+16)
+  #07 pc 0029f64b  libunwindstack_unit_test (OuterFunction+16)
+  #08 pc 002a1711  libunwindstack_unit_test (unwindstack::RemoteThroughSignal(int, unsigned int)+260)
+  #09 pc 002a1603  libunwindstack_unit_test (unwindstack::UnwindTest_remote_through_signal_Test::TestBody()+10)
+  #10 pc 002c8fe3  libunwindstack_unit_test (testing::Test::Run()+130)
+  #11 pc 002c9b25  libunwindstack_unit_test (testing::TestInfo::Run()+184)
+  #12 pc 002c9e27  libunwindstack_unit_test (testing::TestSuite::Run()+202)
+  #13 pc 002d193d  libunwindstack_unit_test (testing::internal::UnitTestImpl::RunAllTests()+660)
+  #14 pc 002d160b  libunwindstack_unit_test (testing::UnitTest::Run()+134)
+  #15 pc 002de035  libunwindstack_unit_test (IsolateMain+680)
+  #16 pc 00058155  libc.so (__libc_init+68)
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/regs.txt b/libunwindstack/offline_files/signal_load_bias_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_load_bias_arm/regs.txt
rename to libunwindstack/offline_files/signal_load_bias_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/stack0.data b/libunwindstack/offline_files/signal_load_bias_arm/stack0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_load_bias_arm/stack0.data
rename to libunwindstack/offline_files/signal_load_bias_arm/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/stack1.data b/libunwindstack/offline_files/signal_load_bias_arm/stack1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_load_bias_arm/stack1.data
rename to libunwindstack/offline_files/signal_load_bias_arm/stack1.data
Binary files differ
diff --git a/libunwindstack/offline_files/straddle_arm/libbase.so.gz b/libunwindstack/offline_files/straddle_arm/libbase.so.gz
new file mode 100644
index 0000000..94093a5
--- /dev/null
+++ b/libunwindstack/offline_files/straddle_arm/libbase.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/straddle_arm/libc.so.gz b/libunwindstack/offline_files/straddle_arm/libc.so.gz
new file mode 100644
index 0000000..415e83f
--- /dev/null
+++ b/libunwindstack/offline_files/straddle_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm/maps.txt b/libunwindstack/offline_files/straddle_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm/maps.txt
rename to libunwindstack/offline_files/straddle_arm/maps.txt
diff --git a/libunwindstack/offline_files/straddle_arm/output.txt b/libunwindstack/offline_files/straddle_arm/output.txt
new file mode 100644
index 0000000..1a26405
--- /dev/null
+++ b/libunwindstack/offline_files/straddle_arm/output.txt
@@ -0,0 +1,4 @@
+  #00 pc 0001a9f8  libc.so (abort+64)
+  #01 pc 00006a1b  libbase.so (android::base::DefaultAborter(char const*)+6)
+  #02 pc 00007441  libbase.so (android::base::LogMessage::~LogMessage()+748)
+  #03 pc 00015147  /does/not/exist/libhidlbase.so
diff --git a/libunwindstack/tests/files/offline/straddle_arm/regs.txt b/libunwindstack/offline_files/straddle_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm/regs.txt
rename to libunwindstack/offline_files/straddle_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/straddle_arm/stack.data b/libunwindstack/offline_files/straddle_arm/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm/stack.data
rename to libunwindstack/offline_files/straddle_arm/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/straddle_arm64/libunwindstack_test.gz b/libunwindstack/offline_files/straddle_arm64/libunwindstack_test.gz
new file mode 100644
index 0000000..82460c8
--- /dev/null
+++ b/libunwindstack/offline_files/straddle_arm64/libunwindstack_test.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/maps.txt b/libunwindstack/offline_files/straddle_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm64/maps.txt
rename to libunwindstack/offline_files/straddle_arm64/maps.txt
diff --git a/libunwindstack/offline_files/straddle_arm64/output.txt b/libunwindstack/offline_files/straddle_arm64/output.txt
new file mode 100644
index 0000000..c54daec
--- /dev/null
+++ b/libunwindstack/offline_files/straddle_arm64/output.txt
@@ -0,0 +1,6 @@
+  #00 pc 0000000000429fd8  libunwindstack_test (SignalInnerFunction+24)
+  #01 pc 000000000042a078  libunwindstack_test (SignalMiddleFunction+8)
+  #02 pc 000000000042a08c  libunwindstack_test (SignalOuterFunction+8)
+  #03 pc 000000000042d8fc  libunwindstack_test (unwindstack::RemoteThroughSignal(int, unsigned int)+20)
+  #04 pc 000000000042d8d8  libunwindstack_test (unwindstack::UnwindTest_remote_through_signal_Test::TestBody()+32)
+  #05 pc 0000000000455d70  libunwindstack_test (testing::Test::Run()+392)
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/regs.txt b/libunwindstack/offline_files/straddle_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm64/regs.txt
rename to libunwindstack/offline_files/straddle_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/stack.data b/libunwindstack/offline_files/straddle_arm64/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm64/stack.data
rename to libunwindstack/offline_files/straddle_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/libbinder.so.gz b/libunwindstack/offline_files/youtube_compiled_arm64/libbinder.so.gz
new file mode 100644
index 0000000..0e52d42
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/libbinder.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/libcodec2_client.so.gz b/libunwindstack/offline_files/youtube_compiled_arm64/libcodec2_client.so.gz
new file mode 100644
index 0000000..19a563e
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/libcodec2_client.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/libgui.so.gz b/libunwindstack/offline_files/youtube_compiled_arm64/libgui.so.gz
new file mode 100644
index 0000000..45b4068
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/libgui.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/libsfplugin_ccodec.so.gz b/libunwindstack/offline_files/youtube_compiled_arm64/libsfplugin_ccodec.so.gz
new file mode 100644
index 0000000..3db0b38
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/libsfplugin_ccodec.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/libstagefright.so.gz b/libunwindstack/offline_files/youtube_compiled_arm64/libstagefright.so.gz
new file mode 100644
index 0000000..34398ad
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/libstagefright.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/libstagefright_foundation.so.gz b/libunwindstack/offline_files/youtube_compiled_arm64/libstagefright_foundation.so.gz
new file mode 100644
index 0000000..770de8a
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/libstagefright_foundation.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/links.txt b/libunwindstack/offline_files/youtube_compiled_arm64/links.txt
new file mode 100644
index 0000000..72892f4
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/links.txt
@@ -0,0 +1,3 @@
+../common/libandroid_runtime.so_7d88088666db374aecde2fbe51bff2f4 libandroid_runtime.so
+../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
+../common/libutils.so_b8aa8db7e6895d0ba92398ca5d3ed2d4 libutils.so
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/maps.txt b/libunwindstack/offline_files/youtube_compiled_arm64/maps.txt
new file mode 100644
index 0000000..0d595c3
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/maps.txt
@@ -0,0 +1,18 @@
+7e6f820000-7e6f82d000 r--p 0 00:00 0   libutils.so
+7e6f82d000-7e6f839000 r-xp d000 00:00 0   libutils.so
+7e6f881000-7e6f895000 r--p 0 00:00 0   libstagefright_foundation.so
+7e6f895000-7e6f8b4000 r-xp 14000 00:00 0   libstagefright_foundation.so
+7e71cab000-7e71d2f000 r--p 0 00:00 0   libgui.so
+7e71d2f000-7e71dce000 r-xp 84000 00:00 0   libgui.so
+7e72942000-7e7299b000 r--p 0 00:00 0   libbinder.so
+7e7299b000-7e729ec000 r-xp 59000 00:00 0   libbinder.so
+7e77b5e000-7e77b86000 r--p 0 00:00 0   libsfplugin_ccodec.so
+7e77b86000-7e77bf6000 r-xp 28000 00:00 0   libsfplugin_ccodec.so
+7e7a40c000-7e7a48a000 r--p 0 00:00 0   libstagefright.so
+7e7a48a000-7e7a5b8000 r-xp 7e000 00:00 0   libstagefright.so
+7e7c400000-7e7c4af000 r--p 0 00:00 0   libandroid_runtime.so
+7e7c4af000-7e7c5df000 r-xp af000 00:00 0   libandroid_runtime.so
+7e84114000-7e84121000 r--p 0 00:00 0   libcodec2_client.so
+7e84121000-7e8413c000 r-xp d000 00:00 0   libcodec2_client.so
+7e86814000-7e86850000 r--p 0 00:00 0   libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/output.txt b/libunwindstack/offline_files/youtube_compiled_arm64/output.txt
new file mode 100644
index 0000000..d832383
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/output.txt
@@ -0,0 +1,23 @@
+  #00 pc 000000000009d5e4  libc.so (__ioctl+4)
+  #01 pc 00000000000593e8  libc.so (ioctl+152)
+  #02 pc 000000000005ddac  libbinder.so (android::IPCThreadState::transact(int, unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+920)
+  #03 pc 000000000005d7d8  libbinder.so (android::BpBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+176)
+  #04 pc 00000000000aa0e4  libgui.so (android::BpSurfaceComposer::setTransactionState(android::FrameTimelineInfo const&, android::Vector<android::ComposerState> const&, android::Vector<android::DisplayState> const&, unsigned int, android::sp<android::IBinder> const&, android::InputWindowCommands const&, long, bool, android::client_cache_t const&, bool, std::__1::vector<android::ListenerCallbacks, std::__1::allocator<android::ListenerCallbacks> > const&, unsigned long)+872)
+  #05 pc 00000000000a0ed8  libgui.so (android::SurfaceComposerClient::Transaction::apply(bool)+556)
+  #06 pc 00000000000947ac  libgui.so (android::BLASTBufferQueue::processNextBufferLocked(bool)+9748)
+  #07 pc 00000000000ae488  libgui.so (android::BLASTBufferQueue::onFrameAvailable(android::BufferItem const&)+136)
+  #08 pc 00000000000888d8  libgui.so (android::ConsumerBase::onFrameAvailable(android::BufferItem const&)+172)
+  #09 pc 0000000000084448  libgui.so (android::BufferQueue::ProxyConsumerListener::onFrameAvailable(android::BufferItem const&)+92)
+  #10 pc 00000000000c7b8c  libgui.so (android::BufferQueueProducer::queueBuffer(int, android::IGraphicBufferProducer::QueueBufferInput const&, android::IGraphicBufferProducer::QueueBufferOutput*)+2232)
+  #11 pc 00000000000258f4  libcodec2_client.so (android::hardware::media::c2::OutputBufferQueue::outputBuffer(C2ConstGraphicBlock const&, android::IGraphicBufferProducer::QueueBufferInput const&, android::IGraphicBufferProducer::QueueBufferOutput*)+1104)
+  #12 pc 000000000004a038  libsfplugin_ccodec.so (android::CCodecBufferChannel::renderOutputBuffer(android::sp<android::MediaCodecBuffer> const&, long)+2572)
+  #13 pc 000000000012036c  libstagefright.so (android::MediaCodec::onReleaseOutputBuffer(android::sp<android::AMessage> const&)+1184)
+  #14 pc 0000000000110158  libstagefright.so (android::MediaCodec::onMessageReceived(android::sp<android::AMessage> const&)+376)
+  #15 pc 00000000000188c0  libstagefright_foundation.so (android::AHandler::deliverMessage(android::sp<android::AMessage> const&)+84)
+  #16 pc 000000000001e68c  libstagefright_foundation.so (android::AMessage::deliver()+188)
+  #17 pc 0000000000019c08  libstagefright_foundation.so (android::ALooper::loop()+592)
+  #18 pc 0000000000012138  libutils.so (android::Thread::_threadLoop(void*)+460)
+  #19 pc 00000000000bc3ec  libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+144)
+  #20 pc 0000000000011928  libutils.so (thread_data_t::trampoline(thread_data_t const*)+404)
+  #21 pc 00000000000b1920  libc.so (__pthread_start(void*)+264)
+  #22 pc 00000000000513f0  libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/regs.txt b/libunwindstack/offline_files/youtube_compiled_arm64/regs.txt
new file mode 100644
index 0000000..110362b
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 36
+x1: c0306201
+x2: 7b34407370
+x3: 7b344075a0
+x4: 7b344073b0
+x5: 7c8f6ffe54
+x6: 2c
+x7: 0
+x8: 1d
+x9: 7b34407310
+x10: 7b34407310
+x11: 7b344072e0
+x12: ffffff80ffffffd0
+x13: 7c1f790fd0
+x14: 1
+x15: 26
+x16: 7e729f9448
+x17: 7e8686d350
+x18: 7acb076000
+x19: 7b34409000
+x20: 7cbf755480
+x21: 7b34407520
+x22: 7cbf755408
+x23: 7cbf7553e0
+x24: 7cbf7553b8
+x25: 74
+x26: 80407203
+x27: 7b34409000
+x28: 7204
+x29: 7b34407340
+lr: 7e8686d3ec
+sp: 7b34407260
+pc: 7e868b15e4
+pst: 80000000
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/stack.data b/libunwindstack/offline_files/youtube_compiled_arm64/stack.data
new file mode 100644
index 0000000..e238489
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/yt_music_arm64/base.odex.gz b/libunwindstack/offline_files/yt_music_arm64/base.odex.gz
new file mode 100644
index 0000000..a19d1ed
--- /dev/null
+++ b/libunwindstack/offline_files/yt_music_arm64/base.odex.gz
Binary files differ
diff --git a/libunwindstack/offline_files/yt_music_arm64/links.txt b/libunwindstack/offline_files/yt_music_arm64/links.txt
new file mode 100644
index 0000000..aadfa08
--- /dev/null
+++ b/libunwindstack/offline_files/yt_music_arm64/links.txt
@@ -0,0 +1,3 @@
+../common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c boot.oat
+../common/libart.so_82c0556f4b66528e4a608c100a63b712 libart.so
+../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/yt_music_arm64/maps.txt b/libunwindstack/offline_files/yt_music_arm64/maps.txt
new file mode 100644
index 0000000..f54f627
--- /dev/null
+++ b/libunwindstack/offline_files/yt_music_arm64/maps.txt
@@ -0,0 +1,8 @@
+6fcf3000-6fd9e000 r--p 0 00:00 0   boot.oat
+6fd9e000-700fe000 r-xp ab000 00:00 0   boot.oat
+7b620b5000-7b6221b000 r--p 0 00:00 0   base.odex
+7b6221b000-7b6279f000 r-xp 166000 00:00 0   base.odex
+7bdd800000-7bdda00000 r--p 0 00:00 0   libart.so
+7bdda00000-7bddf4a000 r-xp 200000 00:00 0   libart.so
+7e86814000-7e86850000 r--p 0 00:00 0   libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0   libc.so
diff --git a/libunwindstack/offline_files/yt_music_arm64/output.txt b/libunwindstack/offline_files/yt_music_arm64/output.txt
new file mode 100644
index 0000000..0cba24a
--- /dev/null
+++ b/libunwindstack/offline_files/yt_music_arm64/output.txt
@@ -0,0 +1,18 @@
+  #00 pc 000000000004c35c  libc.so (syscall+28)
+  #01 pc 0000000000411148  libart.so (art::Thread::Park(bool, long)+1088)
+  #02 pc 0000000000410790  libart.so (art::Unsafe_park(_JNIEnv*, _jobject*, unsigned char, long)+280)
+  #03 pc 00000000000adf4c  boot.oat (art_jni_trampoline+108)
+  #04 pc 000000000023c924  boot.oat (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await+756)
+  #05 pc 00000000003d966c  boot.oat (java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take+524)
+  #06 pc 0000000000394ab8  boot.oat ([DEDUPED]+40)
+  #07 pc 0000000000377574  boot.oat (java.util.concurrent.ThreadPoolExecutor.getTask+484)
+  #08 pc 000000000037b46c  boot.oat (java.util.concurrent.ThreadPoolExecutor.runWorker+236)
+  #09 pc 0000000000374f84  boot.oat (java.util.concurrent.ThreadPoolExecutor$Worker.run+68)
+  #10 pc 000000000059ec1c  base.odex (lwo.run+92)
+  #11 pc 00000000001bf0bc  boot.oat (java.lang.Thread.run+76)
+  #12 pc 0000000000293564  libart.so (art_quick_invoke_stub+548)
+  #13 pc 00000000002c6558  libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+156)
+  #14 pc 0000000000367ea8  libart.so (art::JValue art::InvokeVirtualOrInterfaceWithJValues<art::ArtMethod*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, jvalue const*)+380)
+  #15 pc 00000000003e9b6c  libart.so (art::Thread::CreateCallback(void*)+1004)
+  #16 pc 00000000000b1920  libc.so (__pthread_start(void*)+264)
+  #17 pc 00000000000513f0  libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/yt_music_arm64/regs.txt b/libunwindstack/offline_files/yt_music_arm64/regs.txt
new file mode 100644
index 0000000..9bbe3ce
--- /dev/null
+++ b/libunwindstack/offline_files/yt_music_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 7d4f6e3cf8
+x1: 80
+x2: 2
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 20
+x8: 62
+x9: 7bff6b7e40
+x10: 430000
+x11: 40
+x12: 341555ac
+x13: 18
+x14: f5c2b97f58
+x15: 12dd106c
+x16: 7bde011728
+x17: 7e86860340
+x18: 7b5e54e000
+x19: 7d4f6e3cf8
+x20: 7d4f6e3cc0
+x21: 47
+x22: 0
+x23: 7bde215000
+x24: 0
+x25: 1
+x26: 0
+x27: 7bde217000
+x28: 7b5e85c000
+x29: 7b5e85b500
+lr: 7bddc1114c
+sp: 7b5e85b4d0
+pc: 7e8686035c
+pst: 40000000
diff --git a/libunwindstack/offline_files/yt_music_arm64/stack.data b/libunwindstack/offline_files/yt_music_arm64/stack.data
new file mode 100644
index 0000000..06345b7
--- /dev/null
+++ b/libunwindstack/offline_files/yt_music_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/AndroidUnwinderTest.cpp b/libunwindstack/tests/AndroidUnwinderTest.cpp
new file mode 100644
index 0000000..1794acc
--- /dev/null
+++ b/libunwindstack/tests/AndroidUnwinderTest.cpp
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2022 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 <dlfcn.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <atomic>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <android-base/threads.h>
+
+#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Error.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/UcontextArm.h>
+#include <unwindstack/UcontextArm64.h>
+#include <unwindstack/UcontextX86.h>
+#include <unwindstack/UcontextX86_64.h>
+#include <unwindstack/Unwinder.h>
+
+#include "PidUtils.h"
+#include "TestUtils.h"
+
+namespace unwindstack {
+
+static std::string GetBacktrace(AndroidUnwinder& unwinder, std::vector<FrameData>& frames) {
+  std::string backtrace_str;
+  for (auto& frame : frames) {
+    backtrace_str += unwinder.FormatFrame(frame) + '\n';
+  }
+  return backtrace_str;
+}
+
+static pid_t ForkWaitForever() {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    // Do a loop that guarantees the terminating leaf frame will be in
+    // the test executable and not any other library function.
+    bool run = true;
+    while (run) {
+      DoNotOptimize(run = true);
+    }
+    exit(1);
+  }
+  return pid;
+}
+
+TEST(AndroidUnwinderDataTest, demangle_function_names) {
+  AndroidUnwinderData data;
+
+  // Add a few frames with and without demangled function names.
+  data.frames.resize(4);
+  data.frames[0].function_name = "no_demangle()";
+  data.frames[1].function_name = "_Z4fakeb";
+  data.frames[3].function_name = "_Z8demanglei";
+
+  data.DemangleFunctionNames();
+  EXPECT_EQ("no_demangle()", data.frames[0].function_name);
+  EXPECT_EQ("fake(bool)", data.frames[1].function_name);
+  EXPECT_EQ("", data.frames[2].function_name);
+  EXPECT_EQ("demangle(int)", data.frames[3].function_name);
+
+  // Make sure that this action is idempotent.
+  data.DemangleFunctionNames();
+  EXPECT_EQ("no_demangle()", data.frames[0].function_name);
+  EXPECT_EQ("fake(bool)", data.frames[1].function_name);
+  EXPECT_EQ("", data.frames[2].function_name);
+  EXPECT_EQ("demangle(int)", data.frames[3].function_name);
+}
+
+TEST(AndroidUnwinderDataTest, get_error_string) {
+  AndroidUnwinderData data;
+
+  EXPECT_EQ("None", data.GetErrorString());
+  data.error.code = ERROR_INVALID_ELF;
+  EXPECT_EQ("Invalid Elf", data.GetErrorString());
+  data.error.code = ERROR_MEMORY_INVALID;
+  EXPECT_EQ("Memory Invalid", data.GetErrorString());
+  data.error.address = 0x1000;
+  EXPECT_EQ("Memory Invalid at address 0x1000", data.GetErrorString());
+}
+
+TEST(AndroidUnwinderTest, unwind_errors) {
+  AndroidLocalUnwinder unwinder;
+
+  AndroidUnwinderData data;
+  void* ucontext = nullptr;
+  EXPECT_FALSE(unwinder.Unwind(ucontext, data));
+  EXPECT_EQ(ERROR_INVALID_PARAMETER, data.error.code);
+  std::unique_ptr<Regs> regs;
+  EXPECT_FALSE(unwinder.Unwind(regs.get(), data));
+  EXPECT_EQ(ERROR_INVALID_PARAMETER, data.error.code);
+  // Make sure that we are using a different arch from the
+  // current arch.
+  if (Regs::CurrentArch() == ARCH_ARM) {
+    regs.reset(new RegsArm64);
+  } else {
+    regs.reset(new RegsArm);
+  }
+  EXPECT_FALSE(unwinder.Unwind(regs.get(), data));
+  EXPECT_EQ(ERROR_BAD_ARCH, data.error.code);
+}
+
+TEST(AndroidUnwinderTest, create) {
+  // Verify the local unwinder object is created.
+  std::unique_ptr<AndroidUnwinder> unwinder(AndroidUnwinder::Create(getpid()));
+  AndroidUnwinderData data;
+  ASSERT_TRUE(unwinder->Unwind(data));
+
+  pid_t pid = ForkWaitForever();
+  ASSERT_NE(-1, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_TRUE(RunWhenQuiesced(pid, false, [pid, &unwinder]() {
+    // Verify the remote unwinder object is created.
+    unwinder.reset(AndroidUnwinder::Create(pid));
+    AndroidUnwinderData data;
+    if (!unwinder->Unwind(data)) {
+      printf("Failed to unwind %s\n", data.GetErrorString().c_str());
+      return PID_RUN_FAIL;
+    }
+    return PID_RUN_PASS;
+  }));
+}
+
+TEST(AndroidLocalUnwinderTest, initialize_before) {
+  AndroidLocalUnwinder unwinder;
+  ErrorData error;
+  ASSERT_TRUE(unwinder.Initialize(error));
+
+  AndroidUnwinderData data;
+  ASSERT_TRUE(unwinder.Unwind(data));
+}
+
+TEST(AndroidLocalUnwinderTest, suffix_ignore) {
+  AndroidLocalUnwinder unwinder(std::vector<std::string>{}, std::vector<std::string>{"so"});
+  AndroidUnwinderData data;
+  // This should work as long as the first frame is in the test executable.
+  ASSERT_TRUE(unwinder.Unwind(data));
+  // Make sure the unwind doesn't include any .so frames.
+  for (const auto& frame : data.frames) {
+    ASSERT_TRUE(frame.map_info == nullptr ||
+                !android::base::EndsWith(frame.map_info->name(), ".so"))
+        << GetBacktrace(unwinder, data.frames);
+  }
+}
+
+TEST(AndroidUnwinderTest, verify_all_unwind_functions) {
+  AndroidLocalUnwinder unwinder;
+  AndroidUnwinderData data;
+  ASSERT_TRUE(unwinder.Unwind(data));
+  ASSERT_TRUE(unwinder.Unwind(std::nullopt, data));
+  ASSERT_TRUE(unwinder.Unwind(getpid(), data));
+  std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+  RegsGetLocal(regs.get());
+
+  void* ucontext;
+  switch (regs->Arch()) {
+    case ARCH_ARM: {
+      arm_ucontext_t* arm_ucontext =
+          reinterpret_cast<arm_ucontext_t*>(malloc(sizeof(arm_ucontext_t)));
+      ucontext = arm_ucontext;
+      memcpy(&arm_ucontext->uc_mcontext.regs[0], regs->RawData(), ARM_REG_LAST * sizeof(uint32_t));
+    } break;
+    case ARCH_ARM64: {
+      arm64_ucontext_t* arm64_ucontext =
+          reinterpret_cast<arm64_ucontext_t*>(malloc(sizeof(arm64_ucontext_t)));
+      ucontext = arm64_ucontext;
+      memcpy(&arm64_ucontext->uc_mcontext.regs[0], regs->RawData(),
+             ARM64_REG_LAST * sizeof(uint64_t));
+    } break;
+    case ARCH_X86: {
+      x86_ucontext_t* x86_ucontext =
+          reinterpret_cast<x86_ucontext_t*>(malloc(sizeof(x86_ucontext_t)));
+      ucontext = x86_ucontext;
+      RegsX86* regs_x86 = static_cast<RegsX86*>(regs.get());
+
+      x86_ucontext->uc_mcontext.edi = (*regs_x86)[X86_REG_EDI];
+      x86_ucontext->uc_mcontext.esi = (*regs_x86)[X86_REG_ESI];
+      x86_ucontext->uc_mcontext.ebp = (*regs_x86)[X86_REG_EBP];
+      x86_ucontext->uc_mcontext.esp = (*regs_x86)[X86_REG_ESP];
+      x86_ucontext->uc_mcontext.ebx = (*regs_x86)[X86_REG_EBX];
+      x86_ucontext->uc_mcontext.edx = (*regs_x86)[X86_REG_EDX];
+      x86_ucontext->uc_mcontext.ecx = (*regs_x86)[X86_REG_ECX];
+      x86_ucontext->uc_mcontext.eax = (*regs_x86)[X86_REG_EAX];
+      x86_ucontext->uc_mcontext.eip = (*regs_x86)[X86_REG_EIP];
+    } break;
+    case ARCH_X86_64: {
+      x86_64_ucontext_t* x86_64_ucontext =
+          reinterpret_cast<x86_64_ucontext_t*>(malloc(sizeof(x86_64_ucontext_t)));
+      ucontext = x86_64_ucontext;
+      RegsX86_64* regs_x86_64 = static_cast<RegsX86_64*>(regs.get());
+
+      memcpy(&x86_64_ucontext->uc_mcontext.r8, &(*regs_x86_64)[X86_64_REG_R8],
+             8 * sizeof(uint64_t));
+
+      x86_64_ucontext->uc_mcontext.rdi = (*regs_x86_64)[X86_64_REG_RDI];
+      x86_64_ucontext->uc_mcontext.rsi = (*regs_x86_64)[X86_64_REG_RSI];
+      x86_64_ucontext->uc_mcontext.rbp = (*regs_x86_64)[X86_64_REG_RBP];
+      x86_64_ucontext->uc_mcontext.rbx = (*regs_x86_64)[X86_64_REG_RBX];
+      x86_64_ucontext->uc_mcontext.rdx = (*regs_x86_64)[X86_64_REG_RDX];
+      x86_64_ucontext->uc_mcontext.rax = (*regs_x86_64)[X86_64_REG_RAX];
+      x86_64_ucontext->uc_mcontext.rcx = (*regs_x86_64)[X86_64_REG_RCX];
+      x86_64_ucontext->uc_mcontext.rsp = (*regs_x86_64)[X86_64_REG_RSP];
+      x86_64_ucontext->uc_mcontext.rip = (*regs_x86_64)[X86_64_REG_RIP];
+    } break;
+    default:
+      ucontext = nullptr;
+      break;
+  }
+  ASSERT_TRUE(ucontext != nullptr);
+  ASSERT_TRUE(unwinder.Unwind(ucontext, data));
+  free(ucontext);
+  AndroidUnwinderData reg_data;
+  ASSERT_TRUE(unwinder.Unwind(regs.get(), reg_data));
+  ASSERT_EQ(data.frames.size(), reg_data.frames.size());
+  // Make sure all of the frame data is exactly the same.
+  for (size_t i = 0; i < data.frames.size(); i++) {
+    SCOPED_TRACE("\nMismatch at Frame " + std::to_string(i) + "\nucontext trace:\n" +
+                 GetBacktrace(unwinder, data.frames) + "\nregs trace:\n" +
+                 GetBacktrace(unwinder, reg_data.frames));
+    const auto& frame_context = data.frames[i];
+    const auto& frame_reg = reg_data.frames[i];
+    ASSERT_EQ(frame_context.num, frame_reg.num);
+    ASSERT_EQ(frame_context.rel_pc, frame_reg.rel_pc);
+    ASSERT_EQ(frame_context.pc, frame_reg.pc);
+    ASSERT_EQ(frame_context.sp, frame_reg.sp);
+    ASSERT_STREQ(frame_context.function_name.c_str(), frame_reg.function_name.c_str());
+    ASSERT_EQ(frame_context.function_offset, frame_reg.function_offset);
+    ASSERT_EQ(frame_context.map_info.get(), frame_reg.map_info.get());
+  }
+}
+
+TEST(AndroidLocalUnwinderTest, unwind_current_thread) {
+  AndroidLocalUnwinder unwinder;
+  AndroidUnwinderData data;
+  ASSERT_TRUE(unwinder.Unwind(data));
+  // Verify that the libunwindstack.so does not appear in the first frame.
+  ASSERT_TRUE(data.frames[0].map_info == nullptr ||
+              !android::base::EndsWith(data.frames[0].map_info->name(), "/libunwindstack.so"))
+      << "libunwindstack.so not removed properly\n"
+      << GetBacktrace(unwinder, data.frames);
+}
+
+TEST(AndroidLocalUnwinderTest, unwind_current_thread_show_all_frames) {
+  AndroidLocalUnwinder unwinder;
+  AndroidUnwinderData data(true);
+  ASSERT_TRUE(unwinder.Unwind(data));
+  // Verify that the libunwindstack.so does appear in the first frame.
+  ASSERT_TRUE(data.frames[0].map_info != nullptr &&
+              android::base::EndsWith(data.frames[0].map_info->name(), "/libunwindstack.so"))
+      << "libunwindstack.so was removed improperly\n"
+      << GetBacktrace(unwinder, data.frames);
+}
+
+TEST(AndroidLocalUnwinderTest, unwind_different_thread) {
+  std::atomic<pid_t> tid;
+  std::atomic_bool keep_running = true;
+  std::thread thread([&tid, &keep_running] {
+    tid = android::base::GetThreadId();
+    while (keep_running) {
+    }
+    return nullptr;
+  });
+
+  while (tid == 0) {
+  }
+
+  {
+    AndroidLocalUnwinder unwinder;
+    AndroidUnwinderData data;
+    ASSERT_TRUE(unwinder.Unwind(data));
+    // Verify that the libunwindstack.so does not appear in the first frame.
+    ASSERT_TRUE(data.frames[0].map_info == nullptr ||
+                !android::base::EndsWith(data.frames[0].map_info->name(), "/libunwindstack.so"))
+        << "libunwindstack.so not removed properly\n"
+        << GetBacktrace(unwinder, data.frames);
+  }
+
+  {
+    AndroidLocalUnwinder unwinder;
+    AndroidUnwinderData data(true);
+    ASSERT_TRUE(unwinder.Unwind(data));
+    // Verify that the libunwindstack.so does appear in the first frame.
+    ASSERT_TRUE(data.frames[0].map_info != nullptr &&
+                android::base::EndsWith(data.frames[0].map_info->name(), "/libunwindstack.so"))
+        << "libunwindstack.so was removed improperly\n"
+        << GetBacktrace(unwinder, data.frames);
+  }
+
+  // Allow the thread to terminate normally.
+  keep_running = false;
+  thread.join();
+}
+
+TEST(AndroidRemoteUnwinderTest, initialize_before) {
+  pid_t pid = ForkWaitForever();
+  ASSERT_NE(-1, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_TRUE(Attach(pid));
+
+  AndroidRemoteUnwinder unwinder(pid);
+  ErrorData error;
+  ASSERT_TRUE(unwinder.Initialize(error));
+
+  AndroidUnwinderData data;
+  ASSERT_TRUE(unwinder.Unwind(data));
+
+  ASSERT_TRUE(Detach(pid));
+}
+
+static bool Verify(pid_t pid, std::function<PidRunEnum(const FrameData& frame)> fn) {
+  return RunWhenQuiesced(pid, false, [pid, &fn]() {
+    AndroidRemoteUnwinder unwinder(pid);
+    AndroidUnwinderData data;
+    if (!unwinder.Unwind(data)) {
+      printf("Failed to unwind %s\n", data.GetErrorString().c_str());
+      return PID_RUN_FAIL;
+    }
+    const auto& frame = data.frames[0];
+    return fn(frame);
+  });
+}
+
+TEST(AndroidRemoteUnwinderTest, skip_libraries) {
+  void* test_lib = GetTestLibHandle();
+  ASSERT_TRUE(test_lib != nullptr);
+  int (*wait_func)() = reinterpret_cast<int (*)()>(dlsym(test_lib, "WaitForever"));
+  ASSERT_TRUE(wait_func != nullptr);
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    DoNotOptimize(wait_func());
+    exit(0);
+  }
+  ASSERT_NE(-1, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_TRUE(Verify(pid, [pid](const FrameData& frame) {
+    // Make sure that the frame is in the dlopen'd library before proceeding.
+    if (frame.map_info == nullptr ||
+        !android::base::EndsWith(frame.map_info->name(), "/libunwindstack_local.so")) {
+      return PID_RUN_KEEP_GOING;
+    }
+
+    // Do an unwind removing the libunwindstack_local.so library.
+    AndroidRemoteUnwinder unwinder(pid, std::vector<std::string>{"libunwindstack_local.so"});
+    AndroidUnwinderData data;
+    if (!unwinder.Unwind(data)) {
+      printf("Failed to unwind %s\n", data.GetErrorString().c_str());
+      return PID_RUN_FAIL;
+    }
+
+    // Verify that library is properly ignored.
+    if (android::base::EndsWith(data.frames[0].map_info->name(), "/libunwindstack_local.so")) {
+      printf("Failed to strip libunwindstack_local.so\n%s\n",
+             GetBacktrace(unwinder, data.frames).c_str());
+      return PID_RUN_FAIL;
+    }
+    return PID_RUN_PASS;
+  }));
+}
+
+TEST(AndroidRemoteUnwinderTest, suffix_ignore) {
+  pid_t pid = ForkWaitForever();
+  ASSERT_NE(-1, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_TRUE(Verify(pid, [pid](const FrameData& frame) {
+    // Wait until the forked process is no longer in libc.so.
+    if (frame.map_info != nullptr && android::base::EndsWith(frame.map_info->name(), ".so")) {
+      return PID_RUN_KEEP_GOING;
+    }
+
+    AndroidRemoteUnwinder unwinder(pid, std::vector<std::string>{}, std::vector<std::string>{"so"});
+    AndroidUnwinderData data;
+    if (!unwinder.Unwind(data)) {
+      printf("Failed to unwind %s\n", data.GetErrorString().c_str());
+
+      AndroidRemoteUnwinder normal_unwinder(pid);
+      if (normal_unwinder.Unwind(data)) {
+        printf("Full unwind %s\n", GetBacktrace(normal_unwinder, data.frames).c_str());
+      }
+      return PID_RUN_FAIL;
+    }
+
+    // Make sure the unwind doesn't include any .so frames.
+    for (const auto& frame : data.frames) {
+      if (frame.map_info != nullptr && android::base::EndsWith(frame.map_info->name(), ".so")) {
+        printf("Found unexpected .so frame\n%s\n", GetBacktrace(unwinder, data.frames).c_str());
+        return PID_RUN_FAIL;
+      }
+    }
+    return PID_RUN_PASS;
+  }));
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 69a7816..ea0dc0e 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -29,7 +29,7 @@
 #include "ArmExidx.h"
 
 #include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
index 79c799c..cfd5b7e 100644
--- a/libunwindstack/tests/ArmExidxExtractTest.cpp
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -26,7 +26,7 @@
 #include "ArmExidx.h"
 
 #include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/DexFileData.h b/libunwindstack/tests/DexFileData.h
index 6975c68..d4408d9 100644
--- a/libunwindstack/tests/DexFileData.h
+++ b/libunwindstack/tests/DexFileData.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_DEXFILESDATA_H
-#define _LIBUNWINDSTACK_DEXFILESDATA_H
+#pragma once
 
 namespace unwindstack {
 
@@ -41,5 +40,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_DEXFILESDATA_H
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 05b368b..96d99e3 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -19,6 +19,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <memory>
 #include <unordered_map>
 
 #include <MemoryLocal.h>
@@ -29,12 +30,12 @@
 
 #include "DexFile.h"
 #include "DexFileData.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
 static constexpr size_t kNumLeakLoops = 5000;
-static constexpr size_t kMaxAllowedLeakBytes = 1024;
+static constexpr size_t kMaxAllowedLeakBytes = 4 * 1024;
 
 static void CheckForLeak(size_t loop, size_t* first_allocated_bytes, size_t* last_allocated_bytes) {
   size_t allocated_bytes = mallinfo().uordblks;
@@ -60,8 +61,8 @@
   size_t last_allocated_bytes = 0;
   for (size_t i = 0; i < kNumLeakLoops; i++) {
     MemoryFake memory;
-    MapInfo info(nullptr, nullptr, 0, 0x10000, 0, 0x5, tf.path);
-    EXPECT_TRUE(DexFile::Create(0, sizeof(kDexData), &memory, &info) != nullptr);
+    auto info = MapInfo::Create(0, 0x10000, 0, 0x5, tf.path);
+    EXPECT_TRUE(DexFile::Create(0, sizeof(kDexData), &memory, info.get()) != nullptr);
     ASSERT_NO_FATAL_FAILURE(CheckForLeak(i, &first_allocated_bytes, &last_allocated_bytes));
   }
 }
@@ -88,8 +89,8 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(nullptr, nullptr, 0, 0x10000, 0, 0x5, tf.path);
-  EXPECT_TRUE(DexFile::Create(0x500, sizeof(kDexData), &memory, &info) != nullptr);
+  auto info = MapInfo::Create(0, 0x10000, 0, 0x5, tf.path);
+  EXPECT_TRUE(DexFile::Create(0x500, sizeof(kDexData), &memory, info.get()) != nullptr);
 }
 
 TEST(DexFileTest, create_using_file_non_zero_start) {
@@ -101,8 +102,8 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
-  EXPECT_TRUE(DexFile::Create(0x600, sizeof(kDexData), &memory, &info) != nullptr);
+  auto info = MapInfo::Create(0x100, 0x10000, 0, 0x5, tf.path);
+  EXPECT_TRUE(DexFile::Create(0x600, sizeof(kDexData), &memory, info.get()) != nullptr);
 }
 
 TEST(DexFileTest, create_using_file_non_zero_offset) {
@@ -114,22 +115,22 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
-  EXPECT_TRUE(DexFile::Create(0x400, sizeof(kDexData), &memory, &info) != nullptr);
+  auto info = MapInfo::Create(0x100, 0x10000, 0x200, 0x5, tf.path);
+  EXPECT_TRUE(DexFile::Create(0x400, sizeof(kDexData), &memory, info.get()) != nullptr);
 }
 
 TEST(DexFileTest, create_using_memory_empty_file) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
-  EXPECT_TRUE(DexFile::Create(0x4000, sizeof(kDexData), &memory, &info) != nullptr);
+  auto info = MapInfo::Create(0x100, 0x10000, 0x200, 0x5, "");
+  EXPECT_TRUE(DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get()) != nullptr);
 }
 
 TEST(DexFileTest, create_using_memory_file_does_not_exist) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
-  EXPECT_TRUE(DexFile::Create(0x4000, sizeof(kDexData), &memory, &info) != nullptr);
+  auto info = MapInfo::Create(0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
+  EXPECT_TRUE(DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get()) != nullptr);
 }
 
 TEST(DexFileTest, create_using_memory_file_is_malformed) {
@@ -141,13 +142,14 @@
 
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(nullptr, nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
-  std::shared_ptr<DexFile> dex_file = DexFile::Create(0x4000, sizeof(kDexData), &memory, &info);
+  auto info = MapInfo::Create(0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
+  std::shared_ptr<DexFile> dex_file =
+      DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get());
   ASSERT_TRUE(dex_file != nullptr);
 
   // Check it came from memory by clearing memory and verifying it fails.
   memory.Clear();
-  dex_file = DexFile::Create(0x4000, sizeof(kDexData), &memory, &info);
+  dex_file = DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get());
   EXPECT_TRUE(dex_file == nullptr);
 }
 
@@ -168,8 +170,8 @@
 TEST(DexFileTest, get_method) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
-  std::shared_ptr<DexFile> dex_file(DexFile::Create(0x4000, sizeof(kDexData), &memory, &info));
+  auto info = MapInfo::Create(0x100, 0x10000, 0x200, 0x5, "");
+  std::shared_ptr<DexFile> dex_file(DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get()));
   ASSERT_TRUE(dex_file != nullptr);
 
   SharedString method;
@@ -186,8 +188,8 @@
 TEST(DexFileTest, get_method_empty) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
-  std::shared_ptr<DexFile> dex_file(DexFile::Create(0x4000, sizeof(kDexData), &memory, &info));
+  auto info = MapInfo::Create(0x100, 0x10000, 0x200, 0x5, "");
+  std::shared_ptr<DexFile> dex_file(DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get()));
   ASSERT_TRUE(dex_file != nullptr);
 
   SharedString method;
@@ -205,8 +207,9 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(nullptr, nullptr, 0x4000, 0x10000, 0, 0x5, tf.path);
-  std::shared_ptr<DexFile> dex_file = DexFile::Create(0x4000, sizeof(kDexData), &memory, &info);
+  auto info = MapInfo::Create(0x4000, 0x10000, 0, 0x5, tf.path);
+  std::shared_ptr<DexFile> dex_file =
+      DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get());
   EXPECT_TRUE(dex_file != nullptr);
 
   SharedString method;
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index c18a437..9250d6d 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -31,7 +31,7 @@
 #include "DexFile.h"
 #include "DexFileData.h"
 #include "ElfFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
@@ -72,22 +72,22 @@
     ASSERT_TRUE(maps_->Parse());
 
     // Global variable in a section that is not readable.
-    MapInfo* map_info = maps_->Get(kMapGlobalNonReadable);
+    MapInfo* map_info = maps_->Get(kMapGlobalNonReadable).get();
     ASSERT_TRUE(map_info != nullptr);
     CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
 
     // Global variable not set by default.
-    map_info = maps_->Get(kMapGlobalSetToZero);
+    map_info = maps_->Get(kMapGlobalSetToZero).get();
     ASSERT_TRUE(map_info != nullptr);
     CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
 
     // Global variable set in this map.
-    map_info = maps_->Get(kMapGlobal);
+    map_info = maps_->Get(kMapGlobal).get();
     ASSERT_TRUE(map_info != nullptr);
     CreateFakeElf(map_info, 0xf1800, 0xf1000, 0xf1000, 0x10000);
 
     // Global variable set in this map, but there is an empty map before rw map.
-    map_info = maps_->Get(kMapGlobalAfterEmpty);
+    map_info = maps_->Get(kMapGlobalAfterEmpty).get();
     ASSERT_TRUE(map_info != nullptr);
     CreateFakeElf(map_info, 0x3800, 0x3000, 0x3000, 0xd000);
   }
@@ -278,7 +278,7 @@
   EXPECT_EQ("nothing", method_name);
   EXPECT_EQ(0x124U, method_offset);
 
-  MapInfo* map_info = maps_->Get(kMapGlobal);
+  auto map_info = maps_->Get(kMapGlobal);
   map_info->set_name("/system/lib/libart.so");
   dex_files_ = CreateDexFiles(ARCH_ARM, process_memory_, libs);
   // Set the rw map to the same name or this will not scan this entry.
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index 7cb51ef..876f951 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -32,7 +32,7 @@
 #include "DwarfCfa.h"
 
 #include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 5ed428d..45728fb 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -32,7 +32,7 @@
 #include "DwarfCfa.h"
 
 #include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
@@ -204,7 +204,7 @@
   ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
   ASSERT_EQ(0U, loc_regs.size());
 
-  ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint());
+  ASSERT_EQ("6 unwind Invalid: restore while processing cie.\n", GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
 
   ResetLogs();
@@ -233,7 +233,7 @@
   ASSERT_EQ(0x4002U, this->dmem_->cur_offset());
   ASSERT_EQ(0U, loc_regs.size());
 
-  ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint());
+  ASSERT_EQ("6 unwind Invalid: restore while processing cie.\n", GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
 
   ResetLogs();
@@ -598,7 +598,7 @@
   ASSERT_EQ(0U, loc_regs.size());
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
 
-  ASSERT_EQ("4 unwind Attempt to set new register, but cfa is not already set to a register.\n",
+  ASSERT_EQ("6 unwind Attempt to set new register, but cfa is not already set to a register.\n",
             GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
 
@@ -641,7 +641,7 @@
   ASSERT_EQ(0U, loc_regs.size());
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
 
-  ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
+  ASSERT_EQ("6 unwind Attempt to set offset, but cfa is not set to a register.\n",
             GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
 
@@ -683,7 +683,7 @@
   ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
 
-  ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
+  ASSERT_EQ("6 unwind Attempt to set offset, but cfa is not set to a register.\n",
             GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
 
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index 235151b..47a00b4 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -26,7 +26,7 @@
 #include "DwarfEncoding.h"
 
 #include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
@@ -195,6 +195,25 @@
   ASSERT_EQ(3U, fdes.size());
 }
 
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes32_big_function_address) {
+  SetCie32(&this->memory_, 0x5000, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+  SetFde32(&this->memory_, 0x5100, 0xfc, 0, 0xe9ad9b1f, 0x200);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200, 0));
+
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
+
+  ASSERT_EQ(1U, fdes.size());
+
+  EXPECT_EQ(0x5000U, fdes[0]->cie_offset);
+  EXPECT_EQ(0x5110U, fdes[0]->cfa_instructions_offset);
+  EXPECT_EQ(0x5200U, fdes[0]->cfa_instructions_end);
+  EXPECT_EQ(0xe9ad9b1fU, fdes[0]->pc_start);
+  EXPECT_EQ(0xe9ad9d1fU, fdes[0]->pc_end);
+  EXPECT_EQ(0U, fdes[0]->lsda_address);
+  EXPECT_TRUE(fdes[0]->cie != nullptr);
+}
+
 TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc32) {
   SetFourFdes32(&this->memory_);
   ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
@@ -414,7 +433,7 @@
 
   ASSERT_TRUE(fde->cie != nullptr);
   EXPECT_EQ(1U, fde->cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_udata4, fde->cie->fde_address_encoding);
   EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
   EXPECT_EQ(0U, fde->cie->segment_size);
   EXPECT_EQ(1U, fde->cie->augmentation_string.size());
@@ -442,7 +461,7 @@
 
   ASSERT_TRUE(fde->cie != nullptr);
   EXPECT_EQ(1U, fde->cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_udata8, fde->cie->fde_address_encoding);
   EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
   EXPECT_EQ(0U, fde->cie->segment_size);
   EXPECT_EQ(1U, fde->cie->augmentation_string.size());
@@ -477,14 +496,14 @@
   const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
   EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
   ASSERT_TRUE(cie != nullptr);
-  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_udata4, 0x20, 0xd, 0x104);
 
   std::vector<uint8_t> zero(0x100, 0);
   this->memory_.SetMemory(0x5000, zero);
   cie = this->debug_frame_->GetCieFromOffset(0x5000);
   EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
   ASSERT_TRUE(cie != nullptr);
-  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_udata4, 0x20, 0xd, 0x104);
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_cie_cached) {
@@ -492,14 +511,14 @@
   const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
   EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
   ASSERT_TRUE(cie != nullptr);
-  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_udata8, 0x20, 0x19, 0x10c);
 
   std::vector<uint8_t> zero(0x100, 0);
   this->memory_.SetMemory(0x5000, zero);
   cie = this->debug_frame_->GetCieFromOffset(0x5000);
   EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
   ASSERT_TRUE(cie != nullptr);
-  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_udata8, 0x20, 0x19, 0x10c);
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version1) {
@@ -507,7 +526,7 @@
   const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
   EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
   ASSERT_TRUE(cie != nullptr);
-  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_udata4, 0x20, 0xd, 0x104);
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version1) {
@@ -515,7 +534,7 @@
   const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
   EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
   ASSERT_TRUE(cie != nullptr);
-  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_udata8, 0x20, 0x19, 0x10c);
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version3) {
@@ -523,7 +542,7 @@
   const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
   EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
   ASSERT_TRUE(cie != nullptr);
-  VerifyCieVersion(cie, 3, 0, DW_EH_PE_sdata4, 0x181, 0xe, 0x104);
+  VerifyCieVersion(cie, 3, 0, DW_EH_PE_udata4, 0x181, 0xe, 0x104);
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version3) {
@@ -531,39 +550,55 @@
   const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
   EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
   ASSERT_TRUE(cie != nullptr);
-  VerifyCieVersion(cie, 3, 0, DW_EH_PE_sdata8, 0x181, 0x1a, 0x10c);
+  VerifyCieVersion(cie, 3, 0, DW_EH_PE_udata8, 0x181, 0x1a, 0x10c);
 }
 
-TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version4) {
-  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 0, 10, 4, 8, 0x81, 3});
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version4_32bit_address) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 4, 10, 4, 8, 0x81, 3});
   const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
   EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
   ASSERT_TRUE(cie != nullptr);
-  VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata4, 0x181, 0x10, 0x104);
+  VerifyCieVersion(cie, 4, 10, DW_EH_PE_udata4, 0x181, 0x10, 0x104);
 }
 
-TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version4) {
-  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 0, 10, 4, 8, 0x81, 3});
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version4_64bit_address) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 8, 10, 4, 8, 0x81, 3});
   const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
   EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
   ASSERT_TRUE(cie != nullptr);
-  VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
+  VerifyCieVersion(cie, 4, 10, DW_EH_PE_udata8, 0x181, 0x10, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version4_32bit_address) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 4, 10, 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 4, 10, DW_EH_PE_udata4, 0x181, 0x1c, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version4_64bit_address) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 8, 10, 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 4, 10, DW_EH_PE_udata8, 0x181, 0x1c, 0x10c);
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version5) {
-  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 0, 10, 4, 8, 0x81, 3});
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 4, 10, 4, 8, 0x81, 3});
   const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
   EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
   ASSERT_TRUE(cie != nullptr);
-  VerifyCieVersion(cie, 5, 10, DW_EH_PE_sdata4, 0x181, 0x10, 0x104);
+  VerifyCieVersion(cie, 5, 10, DW_EH_PE_udata4, 0x181, 0x10, 0x104);
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version5) {
-  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 0, 10, 4, 8, 0x81, 3});
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 8, 10, 4, 8, 0x81, 3});
   const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
   EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
   ASSERT_TRUE(cie != nullptr);
-  VerifyCieVersion(cie, 5, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
+  VerifyCieVersion(cie, 5, 10, DW_EH_PE_udata8, 0x181, 0x1c, 0x10c);
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset_version_invalid) {
@@ -639,7 +674,7 @@
   SetCie32(&this->memory_, 0x5000, 0xfc,
            std::vector<uint8_t>{/* version */ 4,
                                 /* augment string */ 'z', '\0',
-                                /* address size */ 8,
+                                /* address size */ 4,
                                 /* segment size */ 0x10,
                                 /* code alignment factor */ 16,
                                 /* data alignment factor */ 32,
@@ -872,15 +907,18 @@
 
 REGISTER_TYPED_TEST_SUITE_P(
     DwarfDebugFrameTest, GetFdes32, GetFdes32_after_GetFdeFromPc, GetFdes32_not_in_section,
-    GetFdeFromPc32, GetFdeFromPc32_reverse, GetFdeFromPc32_not_in_section, GetFdes64,
-    GetFdes64_after_GetFdeFromPc, GetFdes64_not_in_section, GetFdeFromPc64, GetFdeFromPc64_reverse,
-    GetFdeFromPc64_not_in_section, GetCieFde32, GetCieFde64, GetCieFromOffset32_cie_cached,
-    GetCieFromOffset64_cie_cached, GetCieFromOffset32_version1, GetCieFromOffset64_version1,
-    GetCieFromOffset32_version3, GetCieFromOffset64_version3, GetCieFromOffset32_version4,
-    GetCieFromOffset64_version4, GetCieFromOffset32_version5, GetCieFromOffset64_version5,
-    GetCieFromOffset_version_invalid, GetCieFromOffset32_augment, GetCieFromOffset64_augment,
-    GetFdeFromOffset32_augment, GetFdeFromOffset64_augment, GetFdeFromOffset32_lsda_address,
-    GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved, GetFdeFromPc_overlap);
+    GetFdes32_big_function_address, GetFdeFromPc32, GetFdeFromPc32_reverse,
+    GetFdeFromPc32_not_in_section, GetFdes64, GetFdes64_after_GetFdeFromPc,
+    GetFdes64_not_in_section, GetFdeFromPc64, GetFdeFromPc64_reverse, GetFdeFromPc64_not_in_section,
+    GetCieFde32, GetCieFde64, GetCieFromOffset32_cie_cached, GetCieFromOffset64_cie_cached,
+    GetCieFromOffset32_version1, GetCieFromOffset64_version1, GetCieFromOffset32_version3,
+    GetCieFromOffset64_version3, GetCieFromOffset32_version4_32bit_address,
+    GetCieFromOffset32_version4_64bit_address, GetCieFromOffset64_version4_32bit_address,
+    GetCieFromOffset64_version4_64bit_address, GetCieFromOffset32_version5,
+    GetCieFromOffset64_version5, GetCieFromOffset_version_invalid, GetCieFromOffset32_augment,
+    GetCieFromOffset64_augment, GetFdeFromOffset32_augment, GetFdeFromOffset64_augment,
+    GetFdeFromOffset32_lsda_address, GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved,
+    GetFdeFromPc_overlap);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
 INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index 46a25a4..ea2e397 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -24,7 +24,7 @@
 #include "DwarfEncoding.h"
 
 #include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
@@ -74,7 +74,7 @@
   const DwarfCie* cie = fde->cie;
   ASSERT_TRUE(cie != nullptr);
   EXPECT_EQ(1U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_udata4, cie->fde_address_encoding);
   EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
   EXPECT_EQ(0U, cie->segment_size);
   EXPECT_EQ('\0', cie->augmentation_string[0]);
@@ -113,7 +113,7 @@
   const DwarfCie* cie = fde->cie;
   ASSERT_TRUE(cie != nullptr);
   EXPECT_EQ(1U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata8, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_udata8, cie->fde_address_encoding);
   EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
   EXPECT_EQ(0U, cie->segment_size);
   EXPECT_EQ('\0', cie->augmentation_string[0]);
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 6aa3867..87b477a 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -25,7 +25,7 @@
 #include "DwarfEncoding.h"
 
 #include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
@@ -479,7 +479,7 @@
 
   ASSERT_TRUE(fde->cie != nullptr);
   EXPECT_EQ(1U, fde->cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_udata4, fde->cie->fde_address_encoding);
   EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
   EXPECT_EQ(0U, fde->cie->segment_size);
   EXPECT_EQ(1U, fde->cie->augmentation_string.size());
@@ -517,7 +517,7 @@
 
   ASSERT_TRUE(fde->cie != nullptr);
   EXPECT_EQ(1U, fde->cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_udata8, fde->cie->fde_address_encoding);
   EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
   EXPECT_EQ(0U, fde->cie->segment_size);
   EXPECT_EQ(1U, fde->cie->augmentation_string.size());
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
index 650e965..b3d04c4 100644
--- a/libunwindstack/tests/DwarfMemoryTest.cpp
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -23,7 +23,7 @@
 
 #include <unwindstack/DwarfMemory.h>
 
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
index 8dbf6e8..f4dbf8d 100644
--- a/libunwindstack/tests/DwarfOpLogTest.cpp
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -28,7 +28,7 @@
 
 #include "DwarfOp.h"
 
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 942d074..eda9a58 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -27,8 +27,8 @@
 
 #include "DwarfOp.h"
 
-#include "MemoryFake.h"
 #include "RegsFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 9d047f2..a487bc0 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -25,49 +25,25 @@
 #include "DwarfEncoding.h"
 
 #include "LogFake.h"
-#include "MemoryFake.h"
-#include "RegsFake.h"
+#include "utils/DwarfSectionImplFake.h"
+#include "utils/MemoryFake.h"
+#include "utils/RegsFake.h"
 
 namespace unwindstack {
 
 template <typename TypeParam>
-class TestDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
- public:
-  TestDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
-  virtual ~TestDwarfSectionImpl() = default;
-
-  bool Init(uint64_t, uint64_t, int64_t) override { return false; }
-
-  void GetFdes(std::vector<const DwarfFde*>*) override {}
-
-  const DwarfFde* GetFdeFromPc(uint64_t) override { return nullptr; }
-
-  uint64_t GetCieOffsetFromFde32(uint32_t) { return 0; }
-
-  uint64_t GetCieOffsetFromFde64(uint64_t) { return 0; }
-
-  uint64_t AdjustPcFromFde(uint64_t) override { return 0; }
-
-  void TestSetCachedCieLocRegs(uint64_t offset, const DwarfLocations& loc_regs) {
-    this->cie_loc_regs_[offset] = loc_regs;
-  }
-  void TestClearCachedCieLocRegs() { this->cie_loc_regs_.clear(); }
-  void TestClearError() { this->last_error_.code = DWARF_ERROR_NONE; }
-};
-
-template <typename TypeParam>
 class DwarfSectionImplTest : public ::testing::Test {
  protected:
   void SetUp() override {
     memory_.Clear();
-    section_ = new TestDwarfSectionImpl<TypeParam>(&memory_);
+    section_ = new DwarfSectionImplFake<TypeParam>(&memory_);
     ResetLogs();
   }
 
   void TearDown() override { delete section_; }
 
   MemoryFake memory_;
-  TestDwarfSectionImpl<TypeParam>* section_ = nullptr;
+  DwarfSectionImplFake<TypeParam>* section_ = nullptr;
 };
 TYPED_TEST_SUITE_P(DwarfSectionImplTest);
 
@@ -78,7 +54,7 @@
   EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
   EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
 
-  this->section_->TestClearError();
+  this->section_->FakeClearError();
   ASSERT_TRUE(this->section_->GetCieFromOffset(0x4000) == nullptr);
   EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
   EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
@@ -89,7 +65,7 @@
   EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
   EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
 
-  this->section_->TestClearError();
+  this->section_->FakeClearError();
   ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
   EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
   EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
@@ -209,19 +185,19 @@
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
 
-  this->section_->TestClearError();
+  this->section_->FakeClearError();
   loc_regs.erase(CFA_REG);
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_INVALID, {0, 0}};
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
 
-  this->section_->TestClearError();
+  this->section_->FakeClearError();
   loc_regs.erase(CFA_REG);
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_OFFSET, {0, 0}};
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
 
-  this->section_->TestClearError();
+  this->section_->FakeClearError();
   loc_regs.erase(CFA_REG);
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0, 0}};
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
@@ -566,7 +542,7 @@
 
   DwarfLocations cie_loc_regs;
   cie_loc_regs[6] = DwarfLocation{DWARF_LOCATION_REGISTER, {4, 0}};
-  this->section_->TestSetCachedCieLocRegs(0x8000, cie_loc_regs);
+  this->section_->FakeSetCachedCieLocRegs(0x8000, cie_loc_regs);
   this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
 
   DwarfLocations loc_regs;
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index e1ed885..55f1d3a 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -22,8 +22,8 @@
 #include <unwindstack/DwarfSection.h>
 #include <unwindstack/Elf.h>
 
-#include "MemoryFake.h"
 #include "RegsFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 65eef20..1687a4c 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -15,17 +15,24 @@
  */
 
 #include <elf.h>
+#include <sys/mman.h>
 #include <unistd.h>
 
+#include <memory>
+
 #include <android-base/file.h>
 
 #include <gtest/gtest.h>
 
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
 
+#include "ElfFake.h"
 #include "ElfTestUtils.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
+
+#include <inttypes.h>
 
 namespace unwindstack {
 
@@ -33,231 +40,337 @@
  protected:
   static void SetUpTestSuite() { memory_.reset(new MemoryFake); }
 
-  void SetUp() override { Elf::SetCachingEnabled(true); }
+  void SetUp() override {
+    Elf::SetCachingEnabled(true);
 
-  void TearDown() override { Elf::SetCachingEnabled(false); }
+    // Create maps for testing.
+    maps_.reset(
+        new BufferMaps("1000-2000 r-xs 00000000 00:00 0 elf_one.so\n"
+                       "2000-3000 r-xs 00000000 00:00 0 elf_two.so\n"
+                       "3000-4000 ---s 00000000 00:00 0\n"
+                       "4000-5000 r--s 00000000 00:00 0 elf_three.so\n"
+                       "5000-6000 r-xs 00001000 00:00 0 elf_three.so\n"
+                       "6000-7000 ---s 00000000 00:00 0\n"
+                       "7000-8000 r--s 00001000 00:00 0 app_one.apk\n"
+                       "8000-9000 r-xs 00005000 00:00 0 app_one.apk\n"
+                       "9000-a000 r--s 00004000 00:00 0 app_two.apk\n"
+                       "a000-b000 r-xs 00005000 00:00 0 app_two.apk\n"
+                       "b000-c000 r--s 00008000 00:00 0 app_two.apk\n"
+                       "c000-d000 r-xs 00009000 00:00 0 app_two.apk\n"
+                       "d000-e000 ---s 00000000 00:00 0\n"
+                       "e000-f000 r-xs 00000000 00:00 0 invalid\n"
+                       "f000-10000 r-xs 00000000 00:00 0 invalid\n"
+                       "10000-11000 r-xs 00000000 00:00 0 elf_two.so\n"
+                       "11000-12000 r-xs 00000000 00:00 0 elf_one.so\n"
+                       "12000-13000 r--s 00000000 00:00 0 elf_three.so\n"
+                       "13000-14000 r-xs 00001000 00:00 0 elf_three.so\n"
+                       "14000-15000 ---s 00000000 00:00 0\n"
+                       "15000-16000 r--s 00001000 00:00 0 app_one.apk\n"
+                       "16000-17000 r-xs 00005000 00:00 0 app_one.apk\n"
+                       "17000-18000 r--s 00004000 00:00 0 app_two.apk\n"
+                       "18000-19000 r-xs 00005000 00:00 0 app_two.apk\n"
+                       "19000-1a000 r--s 00008000 00:00 0 app_two.apk\n"
+                       "1a000-1b000 r-xs 00009000 00:00 0 app_two.apk\n"));
+    ASSERT_TRUE(maps_->Parse());
 
-  void WriteElfFile(uint64_t offset, TemporaryFile* tf, uint32_t type) {
-    ASSERT_TRUE(type == EM_ARM || type == EM_386 || type == EM_X86_64);
-    size_t ehdr_size;
-    Elf32_Ehdr ehdr32;
-    Elf64_Ehdr ehdr64;
-    void* ptr;
-    if (type == EM_ARM || type == EM_386) {
-      ehdr_size = sizeof(ehdr32);
-      ptr = &ehdr32;
-      TestInitEhdr(&ehdr32, ELFCLASS32, type);
-    } else {
-      ehdr_size = sizeof(ehdr64);
-      ptr = &ehdr64;
-      TestInitEhdr(&ehdr64, ELFCLASS64, type);
+    std::unordered_map<std::string, std::string> renames;
+
+    temps_.emplace_back(new TemporaryFile);
+    renames["elf_one.so"] = temps_.back()->path;
+    WriteElfFile(0, temps_.back().get());
+
+    temps_.emplace_back(new TemporaryFile);
+    renames["elf_two.so"] = temps_.back()->path;
+    WriteElfFile(0, temps_.back().get());
+
+    temps_.emplace_back(new TemporaryFile);
+    renames["elf_three.so"] = temps_.back()->path;
+    WriteElfFile(0, temps_.back().get());
+
+    temps_.emplace_back(new TemporaryFile);
+    renames["app_one.apk"] = temps_.back()->path;
+    WriteElfFile(0x1000, temps_.back().get());
+    WriteElfFile(0x5000, temps_.back().get());
+
+    temps_.emplace_back(new TemporaryFile);
+    renames["app_two.apk"] = temps_.back()->path;
+    WriteElfFile(0x4000, temps_.back().get());
+    WriteElfFile(0x8000, temps_.back().get());
+
+    for (auto& map_info : *maps_) {
+      if (!map_info->name().empty()) {
+        if (renames.count(map_info->name()) != 0) {
+          // Replace the name with the temporary file name.
+          map_info->name() = renames.at(map_info->name());
+        }
+      }
     }
-
-    ASSERT_EQ(offset, static_cast<uint64_t>(lseek(tf->fd, offset, SEEK_SET)));
-    ASSERT_TRUE(android::base::WriteFully(tf->fd, ptr, ehdr_size));
   }
 
-  void VerifyWithinSameMap(bool cache_enabled);
-  void VerifySameMap(bool cache_enabled);
-  void VerifyWithinSameMapNeverReadAtZero(bool cache_enabled);
+  // Make sure the cache is cleared between runs.
+  void TearDown() override { Elf::SetCachingEnabled(false); }
 
+  void WriteElfFile(uint64_t offset, TemporaryFile* tf) {
+    Elf32_Ehdr ehdr;
+    TestInitEhdr(&ehdr, ELFCLASS32, EM_ARM);
+    Elf32_Shdr shdr = {};
+    shdr.sh_type = SHT_NULL;
+
+    ehdr.e_shnum = 1;
+    ehdr.e_shoff = 0x2000;
+    ehdr.e_shentsize = sizeof(shdr);
+
+    ASSERT_EQ(offset, static_cast<uint64_t>(lseek(tf->fd, offset, SEEK_SET)));
+    ASSERT_TRUE(android::base::WriteFully(tf->fd, &ehdr, sizeof(ehdr)));
+    ASSERT_EQ(offset + 0x2000, static_cast<uint64_t>(lseek(tf->fd, offset + 0x2000, SEEK_SET)));
+    ASSERT_TRUE(android::base::WriteFully(tf->fd, &shdr, sizeof(shdr)));
+  }
+
+  std::vector<std::unique_ptr<TemporaryFile>> temps_;
+  std::unique_ptr<Maps> maps_;
   static std::shared_ptr<Memory> memory_;
 };
 
 std::shared_ptr<Memory> ElfCacheTest::memory_;
 
-void ElfCacheTest::VerifySameMap(bool cache_enabled) {
-  if (!cache_enabled) {
-    Elf::SetCachingEnabled(false);
-  }
+TEST_F(ElfCacheTest, verify_elf_caching) {
+  Elf* elf_one = maps_->Find(0x1000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(elf_one->valid());
+  Elf* elf_two = maps_->Find(0x2000)->GetElf(memory_, ARCH_ARM);
+  EXPECT_TRUE(elf_two->valid());
+  Elf* elf_three = maps_->Find(0x4000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(elf_three->valid());
 
-  TemporaryFile tf;
-  ASSERT_TRUE(tf.fd != -1);
-  WriteElfFile(0, &tf, EM_ARM);
-  close(tf.fd);
+  // Check that the caching is working for elf files.
+  EXPECT_EQ(maps_->Find(0x5000)->GetElf(memory_, ARCH_ARM), elf_three);
+  EXPECT_EQ(0U, maps_->Find(0x5000)->elf_start_offset());
+  EXPECT_EQ(0x1000U, maps_->Find(0x5000)->elf_offset());
+  EXPECT_EQ(0x1000U, maps_->Find(0x5000)->offset());
 
-  uint64_t start = 0x1000;
-  uint64_t end = 0x20000;
-  MapInfo info1(nullptr, nullptr, start, end, 0, 0x5, tf.path);
-  MapInfo info2(nullptr, nullptr, start, end, 0, 0x5, tf.path);
+  EXPECT_EQ(maps_->Find(0x10000)->GetElf(memory_, ARCH_ARM), elf_two);
+  EXPECT_EQ(0U, maps_->Find(0x10000)->elf_start_offset());
+  EXPECT_EQ(0U, maps_->Find(0x10000)->elf_offset());
+  EXPECT_EQ(0U, maps_->Find(0x10000)->offset());
 
-  Elf* elf1 = info1.GetElf(memory_, ARCH_ARM);
-  ASSERT_TRUE(elf1->valid());
-  Elf* elf2 = info2.GetElf(memory_, ARCH_ARM);
-  ASSERT_TRUE(elf2->valid());
+  EXPECT_EQ(maps_->Find(0x11000)->GetElf(memory_, ARCH_ARM), elf_one);
+  EXPECT_EQ(0U, maps_->Find(0x11000)->elf_start_offset());
+  EXPECT_EQ(0U, maps_->Find(0x11000)->elf_offset());
+  EXPECT_EQ(0U, maps_->Find(0x11000)->offset());
 
-  if (cache_enabled) {
-    EXPECT_EQ(elf1, elf2);
-  } else {
-    EXPECT_NE(elf1, elf2);
-  }
+  EXPECT_EQ(maps_->Find(0x12000)->GetElf(memory_, ARCH_ARM), elf_three);
+  EXPECT_EQ(0U, maps_->Find(0x12000)->elf_start_offset());
+  EXPECT_EQ(0U, maps_->Find(0x12000)->elf_offset());
+  EXPECT_EQ(0U, maps_->Find(0x12000)->offset());
+
+  EXPECT_EQ(maps_->Find(0x13000)->GetElf(memory_, ARCH_ARM), elf_three);
+  EXPECT_EQ(0U, maps_->Find(0x13000)->elf_start_offset());
+  EXPECT_EQ(0x1000U, maps_->Find(0x13000)->elf_offset());
+  EXPECT_EQ(0x1000U, maps_->Find(0x13000)->offset());
 }
 
-TEST_F(ElfCacheTest, no_caching) {
-  VerifySameMap(false);
+TEST_F(ElfCacheTest, verify_elf_caching_ro_first_ro_second) {
+  Elf* elf_three = maps_->Find(0x4000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(elf_three->valid());
+
+  EXPECT_EQ(maps_->Find(0x12000)->GetElf(memory_, ARCH_ARM), elf_three);
+  EXPECT_EQ(0U, maps_->Find(0x12000)->elf_start_offset());
+  EXPECT_EQ(0U, maps_->Find(0x12000)->elf_offset());
+  EXPECT_EQ(0U, maps_->Find(0x12000)->offset());
 }
 
-TEST_F(ElfCacheTest, caching_invalid_elf) {
-  VerifySameMap(true);
+TEST_F(ElfCacheTest, verify_elf_caching_ro_first_rx_second) {
+  Elf* elf_three = maps_->Find(0x4000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(elf_three->valid());
+
+  EXPECT_EQ(maps_->Find(0x13000)->GetElf(memory_, ARCH_ARM), elf_three);
+  EXPECT_EQ(0U, maps_->Find(0x13000)->elf_start_offset());
+  EXPECT_EQ(0x1000U, maps_->Find(0x13000)->elf_offset());
+  EXPECT_EQ(0x1000U, maps_->Find(0x13000)->offset());
 }
 
-void ElfCacheTest::VerifyWithinSameMap(bool cache_enabled) {
-  if (!cache_enabled) {
-    Elf::SetCachingEnabled(false);
-  }
+TEST_F(ElfCacheTest, verify_elf_caching_rx_first_ro_second) {
+  Elf* elf_three = maps_->Find(0x5000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(elf_three->valid());
 
-  TemporaryFile tf;
-  ASSERT_TRUE(tf.fd != -1);
-  WriteElfFile(0, &tf, EM_ARM);
-  WriteElfFile(0x100, &tf, EM_386);
-  WriteElfFile(0x200, &tf, EM_X86_64);
-  lseek(tf.fd, 0x500, SEEK_SET);
-  uint8_t value = 0;
-  write(tf.fd, &value, 1);
-  close(tf.fd);
-
-  uint64_t start = 0x1000;
-  uint64_t end = 0x20000;
-  // Will have an elf at offset 0 in file.
-  MapInfo info0_1(nullptr, nullptr, start, end, 0, 0x5, tf.path);
-  MapInfo info0_2(nullptr, nullptr, start, end, 0, 0x5, tf.path);
-  // Will have an elf at offset 0x100 in file.
-  MapInfo info100_1(nullptr, nullptr, start, end, 0x100, 0x5, tf.path);
-  MapInfo info100_2(nullptr, nullptr, start, end, 0x100, 0x5, tf.path);
-  // Will have an elf at offset 0x200 in file.
-  MapInfo info200_1(nullptr, nullptr, start, end, 0x200, 0x5, tf.path);
-  MapInfo info200_2(nullptr, nullptr, start, end, 0x200, 0x5, tf.path);
-  // Will have an elf at offset 0 in file.
-  MapInfo info300_1(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
-  MapInfo info300_2(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
-
-  Elf* elf0_1 = info0_1.GetElf(memory_, ARCH_ARM);
-  ASSERT_TRUE(elf0_1->valid());
-  EXPECT_EQ(ARCH_ARM, elf0_1->arch());
-  Elf* elf0_2 = info0_2.GetElf(memory_, ARCH_ARM);
-  ASSERT_TRUE(elf0_2->valid());
-  EXPECT_EQ(ARCH_ARM, elf0_2->arch());
-  EXPECT_EQ(0U, info0_1.elf_offset());
-  EXPECT_EQ(0U, info0_2.elf_offset());
-  if (cache_enabled) {
-    EXPECT_EQ(elf0_1, elf0_2);
-  } else {
-    EXPECT_NE(elf0_1, elf0_2);
-  }
-
-  Elf* elf100_1 = info100_1.GetElf(memory_, ARCH_X86);
-  ASSERT_TRUE(elf100_1->valid());
-  EXPECT_EQ(ARCH_X86, elf100_1->arch());
-  Elf* elf100_2 = info100_2.GetElf(memory_, ARCH_X86);
-  ASSERT_TRUE(elf100_2->valid());
-  EXPECT_EQ(ARCH_X86, elf100_2->arch());
-  EXPECT_EQ(0U, info100_1.elf_offset());
-  EXPECT_EQ(0U, info100_2.elf_offset());
-  if (cache_enabled) {
-    EXPECT_EQ(elf100_1, elf100_2);
-  } else {
-    EXPECT_NE(elf100_1, elf100_2);
-  }
-
-  Elf* elf200_1 = info200_1.GetElf(memory_, ARCH_X86_64);
-  ASSERT_TRUE(elf200_1->valid());
-  EXPECT_EQ(ARCH_X86_64, elf200_1->arch());
-  Elf* elf200_2 = info200_2.GetElf(memory_, ARCH_X86_64);
-  ASSERT_TRUE(elf200_2->valid());
-  EXPECT_EQ(ARCH_X86_64, elf200_2->arch());
-  EXPECT_EQ(0U, info200_1.elf_offset());
-  EXPECT_EQ(0U, info200_2.elf_offset());
-  if (cache_enabled) {
-    EXPECT_EQ(elf200_1, elf200_2);
-  } else {
-    EXPECT_NE(elf200_1, elf200_2);
-  }
-
-  Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
-  ASSERT_TRUE(elf300_1->valid());
-  EXPECT_EQ(ARCH_ARM, elf300_1->arch());
-  Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
-  ASSERT_TRUE(elf300_2->valid());
-  EXPECT_EQ(ARCH_ARM, elf300_2->arch());
-  EXPECT_EQ(0x300U, info300_1.elf_offset());
-  EXPECT_EQ(0x300U, info300_2.elf_offset());
-  if (cache_enabled) {
-    EXPECT_EQ(elf300_1, elf300_2);
-    EXPECT_EQ(elf0_1, elf300_1);
-  } else {
-    EXPECT_NE(elf300_1, elf300_2);
-    EXPECT_NE(elf0_1, elf300_1);
-  }
+  EXPECT_EQ(maps_->Find(0x12000)->GetElf(memory_, ARCH_ARM), elf_three);
+  EXPECT_EQ(0U, maps_->Find(0x12000)->elf_start_offset());
+  EXPECT_EQ(0U, maps_->Find(0x12000)->elf_offset());
+  EXPECT_EQ(0U, maps_->Find(0x12000)->offset());
 }
 
-TEST_F(ElfCacheTest, no_caching_valid_elf_offset_non_zero) {
-  VerifyWithinSameMap(false);
+TEST_F(ElfCacheTest, verify_elf_caching_rx_first_rx_second) {
+  Elf* elf_three = maps_->Find(0x5000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(elf_three->valid());
+
+  EXPECT_EQ(maps_->Find(0x13000)->GetElf(memory_, ARCH_ARM), elf_three);
+  EXPECT_EQ(0U, maps_->Find(0x13000)->elf_start_offset());
+  EXPECT_EQ(0x1000U, maps_->Find(0x13000)->elf_offset());
+  EXPECT_EQ(0x1000U, maps_->Find(0x13000)->offset());
 }
 
-TEST_F(ElfCacheTest, caching_valid_elf_offset_non_zero) {
-  VerifyWithinSameMap(true);
+TEST_F(ElfCacheTest, verify_elf_apk_caching) {
+  Elf* app_one_elf1 = maps_->Find(0x7000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_one_elf1->valid());
+  Elf* app_one_elf2 = maps_->Find(0x8000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_one_elf2->valid());
+  Elf* app_two_elf1 = maps_->Find(0x9000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_two_elf1->valid());
+  Elf* app_two_elf2 = maps_->Find(0xb000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_two_elf2->valid());
+
+  // Check that the caching is working for elf files in apks.
+  EXPECT_EQ(maps_->Find(0xa000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+  EXPECT_EQ(0x4000U, maps_->Find(0xa000)->elf_start_offset());
+  EXPECT_EQ(0x1000U, maps_->Find(0xa000)->elf_offset());
+  EXPECT_EQ(0x5000U, maps_->Find(0xa000)->offset());
+
+  EXPECT_EQ(maps_->Find(0xc000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+  EXPECT_EQ(0x8000U, maps_->Find(0xc000)->elf_start_offset());
+  EXPECT_EQ(0x1000U, maps_->Find(0xc000)->elf_offset());
+  EXPECT_EQ(0x9000U, maps_->Find(0xc000)->offset());
+
+  EXPECT_EQ(maps_->Find(0x15000)->GetElf(memory_, ARCH_ARM), app_one_elf1);
+  EXPECT_EQ(0x1000U, maps_->Find(0x15000)->elf_start_offset());
+  EXPECT_EQ(0U, maps_->Find(0x15000)->elf_offset());
+  EXPECT_EQ(0x1000U, maps_->Find(0x15000)->offset());
+
+  EXPECT_EQ(maps_->Find(0x16000)->GetElf(memory_, ARCH_ARM), app_one_elf2);
+  EXPECT_EQ(0x1000U, maps_->Find(0x16000)->elf_start_offset());
+  EXPECT_EQ(0x4000U, maps_->Find(0x16000)->elf_offset());
+  EXPECT_EQ(0x5000U, maps_->Find(0x16000)->offset());
+
+  EXPECT_EQ(maps_->Find(0x17000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+  EXPECT_EQ(0x4000U, maps_->Find(0x17000)->elf_start_offset());
+  EXPECT_EQ(0U, maps_->Find(0x17000)->elf_offset());
+  EXPECT_EQ(0x4000U, maps_->Find(0x17000)->offset());
+
+  EXPECT_EQ(maps_->Find(0x18000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+  EXPECT_EQ(0x4000U, maps_->Find(0x18000)->elf_start_offset());
+  EXPECT_EQ(0x1000U, maps_->Find(0x18000)->elf_offset());
+  EXPECT_EQ(0x5000U, maps_->Find(0x18000)->offset());
+
+  EXPECT_EQ(maps_->Find(0x19000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+  EXPECT_EQ(0x8000U, maps_->Find(0x19000)->elf_start_offset());
+  EXPECT_EQ(0U, maps_->Find(0x19000)->elf_offset());
+  EXPECT_EQ(0x8000U, maps_->Find(0x19000)->offset());
+
+  EXPECT_EQ(maps_->Find(0x1a000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+  EXPECT_EQ(0x8000U, maps_->Find(0x1a000)->elf_start_offset());
+  EXPECT_EQ(0x1000U, maps_->Find(0x1a000)->elf_offset());
+  EXPECT_EQ(0x9000U, maps_->Find(0x1a000)->offset());
 }
 
-// Verify that when reading from multiple non-zero offsets in the same map
-// that when cached, all of the elf objects are the same.
-void ElfCacheTest::VerifyWithinSameMapNeverReadAtZero(bool cache_enabled) {
-  if (!cache_enabled) {
-    Elf::SetCachingEnabled(false);
-  }
+TEST_F(ElfCacheTest, verify_elf_apk_caching_ro_first_ro_second) {
+  Elf* app_two_elf1 = maps_->Find(0x9000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_two_elf1->valid());
+  Elf* app_two_elf2 = maps_->Find(0xb000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_two_elf2->valid());
 
-  TemporaryFile tf;
-  ASSERT_TRUE(tf.fd != -1);
-  WriteElfFile(0, &tf, EM_ARM);
-  lseek(tf.fd, 0x500, SEEK_SET);
-  uint8_t value = 0;
-  write(tf.fd, &value, 1);
-  close(tf.fd);
+  EXPECT_EQ(maps_->Find(0x17000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+  EXPECT_EQ(0x4000U, maps_->Find(0x17000)->elf_start_offset());
+  EXPECT_EQ(0U, maps_->Find(0x17000)->elf_offset());
+  EXPECT_EQ(0x4000U, maps_->Find(0x17000)->offset());
 
-  uint64_t start = 0x1000;
-  uint64_t end = 0x20000;
-  // Multiple info sections at different offsets will have non-zero elf offsets.
-  MapInfo info300_1(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
-  MapInfo info300_2(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
-  MapInfo info400_1(nullptr, nullptr, start, end, 0x400, 0x5, tf.path);
-  MapInfo info400_2(nullptr, nullptr, start, end, 0x400, 0x5, tf.path);
-
-  Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
-  ASSERT_TRUE(elf300_1->valid());
-  EXPECT_EQ(ARCH_ARM, elf300_1->arch());
-  Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
-  ASSERT_TRUE(elf300_2->valid());
-  EXPECT_EQ(ARCH_ARM, elf300_2->arch());
-  EXPECT_EQ(0x300U, info300_1.elf_offset());
-  EXPECT_EQ(0x300U, info300_2.elf_offset());
-  if (cache_enabled) {
-    EXPECT_EQ(elf300_1, elf300_2);
-  } else {
-    EXPECT_NE(elf300_1, elf300_2);
-  }
-
-  Elf* elf400_1 = info400_1.GetElf(memory_, ARCH_ARM);
-  ASSERT_TRUE(elf400_1->valid());
-  EXPECT_EQ(ARCH_ARM, elf400_1->arch());
-  Elf* elf400_2 = info400_2.GetElf(memory_, ARCH_ARM);
-  ASSERT_TRUE(elf400_2->valid());
-  EXPECT_EQ(ARCH_ARM, elf400_2->arch());
-  EXPECT_EQ(0x400U, info400_1.elf_offset());
-  EXPECT_EQ(0x400U, info400_2.elf_offset());
-  if (cache_enabled) {
-    EXPECT_EQ(elf400_1, elf400_2);
-    EXPECT_EQ(elf300_1, elf400_1);
-  } else {
-    EXPECT_NE(elf400_1, elf400_2);
-    EXPECT_NE(elf300_1, elf400_1);
-  }
+  EXPECT_EQ(maps_->Find(0x19000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+  EXPECT_EQ(0x8000U, maps_->Find(0x19000)->elf_start_offset());
+  EXPECT_EQ(0U, maps_->Find(0x19000)->elf_offset());
+  EXPECT_EQ(0x8000U, maps_->Find(0x19000)->offset());
 }
 
-TEST_F(ElfCacheTest, no_caching_valid_elf_offset_non_zero_never_read_at_zero) {
-  VerifyWithinSameMapNeverReadAtZero(false);
+TEST_F(ElfCacheTest, verify_elf_apk_caching_ro_first_rx_second) {
+  Elf* app_two_elf1 = maps_->Find(0x9000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_two_elf1->valid());
+  Elf* app_two_elf2 = maps_->Find(0xb000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_two_elf2->valid());
+
+  EXPECT_EQ(maps_->Find(0x18000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+  EXPECT_EQ(0x4000U, maps_->Find(0x18000)->elf_start_offset());
+  EXPECT_EQ(0x1000U, maps_->Find(0x18000)->elf_offset());
+  EXPECT_EQ(0x5000U, maps_->Find(0x18000)->offset());
+
+  EXPECT_EQ(maps_->Find(0x1a000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+  EXPECT_EQ(0x8000U, maps_->Find(0x1a000)->elf_start_offset());
+  EXPECT_EQ(0x1000U, maps_->Find(0x1a000)->elf_offset());
+  EXPECT_EQ(0x9000U, maps_->Find(0x1a000)->offset());
 }
 
-TEST_F(ElfCacheTest, caching_valid_elf_offset_non_zero_never_read_at_zero) {
-  VerifyWithinSameMapNeverReadAtZero(true);
+TEST_F(ElfCacheTest, verify_elf_apk_caching_rx_first_ro_second) {
+  Elf* app_two_elf1 = maps_->Find(0xa000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_two_elf1->valid());
+  Elf* app_two_elf2 = maps_->Find(0xc000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_two_elf2->valid());
+
+  EXPECT_EQ(maps_->Find(0x17000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+  EXPECT_EQ(0x4000U, maps_->Find(0x17000)->elf_start_offset());
+  EXPECT_EQ(0U, maps_->Find(0x17000)->elf_offset());
+  EXPECT_EQ(0x4000U, maps_->Find(0x17000)->offset());
+
+  EXPECT_EQ(maps_->Find(0x19000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+  EXPECT_EQ(0x8000U, maps_->Find(0x19000)->elf_start_offset());
+  EXPECT_EQ(0U, maps_->Find(0x19000)->elf_offset());
+  EXPECT_EQ(0x8000U, maps_->Find(0x19000)->offset());
+}
+
+TEST_F(ElfCacheTest, verify_elf_apk_caching_rx_first_rx_second) {
+  Elf* app_two_elf1 = maps_->Find(0x9000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_two_elf1->valid());
+  Elf* app_two_elf2 = maps_->Find(0xb000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_two_elf2->valid());
+
+  EXPECT_EQ(maps_->Find(0x17000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+  EXPECT_EQ(0x4000U, maps_->Find(0x17000)->elf_start_offset());
+  EXPECT_EQ(0U, maps_->Find(0x17000)->elf_offset());
+  EXPECT_EQ(0x4000U, maps_->Find(0x17000)->offset());
+
+  EXPECT_EQ(maps_->Find(0x19000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+  EXPECT_EQ(0x8000U, maps_->Find(0x19000)->elf_start_offset());
+  EXPECT_EQ(0U, maps_->Find(0x19000)->elf_offset());
+  EXPECT_EQ(0x8000U, maps_->Find(0x19000)->offset());
+}
+
+// Verify that with elf caching disabled, we aren't caching improperly.
+TEST_F(ElfCacheTest, verify_disable_elf_caching) {
+  Elf::SetCachingEnabled(false);
+
+  Elf* elf_one = maps_->Find(0x1000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(elf_one->valid());
+  Elf* elf_two = maps_->Find(0x2000)->GetElf(memory_, ARCH_ARM);
+  EXPECT_TRUE(elf_two->valid());
+  Elf* elf_three = maps_->Find(0x4000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(elf_three->valid());
+  EXPECT_EQ(maps_->Find(0x5000)->GetElf(memory_, ARCH_ARM), elf_three);
+
+  EXPECT_NE(maps_->Find(0x10000)->GetElf(memory_, ARCH_ARM), elf_two);
+  EXPECT_NE(maps_->Find(0x11000)->GetElf(memory_, ARCH_ARM), elf_one);
+  EXPECT_NE(maps_->Find(0x12000)->GetElf(memory_, ARCH_ARM), elf_three);
+  EXPECT_NE(maps_->Find(0x13000)->GetElf(memory_, ARCH_ARM), elf_three);
+
+  Elf* app_one_elf1 = maps_->Find(0x7000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_one_elf1->valid());
+  Elf* app_one_elf2 = maps_->Find(0x8000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_one_elf2->valid());
+  Elf* app_two_elf1 = maps_->Find(0x9000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_two_elf1->valid());
+  EXPECT_EQ(maps_->Find(0xa000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+  Elf* app_two_elf2 = maps_->Find(0xb000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_TRUE(app_two_elf2->valid());
+  EXPECT_EQ(maps_->Find(0xc000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+
+  EXPECT_NE(maps_->Find(0x15000)->GetElf(memory_, ARCH_ARM), app_one_elf1);
+  EXPECT_NE(maps_->Find(0x16000)->GetElf(memory_, ARCH_ARM), app_one_elf2);
+  EXPECT_NE(maps_->Find(0x17000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+  EXPECT_NE(maps_->Find(0x18000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+  EXPECT_NE(maps_->Find(0x19000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+  EXPECT_NE(maps_->Find(0x1a000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+}
+
+// Verify that invalid elf objects are not cached.
+TEST_F(ElfCacheTest, verify_invalid_not_cached) {
+  Elf* invalid_elf1 = maps_->Find(0xe000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_FALSE(invalid_elf1->valid());
+  Elf* invalid_elf2 = maps_->Find(0xf000)->GetElf(memory_, ARCH_ARM);
+  ASSERT_FALSE(invalid_elf2->valid());
+  ASSERT_NE(invalid_elf1, invalid_elf2);
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index 80c3e31..3da673e 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -26,7 +26,7 @@
 #include <unwindstack/SharedString.h>
 
 #include "ElfFake.h"
-#include "RegsFake.h"
+#include "utils/RegsFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index 8c5b9ef..97d6dcc 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
-#define _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
+#pragma once
 
 #include <stdint.h>
 
@@ -152,5 +151,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 43c6a97..ee70cc0 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -26,7 +26,7 @@
 #include "ElfInterfaceArm.h"
 
 #include "ElfFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index d250d0a..47ae3d0 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -26,7 +26,7 @@
 #include "ElfInterfaceArm.h"
 
 #include "ElfFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 #if !defined(PT_ARM_EXIDX)
 #define PT_ARM_EXIDX 0x70000001
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index c641f52..f16c6e5 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -31,7 +31,7 @@
 #include "ElfFake.h"
 #include "ElfTestUtils.h"
 #include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 #if !defined(PT_ARM_EXIDX)
 #define PT_ARM_EXIDX 0x70000001
@@ -172,8 +172,7 @@
   ASSERT_FALSE(elf.Init());
 
   ASSERT_EQ("", GetFakeLogBuf());
-  ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86 nor mips: e_machine = 20\n\n",
-            GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogPrint());
 }
 
 TEST_F(ElfTest, elf64_invalid_machine) {
@@ -185,8 +184,7 @@
   ASSERT_FALSE(elf.Init());
 
   ASSERT_EQ("", GetFakeLogBuf());
-  ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = 21\n\n",
-            GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogPrint());
 }
 
 TEST_F(ElfTest, elf_arm) {
@@ -296,12 +294,12 @@
   elf.FakeSetInterface(interface);
 
   elf.FakeSetValid(true);
-  MapInfo map_info(nullptr, nullptr, 0x1000, 0x2000, 0, 0, "");
+  auto map_info = MapInfo::Create(0x1000, 0x2000, 0, 0, "");
 
-  ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+  ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, map_info.get()));
 
   elf.FakeSetValid(false);
-  ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+  ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, map_info.get()));
 }
 
 void ElfTest::VerifyStepIfSignalHandler(uint64_t load_bias) {
@@ -553,4 +551,14 @@
   EXPECT_EQ(0x1000U, elf.GetLastErrorAddress());
 }
 
+TEST(ElfBuildIdTest, get_printable_build_id_empty) {
+  std::string empty;
+  ASSERT_EQ("", Elf::GetPrintableBuildID(empty));
+}
+
+TEST(ElfBuildIdTest, get_printable_build_id_check) {
+  std::string empty = {'\xff', '\x45', '\x40', '\x0f'};
+  ASSERT_EQ("ff45400f", Elf::GetPrintableBuildID(empty));
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.h b/libunwindstack/tests/ElfTestUtils.h
index 62cd59a..25f0052 100644
--- a/libunwindstack/tests/ElfTestUtils.h
+++ b/libunwindstack/tests/ElfTestUtils.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
-#define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
+#pragma once
 
 #include <functional>
 #include <string>
@@ -34,5 +33,3 @@
 std::string TestGetFileDirectory();
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
diff --git a/libunwindstack/tests/GlobalTest.cpp b/libunwindstack/tests/GlobalTest.cpp
new file mode 100644
index 0000000..b6893ba
--- /dev/null
+++ b/libunwindstack/tests/GlobalTest.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2021 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Global.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+
+#include "ElfFake.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+
+namespace unwindstack {
+
+class GlobalMock : public Global {
+ public:
+  explicit GlobalMock(std::shared_ptr<Memory>& memory) : Global(memory) {}
+  GlobalMock(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+      : Global(memory, search_libs) {}
+
+  MOCK_METHOD(bool, ReadVariableData, (uint64_t), (override));
+
+  MOCK_METHOD(void, ProcessArch, (), (override));
+
+  void TestFindAndReadVariable(Maps* maps, const char* var_str) {
+    FindAndReadVariable(maps, var_str);
+  }
+};
+
+class GlobalTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    maps_.reset(
+        new BufferMaps("10000-11000 r--p 0000 00:00 0 first.so\n"
+                       "11000-12000 r-xp 1000 00:00 0 first.so\n"
+                       "12000-13000 rw-p 2000 00:00 0 first.so\n"
+
+                       "20000-22000 r--p 0000 00:00 0 second.so\n"
+                       "22000-23000 rw-p 2000 00:00 0 second.so\n"
+
+                       "30000-31000 r--p 0000 00:00 0 third.so\n"
+                       "31000-32000 ---p 0000 00:00 0\n"
+                       "32000-33000 r-xp 1000 00:00 0 third.so\n"
+                       "33000-34000 rw-p 2000 00:00 0 third.so\n"
+
+                       "40000-42000 r--p 0000 00:00 0 fourth.so\n"
+                       "42000-43000 rw-p 0000 00:00 0 fourth.so\n"));
+    ASSERT_TRUE(maps_->Parse());
+    ASSERT_EQ(11U, maps_->Total());
+
+    elf_fakes_.push_back(new ElfInterfaceFake(nullptr));
+    elf_fakes_.push_back(new ElfInterfaceFake(nullptr));
+    elf_fakes_.push_back(new ElfInterfaceFake(nullptr));
+    elf_fakes_.push_back(new ElfInterfaceFake(nullptr));
+
+    ElfFake* elf_fake = new ElfFake(nullptr);
+    elf_fake->FakeSetValid(true);
+    elf_fake->FakeSetInterface(elf_fakes_[0]);
+    elf_fakes_[0]->FakeSetDataVaddrStart(0x2000);
+    elf_fakes_[0]->FakeSetDataVaddrEnd(0x3000);
+    elf_fakes_[0]->FakeSetDataOffset(0x2000);
+    auto map_info = maps_->Find(0x10000);
+    map_info->GetElfFields().elf_.reset(elf_fake);
+
+    elf_fake = new ElfFake(nullptr);
+    elf_fake->FakeSetValid(true);
+    elf_fake->FakeSetInterface(elf_fakes_[1]);
+    elf_fakes_[1]->FakeSetDataVaddrStart(0x2000);
+    elf_fakes_[1]->FakeSetDataVaddrEnd(0x3000);
+    elf_fakes_[1]->FakeSetDataOffset(0x2000);
+    map_info = maps_->Find(0x20000);
+    map_info->GetElfFields().elf_.reset(elf_fake);
+
+    elf_fake = new ElfFake(nullptr);
+    elf_fake->FakeSetValid(true);
+    elf_fake->FakeSetInterface(elf_fakes_[2]);
+    elf_fakes_[2]->FakeSetDataVaddrStart(0x2000);
+    elf_fakes_[2]->FakeSetDataVaddrEnd(0x3000);
+    elf_fakes_[2]->FakeSetDataOffset(0x2000);
+    map_info = maps_->Find(0x30000);
+    map_info->GetElfFields().elf_.reset(elf_fake);
+
+    elf_fake = new ElfFake(nullptr);
+    elf_fake->FakeSetValid(true);
+    elf_fake->FakeSetInterface(elf_fakes_[3]);
+    elf_fakes_[3]->FakeSetDataVaddrStart(00);
+    elf_fakes_[3]->FakeSetDataVaddrEnd(0x1000);
+    elf_fakes_[3]->FakeSetDataOffset(0);
+    map_info = maps_->Find(0x40000);
+    map_info->GetElfFields().elf_.reset(elf_fake);
+
+    global_.reset(new GlobalMock(empty_));
+  }
+
+  std::shared_ptr<Memory> empty_;
+  std::unique_ptr<BufferMaps> maps_;
+  std::unique_ptr<GlobalMock> global_;
+  std::vector<ElfInterfaceFake*> elf_fakes_;
+};
+
+TEST_F(GlobalTest, ro_rx_rw) {
+  std::string global_var("fake_global");
+  elf_fakes_[0]->FakeSetGlobalVariable(global_var, 0x2010);
+  EXPECT_CALL(*global_, ReadVariableData(0x12010)).WillOnce(Return(true));
+
+  global_->TestFindAndReadVariable(maps_.get(), global_var.c_str());
+}
+
+TEST_F(GlobalTest, ro_rx_rw_searchable) {
+  std::vector<std::string> search_libs = {"first.so"};
+  global_.reset(new GlobalMock(empty_, search_libs));
+
+  std::string global_var("fake_global");
+  elf_fakes_[0]->FakeSetGlobalVariable(global_var, 0x2010);
+  EXPECT_CALL(*global_, ReadVariableData(0x12010)).WillOnce(Return(true));
+
+  global_->TestFindAndReadVariable(maps_.get(), global_var.c_str());
+}
+
+TEST_F(GlobalTest, ro_rx_rw_not_searchable) {
+  std::vector<std::string> search_libs = {"second.so"};
+  global_.reset(new GlobalMock(empty_, search_libs));
+
+  std::string global_var("fake_global");
+  elf_fakes_[0]->FakeSetGlobalVariable(global_var, 0x2010);
+
+  global_->TestFindAndReadVariable(maps_.get(), global_var.c_str());
+}
+
+TEST_F(GlobalTest, ro_rw) {
+  std::string global_var("fake_global");
+  elf_fakes_[1]->FakeSetGlobalVariable(global_var, 0x2010);
+  EXPECT_CALL(*global_, ReadVariableData(0x22010)).WillOnce(Return(true));
+
+  global_->TestFindAndReadVariable(maps_.get(), global_var.c_str());
+}
+
+TEST_F(GlobalTest, ro_blank_rx_rw) {
+  std::string global_var("fake_global");
+  elf_fakes_[2]->FakeSetGlobalVariable(global_var, 0x2010);
+  EXPECT_CALL(*global_, ReadVariableData(0x33010)).WillOnce(Return(true));
+
+  global_->TestFindAndReadVariable(maps_.get(), global_var.c_str());
+}
+
+TEST_F(GlobalTest, ro_rw_with_zero_offset) {
+  std::string global_var("fake_global");
+  elf_fakes_[3]->FakeSetGlobalVariable(global_var, 0x10);
+  EXPECT_CALL(*global_, ReadVariableData(0x42010)).WillOnce(Return(true));
+
+  global_->TestFindAndReadVariable(maps_.get(), global_var.c_str());
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
index 44378a8..66e220c 100644
--- a/libunwindstack/tests/JitDebugTest.cpp
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -29,7 +29,7 @@
 #include <unwindstack/Memory.h>
 
 #include "ElfFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
@@ -65,15 +65,15 @@
                        "200000-210000 rw-p 01ee000 00:00 0 /fake/elf4\n"));
     ASSERT_TRUE(maps_->Parse());
 
-    MapInfo* map_info = maps_->Get(3);
+    MapInfo* map_info = maps_->Get(3).get();
     ASSERT_TRUE(map_info != nullptr);
     CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
 
-    map_info = maps_->Get(5);
+    map_info = maps_->Get(5).get();
     ASSERT_TRUE(map_info != nullptr);
     CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
 
-    map_info = maps_->Get(7);
+    map_info = maps_->Get(7).get();
     ASSERT_TRUE(map_info != nullptr);
     CreateFakeElf(map_info, 0xee800, 0xee000, 0xee000, 0x10000);
   }
@@ -414,7 +414,7 @@
   EXPECT_TRUE(jit_debug_->Find(maps_.get(), 0x1500) == nullptr);
 
   // Change the name of the map that includes the value and verify this works.
-  MapInfo* map_info = maps_->Get(5);
+  auto map_info = maps_->Get(5);
   map_info->set_name("/system/lib/libart.so");
   map_info = maps_->Get(6);
   map_info->set_name("/system/lib/libart.so");
diff --git a/libunwindstack/tests/LocalUnwinderTest.cpp b/libunwindstack/tests/LocalUnwinderTest.cpp
deleted file mode 100644
index 5658aa3..0000000
--- a/libunwindstack/tests/LocalUnwinderTest.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * 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 <dlfcn.h>
-#include <inttypes.h>
-#include <signal.h>
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-#include <android-base/stringprintf.h>
-
-#include <unwindstack/LocalUnwinder.h>
-
-namespace unwindstack {
-
-static std::vector<LocalFrameData>* g_frame_info;
-static LocalUnwinder* g_unwinder;
-
-extern "C" void SignalLocalInnerFunction() {
-  g_unwinder->Unwind(g_frame_info, 256);
-}
-
-extern "C" void SignalLocalMiddleFunction() {
-  SignalLocalInnerFunction();
-}
-
-extern "C" void SignalLocalOuterFunction() {
-  SignalLocalMiddleFunction();
-}
-
-static void SignalLocalCallerHandler(int, siginfo_t*, void*) {
-  SignalLocalOuterFunction();
-}
-
-static std::string ErrorMsg(const std::vector<const char*>& function_names,
-                            const std::vector<LocalFrameData>& frame_info) {
-  std::string unwind;
-  size_t i = 0;
-  for (const auto& frame : frame_info) {
-    unwind += android::base::StringPrintf("#%02zu pc 0x%" PRIx64 " rel_pc 0x%" PRIx64, i++,
-                                          frame.pc, frame.rel_pc);
-    if (frame.map_info != nullptr) {
-      if (!frame.map_info->name().empty()) {
-        unwind += " ";
-        unwind += frame.map_info->name();
-      } else {
-        unwind += android::base::StringPrintf(" 0x%" PRIx64 "-0x%" PRIx64, frame.map_info->start(),
-                                              frame.map_info->end());
-      }
-      if (frame.map_info->offset() != 0) {
-        unwind += android::base::StringPrintf(" offset 0x%" PRIx64, frame.map_info->offset());
-      }
-    }
-    if (!frame.function_name.empty()) {
-      unwind += " " + frame.function_name;
-      if (frame.function_offset != 0) {
-        unwind += android::base::StringPrintf("+%" PRId64, frame.function_offset);
-      }
-    }
-    unwind += '\n';
-  }
-
-  return std::string(
-             "Unwind completed without finding all frames\n"
-             "  Looking for function: ") +
-         function_names.front() + "\n" + "Unwind data:\n" + unwind;
-}
-
-// This test assumes that this code is compiled with optimizations turned
-// off. If this doesn't happen, then all of the calls will be optimized
-// away.
-extern "C" void LocalInnerFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
-  std::vector<LocalFrameData> frame_info;
-  g_frame_info = &frame_info;
-  g_unwinder = unwinder;
-  std::vector<const char*> expected_function_names;
-
-  if (unwind_through_signal) {
-    struct sigaction act, oldact;
-    memset(&act, 0, sizeof(act));
-    act.sa_sigaction = SignalLocalCallerHandler;
-    act.sa_flags = SA_RESTART | SA_ONSTACK;
-    ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
-
-    raise(SIGUSR1);
-
-    ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
-
-    expected_function_names = {"LocalOuterFunction",        "LocalMiddleFunction",
-                               "LocalInnerFunction",        "SignalLocalOuterFunction",
-                               "SignalLocalMiddleFunction", "SignalLocalInnerFunction"};
-  } else {
-    ASSERT_TRUE(unwinder->Unwind(&frame_info, 256));
-
-    expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", "LocalInnerFunction"};
-  }
-
-  for (auto& frame : frame_info) {
-    if (frame.function_name == expected_function_names.back()) {
-      expected_function_names.pop_back();
-      if (expected_function_names.empty()) {
-        break;
-      }
-    }
-  }
-
-  ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
-}
-
-extern "C" void LocalMiddleFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
-  LocalInnerFunction(unwinder, unwind_through_signal);
-}
-
-extern "C" void LocalOuterFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
-  LocalMiddleFunction(unwinder, unwind_through_signal);
-}
-
-class LocalUnwinderTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    unwinder_.reset(new LocalUnwinder);
-    ASSERT_TRUE(unwinder_->Init());
-  }
-
-  std::unique_ptr<LocalUnwinder> unwinder_;
-};
-
-TEST_F(LocalUnwinderTest, local) {
-  LocalOuterFunction(unwinder_.get(), false);
-}
-
-TEST_F(LocalUnwinderTest, local_signal) {
-  LocalOuterFunction(unwinder_.get(), true);
-}
-
-TEST_F(LocalUnwinderTest, local_multiple) {
-  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
-
-  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
-
-  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
-
-  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
-}
-
-// This test verifies that doing an unwind before and after a dlopen
-// works. It's verifying that the maps read during the first unwind
-// do not cause a problem when doing the unwind using the code in
-// the dlopen'd code.
-TEST_F(LocalUnwinderTest, unwind_after_dlopen) {
-  // Prime the maps data.
-  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
-
-  std::string testlib(testing::internal::GetArgvs()[0]);
-  auto const value = testlib.find_last_of('/');
-  if (value != std::string::npos) {
-    testlib = testlib.substr(0, value + 1);
-  } else {
-    testlib = "";
-  }
-  testlib += "libunwindstack_local.so";
-
-  void* handle = dlopen(testlib.c_str(), RTLD_NOW);
-  ASSERT_TRUE(handle != nullptr);
-
-  void (*unwind_function)(void*, void*) =
-      reinterpret_cast<void (*)(void*, void*)>(dlsym(handle, "TestlibLevel1"));
-  ASSERT_TRUE(unwind_function != nullptr);
-
-  std::vector<LocalFrameData> frame_info;
-  unwind_function(unwinder_.get(), &frame_info);
-
-  ASSERT_EQ(0, dlclose(handle));
-
-  std::vector<const char*> expected_function_names{"TestlibLevel1", "TestlibLevel2",
-                                                   "TestlibLevel3", "TestlibLevel4"};
-
-  for (auto& frame : frame_info) {
-    if (frame.function_name == expected_function_names.back()) {
-      expected_function_names.pop_back();
-      if (expected_function_names.empty()) {
-        break;
-      }
-    }
-  }
-
-  ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
-}
-
-}  // namespace unwindstack
diff --git a/libunwindstack/tests/LocalUpdatableMapsTest.cpp b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
index 0632a44..0dbabcd 100644
--- a/libunwindstack/tests/LocalUpdatableMapsTest.cpp
+++ b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
@@ -36,8 +36,6 @@
 
   void TestSetMapsFile(const std::string& maps_file) { maps_file_ = maps_file; }
 
-  const std::vector<std::unique_ptr<MapInfo>>& TestGetSavedMaps() { return saved_maps_; }
-
  private:
   std::string maps_file_;
 };
@@ -56,7 +54,7 @@
     ASSERT_TRUE(maps_.Parse());
     ASSERT_EQ(2U, maps_.Total());
 
-    MapInfo* map_info = maps_.Get(0);
+    auto map_info = maps_.Get(0);
     ASSERT_TRUE(map_info != nullptr);
     EXPECT_EQ(0x3000U, map_info->start());
     EXPECT_EQ(0x4000U, map_info->end());
@@ -83,15 +81,16 @@
   maps_.TestSetMapsFile(tf.path);
   ASSERT_TRUE(maps_.Reparse());
   ASSERT_EQ(2U, maps_.Total());
-  EXPECT_EQ(0U, maps_.TestGetSavedMaps().size());
 
-  MapInfo* map_info = maps_.Get(0);
+  auto map_info = maps_.Get(0);
   ASSERT_TRUE(map_info != nullptr);
   EXPECT_EQ(0x3000U, map_info->start());
   EXPECT_EQ(0x4000U, map_info->end());
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
   EXPECT_TRUE(map_info->name().empty());
+  EXPECT_EQ(nullptr, map_info->prev_map());
+  EXPECT_EQ(maps_.Get(1), map_info->next_map());
 
   map_info = maps_.Get(1);
   ASSERT_TRUE(map_info != nullptr);
@@ -100,6 +99,8 @@
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
   EXPECT_TRUE(map_info->name().empty());
+  EXPECT_EQ(maps_.Get(0), map_info->prev_map());
+  EXPECT_EQ(nullptr, map_info->next_map());
 }
 
 TEST_F(LocalUpdatableMapsTest, same_map_new_perms) {
@@ -113,13 +114,15 @@
   ASSERT_TRUE(maps_.Reparse());
   ASSERT_EQ(2U, maps_.Total());
 
-  MapInfo* map_info = maps_.Get(0);
+  auto map_info = maps_.Get(0);
   ASSERT_TRUE(map_info != nullptr);
   EXPECT_EQ(0x3000U, map_info->start());
   EXPECT_EQ(0x4000U, map_info->end());
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, map_info->flags());
   EXPECT_TRUE(map_info->name().empty());
+  EXPECT_EQ(nullptr, map_info->prev_map());
+  EXPECT_EQ(maps_.Get(1), map_info->next_map());
 
   map_info = maps_.Get(1);
   ASSERT_TRUE(map_info != nullptr);
@@ -128,16 +131,8 @@
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
   EXPECT_TRUE(map_info->name().empty());
-
-  auto& saved_maps = maps_.TestGetSavedMaps();
-  ASSERT_EQ(1U, saved_maps.size());
-  map_info = saved_maps[0].get();
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x3000U, map_info->start());
-  EXPECT_EQ(0x4000U, map_info->end());
-  EXPECT_EQ(0U, map_info->offset());
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
-  EXPECT_TRUE(map_info->name().empty());
+  EXPECT_EQ(maps_.Get(0), map_info->prev_map());
+  EXPECT_EQ(nullptr, map_info->next_map());
 }
 
 TEST_F(LocalUpdatableMapsTest, same_map_new_name) {
@@ -151,13 +146,15 @@
   ASSERT_TRUE(maps_.Reparse());
   ASSERT_EQ(2U, maps_.Total());
 
-  MapInfo* map_info = maps_.Get(0);
+  auto map_info = maps_.Get(0);
   ASSERT_TRUE(map_info != nullptr);
   EXPECT_EQ(0x3000U, map_info->start());
   EXPECT_EQ(0x4000U, map_info->end());
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
   EXPECT_EQ("/fake/lib.so", map_info->name());
+  EXPECT_EQ(nullptr, map_info->prev_map());
+  EXPECT_EQ(maps_.Get(1), map_info->next_map());
 
   map_info = maps_.Get(1);
   ASSERT_TRUE(map_info != nullptr);
@@ -166,16 +163,8 @@
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
   EXPECT_TRUE(map_info->name().empty());
-
-  auto& saved_maps = maps_.TestGetSavedMaps();
-  ASSERT_EQ(1U, saved_maps.size());
-  map_info = saved_maps[0].get();
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x3000U, map_info->start());
-  EXPECT_EQ(0x4000U, map_info->end());
-  EXPECT_EQ(0U, map_info->offset());
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
-  EXPECT_TRUE(map_info->name().empty());
+  EXPECT_EQ(maps_.Get(0), map_info->prev_map());
+  EXPECT_EQ(nullptr, map_info->next_map());
 }
 
 TEST_F(LocalUpdatableMapsTest, only_add_maps) {
@@ -190,15 +179,16 @@
   maps_.TestSetMapsFile(tf.path);
   ASSERT_TRUE(maps_.Reparse());
   ASSERT_EQ(4U, maps_.Total());
-  EXPECT_EQ(0U, maps_.TestGetSavedMaps().size());
 
-  MapInfo* map_info = maps_.Get(0);
+  auto map_info = maps_.Get(0);
   ASSERT_TRUE(map_info != nullptr);
   EXPECT_EQ(0x1000U, map_info->start());
   EXPECT_EQ(0x2000U, map_info->end());
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
   EXPECT_TRUE(map_info->name().empty());
+  EXPECT_EQ(nullptr, map_info->prev_map());
+  EXPECT_EQ(maps_.Get(1), map_info->next_map());
 
   map_info = maps_.Get(1);
   ASSERT_TRUE(map_info != nullptr);
@@ -207,6 +197,8 @@
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
   EXPECT_TRUE(map_info->name().empty());
+  EXPECT_EQ(maps_.Get(0), map_info->prev_map());
+  EXPECT_EQ(maps_.Get(2), map_info->next_map());
 
   map_info = maps_.Get(2);
   ASSERT_TRUE(map_info != nullptr);
@@ -215,6 +207,8 @@
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
   EXPECT_TRUE(map_info->name().empty());
+  EXPECT_EQ(maps_.Get(1), map_info->prev_map());
+  EXPECT_EQ(maps_.Get(3), map_info->next_map());
 
   map_info = maps_.Get(3);
   ASSERT_TRUE(map_info != nullptr);
@@ -223,6 +217,8 @@
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
   EXPECT_TRUE(map_info->name().empty());
+  EXPECT_EQ(maps_.Get(2), map_info->prev_map());
+  EXPECT_EQ(nullptr, map_info->next_map());
 }
 
 TEST_F(LocalUpdatableMapsTest, all_new_maps) {
@@ -236,13 +232,15 @@
   ASSERT_TRUE(maps_.Reparse());
   ASSERT_EQ(2U, maps_.Total());
 
-  MapInfo* map_info = maps_.Get(0);
+  auto map_info = maps_.Get(0);
   ASSERT_TRUE(map_info != nullptr);
   EXPECT_EQ(0x1000U, map_info->start());
   EXPECT_EQ(0x2000U, map_info->end());
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
   EXPECT_TRUE(map_info->name().empty());
+  EXPECT_EQ(nullptr, map_info->prev_map());
+  EXPECT_EQ(maps_.Get(1), map_info->next_map());
 
   map_info = maps_.Get(1);
   ASSERT_TRUE(map_info != nullptr);
@@ -251,24 +249,8 @@
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
   EXPECT_TRUE(map_info->name().empty());
-
-  auto& saved_maps = maps_.TestGetSavedMaps();
-  ASSERT_EQ(2U, saved_maps.size());
-  map_info = saved_maps[0].get();
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x3000U, map_info->start());
-  EXPECT_EQ(0x4000U, map_info->end());
-  EXPECT_EQ(0U, map_info->offset());
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
-  EXPECT_TRUE(map_info->name().empty());
-
-  map_info = saved_maps[1].get();
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x8000U, map_info->start());
-  EXPECT_EQ(0x9000U, map_info->end());
-  EXPECT_EQ(0U, map_info->offset());
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
-  EXPECT_TRUE(map_info->name().empty());
+  EXPECT_EQ(maps_.Get(0), map_info->prev_map());
+  EXPECT_EQ(nullptr, map_info->next_map());
 }
 
 TEST_F(LocalUpdatableMapsTest, add_map_prev_name_updated) {
@@ -283,14 +265,21 @@
   ASSERT_TRUE(maps_.Reparse());
   ASSERT_EQ(3U, maps_.Total());
 
-  MapInfo* map_info = maps_.Get(2);
+  auto map_info = maps_.Get(2);
   ASSERT_TRUE(map_info != nullptr);
   EXPECT_EQ(0x9000U, map_info->start());
   EXPECT_EQ(0xA000U, map_info->end());
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
   EXPECT_TRUE(map_info->name().empty());
-  EXPECT_EQ(maps_.Get(1), map_info->prev_map());
+
+  // Check all of the prev and next pointers.
+  EXPECT_EQ(nullptr, maps_.Get(0)->prev_map());
+  EXPECT_EQ(maps_.Get(1), maps_.Get(0)->next_map());
+  EXPECT_EQ(maps_.Get(0), maps_.Get(1)->prev_map());
+  EXPECT_EQ(maps_.Get(2), maps_.Get(1)->next_map());
+  EXPECT_EQ(maps_.Get(1), maps_.Get(2)->prev_map());
+  EXPECT_EQ(nullptr, maps_.Get(2)->next_map());
 }
 
 TEST_F(LocalUpdatableMapsTest, add_map_prev_real_name_updated) {
@@ -298,7 +287,7 @@
   ASSERT_TRUE(
       android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
                                        "4000-5000 ---p 00000 00:00 0\n"
-                                       "7000-8000 r-xp 00000 00:00 0 /fake/lib1.so\n"
+                                       "7000-8000 r-xp 00000 00:00 0 /fake/lib.so\n"
                                        "8000-9000 ---p 00000 00:00 0\n",
                                        tf.path));
 
@@ -313,15 +302,16 @@
   ASSERT_EQ(4U, maps_.Total());
   ASSERT_FALSE(any_changed);
 
-  MapInfo* map_info = maps_.Get(2);
+  auto map_info = maps_.Get(2);
   ASSERT_TRUE(map_info != nullptr);
   EXPECT_EQ(0x7000U, map_info->start());
   EXPECT_EQ(0x8000U, map_info->end());
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
-  EXPECT_EQ(maps_.Get(0), map_info->prev_real_map());
+  EXPECT_EQ(maps_.Get(0), map_info->GetPrevRealMap());
   EXPECT_EQ(maps_.Get(1), map_info->prev_map());
-  EXPECT_EQ("/fake/lib1.so", map_info->name());
+  EXPECT_EQ(maps_.Get(3), map_info->next_map());
+  EXPECT_EQ("/fake/lib.so", map_info->name());
 
   map_info = maps_.Get(3);
   ASSERT_TRUE(map_info != nullptr);
@@ -329,14 +319,15 @@
   EXPECT_EQ(0x9000U, map_info->end());
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_TRUE(map_info->IsBlank());
-  EXPECT_EQ(maps_.Get(2), map_info->prev_real_map());
+  EXPECT_EQ(nullptr, map_info->GetPrevRealMap());
   EXPECT_EQ(maps_.Get(2), map_info->prev_map());
+  EXPECT_EQ(maps_.Get(4), map_info->next_map());
   EXPECT_TRUE(map_info->name().empty());
 
   ASSERT_TRUE(
       android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
                                        "4000-5000 ---p 00000 00:00 0\n"
-                                       "7000-8000 r-xp 00000 00:00 0 /fake/lib1.so\n"
+                                       "7000-8000 r-xp 00000 00:00 0 /fake/lib.so\n"
                                        "8000-9000 ---p 00000 00:00 0\n"
                                        "9000-a000 r-xp 00000 00:00 0 /fake/lib2.so\n"
                                        "a000-b000 r-xp 00000 00:00 0 /fake/lib3.so\n",
@@ -358,9 +349,10 @@
   EXPECT_EQ(0x8000U, map_info->end());
   EXPECT_EQ(0U, map_info->offset());
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
-  EXPECT_EQ("/fake/lib1.so", map_info->name());
+  EXPECT_EQ("/fake/lib.so", map_info->name());
   EXPECT_EQ(maps_.Get(1), map_info->prev_map());
-  EXPECT_EQ(maps_.Get(0), map_info->prev_real_map());
+  EXPECT_EQ(maps_.Get(0), map_info->GetPrevRealMap());
+  EXPECT_EQ(maps_.Get(3), map_info->next_map());
 
   map_info = maps_.Get(4);
   ASSERT_TRUE(map_info != nullptr);
@@ -370,7 +362,8 @@
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
   EXPECT_EQ("/fake/lib2.so", map_info->name());
   EXPECT_EQ(maps_.Get(3), map_info->prev_map());
-  EXPECT_EQ(maps_.Get(2), map_info->prev_real_map());
+  EXPECT_EQ(nullptr, map_info->GetPrevRealMap());
+  EXPECT_EQ(maps_.Get(5), map_info->next_map());
 
   map_info = maps_.Get(5);
   ASSERT_TRUE(map_info != nullptr);
@@ -380,7 +373,8 @@
   EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
   EXPECT_EQ("/fake/lib3.so", map_info->name());
   EXPECT_EQ(maps_.Get(4), map_info->prev_map());
-  EXPECT_EQ(maps_.Get(4), map_info->prev_real_map());
+  EXPECT_EQ(nullptr, map_info->GetPrevRealMap());
+  EXPECT_EQ(nullptr, map_info->next_map());
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/LogFake.h b/libunwindstack/tests/LogFake.h
index e1dc50d..11f47b1 100644
--- a/libunwindstack/tests/LogFake.h
+++ b/libunwindstack/tests/LogFake.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
-#define _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
+#pragma once
 
 #include <string>
 
@@ -26,5 +25,3 @@
 std::string GetFakeLogPrint();
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index d5e04a1..08e0d2b 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -35,7 +35,7 @@
 #include <unwindstack/Memory.h>
 
 #include "ElfTestUtils.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
@@ -89,32 +89,32 @@
 };
 
 TEST_F(MapInfoCreateMemoryTest, end_le_start) {
-  MapInfo info(nullptr, nullptr, 0x100, 0x100, 0, 0, elf_.path);
+  auto info = MapInfo::Create(0x100, 0x100, 0, 0, elf_.path);
 
-  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  std::unique_ptr<Memory> memory(info->CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
 
-  info.set_end(0xff);
-  memory.reset(info.CreateMemory(process_memory_));
+  info->set_end(0xff);
+  memory.reset(info->CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
 
   // Make sure this test is valid.
-  info.set_end(0x101);
-  memory.reset(info.CreateMemory(process_memory_));
+  info->set_end(0x101);
+  memory.reset(info->CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
-  EXPECT_FALSE(info.memory_backed_elf());
+  EXPECT_FALSE(info->memory_backed_elf());
 }
 
 // Verify that if the offset is non-zero but there is no elf at the offset,
 // that the full file is used.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
-  MapInfo info(nullptr, nullptr, 0x100, 0x200, 0x100, 0, elf_.path);
+  auto info = MapInfo::Create(0x100, 0x200, 0x100, 0, elf_.path);
 
-  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  std::unique_ptr<Memory> memory(info->CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
-  EXPECT_FALSE(info.memory_backed_elf());
-  ASSERT_EQ(0x100U, info.elf_offset());
-  EXPECT_EQ(0x100U, info.elf_start_offset());
+  EXPECT_FALSE(info->memory_backed_elf());
+  ASSERT_EQ(0x100U, info->elf_offset());
+  EXPECT_EQ(0U, info->elf_start_offset());
 
   // Read the entire file.
   std::vector<uint8_t> buffer(1024);
@@ -129,62 +129,61 @@
 
   // Now verify the elf start offset is set correctly based on the previous
   // info.
-  MapInfo prev_info(nullptr, nullptr, 0, 0x100, 0x10, 0, "");
-  info.set_prev_map(&prev_info);
-  info.set_prev_real_map(&prev_info);
+  auto prev_info = MapInfo::Create(0, 0x100, 0x10, 0, "");
+  info->set_prev_map(prev_info);
 
   // No preconditions met, change each one until it should set the elf start
   // offset to zero.
-  info.set_elf_offset(0);
-  info.set_elf_start_offset(0);
-  info.set_memory_backed_elf(false);
-  memory.reset(info.CreateMemory(process_memory_));
+  info->set_elf_offset(0);
+  info->set_elf_start_offset(0);
+  info->set_memory_backed_elf(false);
+  memory.reset(info->CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
-  EXPECT_FALSE(info.memory_backed_elf());
-  ASSERT_EQ(0x100U, info.elf_offset());
-  EXPECT_EQ(0x100U, info.elf_start_offset());
+  EXPECT_FALSE(info->memory_backed_elf());
+  ASSERT_EQ(0x100U, info->elf_offset());
+  EXPECT_EQ(0U, info->elf_start_offset());
 
-  prev_info.set_offset(0);
-  info.set_elf_offset(0);
-  info.set_elf_start_offset(0);
-  info.set_memory_backed_elf(false);
-  memory.reset(info.CreateMemory(process_memory_));
+  prev_info->set_offset(0);
+  info->set_elf_offset(0);
+  info->set_elf_start_offset(0);
+  info->set_memory_backed_elf(false);
+  memory.reset(info->CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
-  EXPECT_FALSE(info.memory_backed_elf());
-  ASSERT_EQ(0x100U, info.elf_offset());
-  EXPECT_EQ(0x100U, info.elf_start_offset());
+  EXPECT_FALSE(info->memory_backed_elf());
+  ASSERT_EQ(0x100U, info->elf_offset());
+  EXPECT_EQ(0U, info->elf_start_offset());
 
-  prev_info.set_flags(PROT_READ);
-  info.set_elf_offset(0);
-  info.set_elf_start_offset(0);
-  info.set_memory_backed_elf(false);
-  memory.reset(info.CreateMemory(process_memory_));
+  prev_info->set_flags(PROT_READ);
+  info->set_elf_offset(0);
+  info->set_elf_start_offset(0);
+  info->set_memory_backed_elf(false);
+  memory.reset(info->CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
-  EXPECT_FALSE(info.memory_backed_elf());
-  ASSERT_EQ(0x100U, info.elf_offset());
-  EXPECT_EQ(0x100U, info.elf_start_offset());
+  EXPECT_FALSE(info->memory_backed_elf());
+  ASSERT_EQ(0x100U, info->elf_offset());
+  EXPECT_EQ(0U, info->elf_start_offset());
 
-  prev_info.set_name(info.name());
-  info.set_elf_offset(0);
-  info.set_elf_start_offset(0);
-  info.set_memory_backed_elf(false);
-  memory.reset(info.CreateMemory(process_memory_));
+  prev_info->set_name(info->name());
+  info->set_elf_offset(0);
+  info->set_elf_start_offset(0);
+  info->set_memory_backed_elf(false);
+  memory.reset(info->CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
-  EXPECT_FALSE(info.memory_backed_elf());
-  ASSERT_EQ(0x100U, info.elf_offset());
-  EXPECT_EQ(0U, info.elf_start_offset());
+  EXPECT_FALSE(info->memory_backed_elf());
+  ASSERT_EQ(0x100U, info->elf_offset());
+  EXPECT_EQ(0U, info->elf_start_offset());
 }
 
 // Verify that if the offset is non-zero and there is an elf at that
 // offset, that only part of the file is used.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
-  MapInfo info(nullptr, nullptr, 0x100, 0x200, 0x1000, 0, elf_at_1000_.path);
+  auto info = MapInfo::Create(0x100, 0x200, 0x1000, 0, elf_at_1000_.path);
 
-  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  std::unique_ptr<Memory> memory(info->CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
-  EXPECT_FALSE(info.memory_backed_elf());
-  ASSERT_EQ(0U, info.elf_offset());
-  EXPECT_EQ(0x1000U, info.elf_start_offset());
+  EXPECT_FALSE(info->memory_backed_elf());
+  ASSERT_EQ(0U, info->elf_offset());
+  EXPECT_EQ(0x1000U, info->elf_start_offset());
 
   // Read the valid part of the file.
   std::vector<uint8_t> buffer(0x100);
@@ -203,13 +202,13 @@
 // embedded elf is bigger than the initial map, the new object is larger
 // than the original map size. Do this for a 32 bit elf and a 64 bit elf.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
-  MapInfo info(nullptr, nullptr, 0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
+  auto info = MapInfo::Create(0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
 
-  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  std::unique_ptr<Memory> memory(info->CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
-  EXPECT_FALSE(info.memory_backed_elf());
-  ASSERT_EQ(0U, info.elf_offset());
-  EXPECT_EQ(0x1000U, info.elf_start_offset());
+  EXPECT_FALSE(info->memory_backed_elf());
+  ASSERT_EQ(0U, info->elf_offset());
+  EXPECT_EQ(0x1000U, info->elf_start_offset());
 
   // Verify the memory is a valid elf.
   uint8_t e_ident[SELFMAG + 1];
@@ -221,13 +220,13 @@
 }
 
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
-  MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
+  auto info = MapInfo::Create(0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
 
-  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  std::unique_ptr<Memory> memory(info->CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
-  EXPECT_FALSE(info.memory_backed_elf());
-  ASSERT_EQ(0U, info.elf_offset());
-  EXPECT_EQ(0x2000U, info.elf_start_offset());
+  EXPECT_FALSE(info->memory_backed_elf());
+  ASSERT_EQ(0U, info->elf_offset());
+  EXPECT_EQ(0x2000U, info->elf_start_offset());
 
   // Verify the memory is a valid elf.
   uint8_t e_ident[SELFMAG + 1];
@@ -244,14 +243,14 @@
   // be returned if the file mapping fails, but the device check is incorrect.
   std::vector<uint8_t> buffer(1024);
   uint64_t start = reinterpret_cast<uint64_t>(buffer.data());
-  MapInfo info(nullptr, nullptr, start, start + buffer.size(), 0, 0x8000, "/dev/something");
+  auto info = MapInfo::Create(start, start + buffer.size(), 0, 0x8000, "/dev/something");
 
-  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  std::unique_ptr<Memory> memory(info->CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
 }
 
 TEST_F(MapInfoCreateMemoryTest, process_memory) {
-  MapInfo info(nullptr, nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+  auto info = MapInfo::Create(0x2000, 0x3000, 0, PROT_READ, "");
 
   Elf32_Ehdr ehdr = {};
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -263,11 +262,11 @@
   for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
     buffer[i] = i % 256;
   }
-  memory_->SetMemory(info.start(), buffer.data(), buffer.size());
+  memory_->SetMemory(info->start(), buffer.data(), buffer.size());
 
-  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  std::unique_ptr<Memory> memory(info->CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
-  EXPECT_TRUE(info.memory_backed_elf());
+  EXPECT_TRUE(info->memory_backed_elf());
 
   memset(buffer.data(), 0, buffer.size());
   ASSERT_TRUE(memory->ReadFully(0, buffer.data(), buffer.size()));
@@ -294,7 +293,7 @@
   // Set the memory in the r-x map.
   memory_->SetMemoryBlock(0x3000, 0x2000, 0x5d);
 
-  MapInfo* map_info = maps.Find(0x3000);
+  auto map_info = maps.Find(0x3000).get();
   ASSERT_TRUE(map_info != nullptr);
 
   std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
@@ -341,7 +340,7 @@
   memory_->SetMemoryBlock(0x3000 + sizeof(ehdr), 0x4000 - sizeof(ehdr), 0x34);
   memory_->SetMemoryBlock(0x4000, 0x1000, 0x43);
 
-  MapInfo* map_info = maps.Find(0x4000);
+  auto map_info = maps.Find(0x4000).get();
   ASSERT_TRUE(map_info != nullptr);
 
   std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
@@ -367,13 +366,44 @@
   }
 }
 
+TEST_F(MapInfoCreateMemoryTest, valid_single_rx_non_zero_offset) {
+  Maps maps;
+  maps.Add(0x3000, 0x5000, 0xa000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+
+  Elf32_Ehdr ehdr = {};
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+
+  // Setup an elf at offset 0x3000 in memory..
+  memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
+  memory_->SetMemoryBlock(0x3000 + sizeof(ehdr), 0x5000 - sizeof(ehdr), 0x34);
+
+  auto map_info = maps.Find(0x3000);
+  ASSERT_TRUE(map_info != nullptr);
+
+  std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+  ASSERT_TRUE(mem.get() != nullptr);
+  EXPECT_TRUE(map_info->memory_backed_elf());
+  EXPECT_EQ(0UL, map_info->elf_offset());
+  EXPECT_EQ(0xa000UL, map_info->offset());
+  EXPECT_EQ(0xa000UL, map_info->elf_start_offset());
+
+  // Verify that reading values from this memory works properly.
+  std::vector<uint8_t> buffer(0x3000);
+  size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+  ASSERT_EQ(0x2000UL, bytes);
+  ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+  for (size_t i = sizeof(ehdr); i < bytes; i++) {
+    ASSERT_EQ(0x34, buffer[i]) << "Failed at byte " << i;
+  }
+}
+
 TEST_F(MapInfoCreateMemoryTest, rosegment_from_file) {
   Maps maps;
   maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
   maps.Add(0x1000, 0x2000, 0x1000, PROT_READ, elf_at_1000_.path, 0);
   maps.Add(0x2000, 0x3000, 0x2000, PROT_READ | PROT_EXEC, elf_at_1000_.path, 0);
 
-  MapInfo* map_info = maps.Find(0x2000);
+  auto map_info = maps.Find(0x2000).get();
   ASSERT_TRUE(map_info != nullptr);
 
   // Set up the size
@@ -425,7 +455,7 @@
   // Set the memory in the r-x map.
   memory_->SetMemoryBlock(0x3000, 0x2000, 0x5d);
 
-  MapInfo* map_info = maps.Find(0x3000);
+  auto map_info = maps.Find(0x3000).get();
   ASSERT_TRUE(map_info != nullptr);
 
   std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
diff --git a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
index e123d0e..4877c68 100644
--- a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
+++ b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
@@ -36,7 +36,8 @@
 
 #include "ElfFake.h"
 #include "ElfTestUtils.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
+#include "utils/OfflineUnwindUtils.h"
 
 namespace unwindstack {
 
@@ -50,8 +51,7 @@
     elf_interface_ = new ElfInterfaceFake(memory_);
     elf_->FakeSetInterface(elf_interface_);
     elf_container_.reset(elf_);
-    map_info_.reset(
-        new MapInfo(nullptr, nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path));
+    map_info_ = MapInfo::Create(0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path);
   }
 
   void TearDown() override { delete memory_; }
@@ -62,15 +62,15 @@
   ElfFake* elf_;
   ElfInterfaceFake* elf_interface_;
   std::unique_ptr<ElfFake> elf_container_;
-  std::unique_ptr<MapInfo> map_info_;
+  std::shared_ptr<MapInfo> map_info_;
   std::unique_ptr<TemporaryFile> tf_;
 };
 
 TEST_F(MapInfoGetBuildIDTest, no_elf_and_no_valid_elf_in_memory) {
-  MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+  auto info = MapInfo::Create(0x1000, 0x2000, 0, PROT_READ, "");
 
-  EXPECT_EQ("", info.GetBuildID());
-  EXPECT_EQ("", info.GetPrintableBuildID());
+  EXPECT_EQ("", info->GetBuildID());
+  EXPECT_EQ("", info->GetPrintableBuildID());
 }
 
 TEST_F(MapInfoGetBuildIDTest, from_elf) {
@@ -197,9 +197,16 @@
 }
 
 TEST_F(MapInfoGetBuildIDTest, real_elf) {
-  MapInfo map_info(nullptr, nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE,
-                   TestGetFileDirectory() + "offline/empty_arm64/libc.so");
-  EXPECT_EQ("6df0590c4920f4c7b9f34fe833f37d54", map_info.GetPrintableBuildID());
+  auto map_info = MapInfo::Create(0x1000, 0x20000, 0, PROT_READ | PROT_WRITE,
+                                  GetOfflineFilesDirectory() + "empty_arm64/libc.so");
+  EXPECT_EQ("6df0590c4920f4c7b9f34fe833f37d54", map_info->GetPrintableBuildID());
+}
+
+TEST_F(MapInfoGetBuildIDTest, in_device_map) {
+  auto map_info =
+      MapInfo::Create(0x1000, 0x20000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
+                      GetOfflineFilesDirectory() + "empty_arm64/libc.so");
+  EXPECT_EQ("", map_info->GetPrintableBuildID());
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index 944f894..79f470f 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -37,7 +37,7 @@
 #include <unwindstack/Memory.h>
 
 #include "ElfTestUtils.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
@@ -59,7 +59,7 @@
     ehdr->e_shnum = 4;
   }
 
-  void InitMapInfo(std::vector<std::unique_ptr<MapInfo>>& maps, bool in_memory);
+  void InitMapInfo(std::vector<std::shared_ptr<MapInfo>>& maps, bool in_memory);
 
   const size_t kMapSize = 4096;
 
@@ -70,42 +70,40 @@
 };
 
 TEST_F(MapInfoGetElfTest, invalid) {
-  MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+  auto info = MapInfo::Create(0x1000, 0x2000, 0, PROT_READ, "");
 
   // The map is empty, but this should still create an invalid elf object.
-  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+  Elf* elf = info->GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 }
 
 TEST_F(MapInfoGetElfTest, valid32) {
-  MapInfo info(nullptr, nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
-
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
   memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+  auto info = MapInfo::Create(0x3000, 0x4000, 0, PROT_READ, "");
+  Elf* elf = info->GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
   EXPECT_EQ(ELFCLASS32, elf->class_type());
 
   // Now verify that an empty process memory returns an invalid elf object.
-  info.set_elf(nullptr);
-  elf = info.GetElf(std::shared_ptr<Memory>(), ARCH_ARM);
+  info->set_elf(nullptr);
+  elf = info->GetElf(std::shared_ptr<Memory>(), ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 }
 
 TEST_F(MapInfoGetElfTest, valid64) {
-  MapInfo info(nullptr, nullptr, 0x8000, 0x9000, 0, PROT_READ, "");
-
   Elf64_Ehdr ehdr;
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
   memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
+  auto info = MapInfo::Create(0x8000, 0x9000, 0, PROT_READ, "");
+  Elf* elf = info->GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@@ -113,26 +111,24 @@
 }
 
 TEST_F(MapInfoGetElfTest, invalid_arch_mismatch) {
-  MapInfo info(nullptr, nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
-
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
   memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, ARCH_X86);
+  auto info = MapInfo::Create(0x3000, 0x4000, 0, PROT_READ, "");
+  Elf* elf = info->GetElf(process_memory_, ARCH_X86);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
-  MapInfo info(nullptr, nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
-
   TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
                                                  memory_->SetMemory(0x2000 + offset, ptr, size);
                                                });
 
-  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+  auto info = MapInfo::Create(0x2000, 0x3000, 0, PROT_READ, "");
+  Elf* elf = info->GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -141,14 +137,13 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
-  MapInfo info(nullptr, nullptr, 0x5000, 0x8000, 0, PROT_READ, "");
-
   TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
                                                  memory_->SetMemory(0x5000 + offset, ptr, size);
                                                });
 
-  Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
+  auto info = MapInfo::Create(0x5000, 0x8000, 0, PROT_READ, "");
+  Elf* elf = info->GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@@ -157,26 +152,25 @@
 }
 
 TEST_F(MapInfoGetElfTest, end_le_start) {
-  MapInfo info(nullptr, nullptr, 0x1000, 0x1000, 0, PROT_READ, elf_.path);
-
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr)));
 
-  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+  auto info = MapInfo::Create(0x1000, 0x1000, 0, PROT_READ, elf_.path);
+  Elf* elf = info->GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
-  info.set_elf(nullptr);
-  info.set_end(0xfff);
-  elf = info.GetElf(process_memory_, ARCH_ARM);
+  info->set_elf(nullptr);
+  info->set_end(0xfff);
+  elf = info->GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   // Make sure this test is valid.
-  info.set_elf(nullptr);
-  info.set_end(0x2000);
-  elf = info.GetElf(process_memory_, ARCH_ARM);
+  info->set_elf(nullptr);
+  info->set_end(0x2000);
+  elf = info->GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
 }
@@ -184,8 +178,6 @@
 // Verify that if the offset is non-zero but there is no elf at the offset,
 // that the full file is used.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
-  MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
-
   std::vector<uint8_t> buffer(0x1000);
   memset(buffer.data(), 0, buffer.size());
   Elf32_Ehdr ehdr;
@@ -193,11 +185,12 @@
   memcpy(buffer.data(), &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+  auto info = MapInfo::Create(0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
+  Elf* elf = info->GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
-  ASSERT_EQ(0x100U, info.elf_offset());
+  ASSERT_EQ(0x100U, info->elf_offset());
 
   // Read the entire file.
   memset(buffer.data(), 0, buffer.size());
@@ -213,20 +206,20 @@
 // Verify that if the offset is non-zero and there is an elf at that
 // offset, that only part of the file is used.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
-  MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
+  auto info = MapInfo::Create(0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
-  memcpy(&buffer[info.offset()], &ehdr, sizeof(ehdr));
+  memcpy(&buffer[info->offset()], &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+  Elf* elf = info->GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
-  ASSERT_EQ(0U, info.elf_offset());
+  ASSERT_EQ(0U, info->elf_offset());
 
   // Read the valid part of the file.
   ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000));
@@ -243,7 +236,7 @@
 // embedded elf is bigger than the initial map, the new object is larger
 // than the original map size. Do this for a 32 bit elf and a 64 bit elf.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
-  MapInfo info(nullptr, nullptr, 0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
+  auto info = MapInfo::Create(0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -252,14 +245,14 @@
   ehdr.e_shoff = 0x2000;
   ehdr.e_shentsize = sizeof(Elf32_Shdr) + 100;
   ehdr.e_shnum = 4;
-  memcpy(&buffer[info.offset()], &ehdr, sizeof(ehdr));
+  memcpy(&buffer[info->offset()], &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+  Elf* elf = info->GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
-  ASSERT_EQ(0U, info.elf_offset());
+  ASSERT_EQ(0U, info->elf_offset());
 
   // Verify the memory is a valid elf.
   memset(buffer.data(), 0, buffer.size());
@@ -271,7 +264,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
-  MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
+  auto info = MapInfo::Create(0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -280,14 +273,45 @@
   ehdr.e_shoff = 0x2000;
   ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
   ehdr.e_shnum = 4;
-  memcpy(&buffer[info.offset()], &ehdr, sizeof(ehdr));
+  memcpy(&buffer[info->offset()], &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
+  Elf* elf = info->GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
-  ASSERT_EQ(0U, info.elf_offset());
+  ASSERT_EQ(0U, info->elf_offset());
+
+  // Verify the memory is a valid elf.
+  memset(buffer.data(), 0, buffer.size());
+  ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000));
+  ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+
+  // Read past the end of what would normally be the size of the map.
+  ASSERT_TRUE(elf->memory()->ReadFully(0x1000, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used. Further verify that if the
+// the initial map is smaller than elf header size, we can still read the elf.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64_small_map_range) {
+  auto info = MapInfo::Create(0x7000, 0x7004, 0x1000, PROT_READ, elf_.path);
+
+  std::vector<uint8_t> buffer(0x4000);
+  memset(buffer.data(), 0, buffer.size());
+  Elf64_Ehdr ehdr;
+  TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+  ehdr.e_shoff = 0x2000;
+  ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+  ehdr.e_shnum = 4;
+  memcpy(&buffer[info->offset()], &ehdr, sizeof(ehdr));
+  ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+  Elf* elf = info->GetElf(process_memory_, ARCH_ARM64);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_TRUE(elf->valid());
+  ASSERT_TRUE(elf->memory() != nullptr);
+  ASSERT_EQ(0U, info->elf_offset());
 
   // Verify the memory is a valid elf.
   memset(buffer.data(), 0, buffer.size());
@@ -299,9 +323,6 @@
 }
 
 TEST_F(MapInfoGetElfTest, check_device_maps) {
-  MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
-               "/dev/something");
-
   // Create valid elf data in process memory for this to verify that only
   // the name is causing invalid elf data.
   Elf64_Ehdr ehdr;
@@ -311,20 +332,22 @@
   ehdr.e_shnum = 0;
   memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
+  auto info =
+      MapInfo::Create(0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP, "/dev/something");
+  Elf* elf = info->GetElf(process_memory_, ARCH_X86_64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   // Set the name to nothing to verify that it still fails.
-  info.set_elf(nullptr);
-  info.set_name("");
-  elf = info.GetElf(process_memory_, ARCH_X86_64);
+  info->set_elf(nullptr);
+  info->set_name("");
+  elf = info->GetElf(process_memory_, ARCH_X86_64);
   ASSERT_FALSE(elf->valid());
 
   // Change the flags and verify the elf is valid now.
-  info.set_elf(nullptr);
-  info.set_flags(PROT_READ);
-  elf = info.GetElf(process_memory_, ARCH_X86_64);
+  info->set_elf(nullptr);
+  info->set_flags(PROT_READ);
+  elf = info->GetElf(process_memory_, ARCH_X86_64);
   ASSERT_TRUE(elf->valid());
 }
 
@@ -345,17 +368,17 @@
   wait = true;
   // Create all of the threads and have them do the GetElf at the same time
   // to make it likely that a race will occur.
-  MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, "");
+  auto info = MapInfo::Create(0x7000, 0x8000, 0x1000, PROT_READ, "");
   for (size_t i = 0; i < kNumConcurrentThreads; i++) {
     std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
       while (wait)
         ;
-      Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
+      Elf* elf = info->GetElf(process_memory_, ARCH_X86_64);
       elf_in_threads[i] = elf;
     });
     threads.push_back(thread);
   }
-  ASSERT_TRUE(info.elf() == nullptr);
+  ASSERT_TRUE(info->elf() == nullptr);
 
   // Set them all going and wait for the threads to finish.
   wait = false;
@@ -365,7 +388,7 @@
   }
 
   // Now verify that all of the elf files are exactly the same and valid.
-  Elf* elf = info.elf().get();
+  Elf* elf = info->elf().get();
   ASSERT_TRUE(elf != nullptr);
   EXPECT_TRUE(elf->valid());
   for (size_t i = 0; i < kNumConcurrentThreads; i++) {
@@ -375,24 +398,23 @@
 
 // Verify that previous maps don't automatically get the same elf object.
 TEST_F(MapInfoGetElfTest, prev_map_elf_not_set) {
-  MapInfo info1(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "/not/present");
-  MapInfo info2(&info1, &info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
+  auto info1 = MapInfo::Create(0x1000, 0x2000, 0, PROT_READ, "/not/present");
+  auto info2 = MapInfo::Create(info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
   memory_->SetMemory(0x2000, &ehdr, sizeof(ehdr));
-  Elf* elf = info2.GetElf(process_memory_, ARCH_ARM);
+  Elf* elf = info2->GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
 
-  ASSERT_NE(elf, info1.GetElf(process_memory_, ARCH_ARM));
+  ASSERT_NE(elf, info1->GetElf(process_memory_, ARCH_ARM));
 }
 
-void MapInfoGetElfTest::InitMapInfo(std::vector<std::unique_ptr<MapInfo>>& maps, bool in_memory) {
+void MapInfoGetElfTest::InitMapInfo(std::vector<std::shared_ptr<MapInfo>>& maps, bool in_memory) {
   maps.resize(2);
-  maps[0].reset(new MapInfo(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path));
-  maps[1].reset(new MapInfo(maps[0].get(), maps[0].get(), 0x2000, 0x3000, 0x1000,
-                            PROT_READ | PROT_EXEC, elf_.path));
+  maps[0] = MapInfo::Create(0x1000, 0x2000, 0, PROT_READ, elf_.path);
+  maps[1] = MapInfo::Create(maps[0], 0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, elf_.path);
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -406,7 +428,7 @@
 // Verify that a read-only map followed by a read-execute map will result
 // in the same elf object in both maps.
 TEST_F(MapInfoGetElfTest, read_only_followed_by_read_exec_share_elf_exec_first) {
-  std::vector<std::unique_ptr<MapInfo>> maps;
+  std::vector<std::shared_ptr<MapInfo>> maps;
 
   // First use in memory maps.
   InitMapInfo(maps, true);
@@ -439,7 +461,7 @@
 // Verify that a read-only map followed by a read-execute map will result
 // in the same elf object in both maps.
 TEST_F(MapInfoGetElfTest, read_only_followed_by_read_exec_share_elf_read_only_first) {
-  std::vector<std::unique_ptr<MapInfo>> maps;
+  std::vector<std::shared_ptr<MapInfo>> maps;
 
   // First use in memory maps.
   InitMapInfo(maps, true);
@@ -472,18 +494,18 @@
 // Verify that a read-only map followed by an empty map, then followed by
 // a read-execute map will result in the same elf object in both maps.
 TEST_F(MapInfoGetElfTest, read_only_followed_by_empty_then_read_exec_share_elf) {
-  MapInfo r_info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
-  MapInfo empty(&r_info, &r_info, 0x2000, 0x3000, 0, 0, "");
-  MapInfo rw_info(&empty, &r_info, 0x3000, 0x4000, 0x2000, PROT_READ | PROT_EXEC, elf_.path);
+  auto r_info = MapInfo::Create(0x1000, 0x2000, 0, PROT_READ, elf_.path);
+  auto empty = MapInfo::Create(r_info, 0x2000, 0x3000, 0, 0, "");
+  auto rw_info = MapInfo::Create(empty, 0x3000, 0x4000, 0x2000, PROT_READ | PROT_EXEC, elf_.path);
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
   memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
-  Elf* elf = rw_info.GetElf(process_memory_, ARCH_ARM);
+  Elf* elf = rw_info->GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
 
-  ASSERT_EQ(elf, r_info.GetElf(process_memory_, ARCH_ARM));
+  ASSERT_EQ(elf, r_info->GetElf(process_memory_, ARCH_ARM));
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index 5ddf1de..a0aca4f 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -39,7 +39,7 @@
 
 #include "ElfFake.h"
 #include "ElfTestUtils.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
@@ -50,7 +50,7 @@
     process_memory_.reset(memory_);
     elf_ = new ElfFake(new MemoryFake);
     elf_container_.reset(elf_);
-    map_info_.reset(new MapInfo(nullptr, nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
+    map_info_ = MapInfo::Create(0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, "");
   }
 
   void MultipleThreadTest(uint64_t expected_load_bias);
@@ -59,13 +59,13 @@
   MemoryFake* memory_;
   ElfFake* elf_;
   std::unique_ptr<ElfFake> elf_container_;
-  std::unique_ptr<MapInfo> map_info_;
+  std::shared_ptr<MapInfo> map_info_;
 };
 
 TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) {
-  MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+  auto info = MapInfo::Create(0x1000, 0x2000, 0, PROT_READ, "");
 
-  EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
+  EXPECT_EQ(0U, info->GetLoadBias(process_memory_));
 }
 
 TEST_F(MapInfoGetLoadBiasTest, load_bias_cached_from_elf) {
@@ -84,7 +84,7 @@
   elf_->FakeSetLoadBias(0);
   EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
 
-  map_info_->set_load_bias(INT64_MAX);
+  map_info_->set_load_bias(UINT64_MAX);
   elf_->FakeSetLoadBias(0x1000);
   EXPECT_EQ(0x1000U, map_info_->GetLoadBias(process_memory_));
 }
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
index 7ba9471..9cfbb44 100644
--- a/libunwindstack/tests/MapInfoTest.cpp
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdint.h>
+#include <sys/mman.h>
 
 #include <thread>
 
@@ -28,34 +29,98 @@
 namespace unwindstack {
 
 TEST(MapInfoTest, maps_constructor_const_char) {
-  MapInfo prev_map(nullptr, nullptr, 0, 0, 0, 0, "");
-  MapInfo map_info(&prev_map, &prev_map, 1, 2, 3, 4, "map");
+  auto prev_map = MapInfo::Create(0, 0, 0, 0, "");
+  auto map_info = MapInfo::Create(prev_map, 1, 2, 3, 4, "map");
 
-  EXPECT_EQ(&prev_map, map_info.prev_map());
-  EXPECT_EQ(1UL, map_info.start());
-  EXPECT_EQ(2UL, map_info.end());
-  EXPECT_EQ(3UL, map_info.offset());
-  EXPECT_EQ(4UL, map_info.flags());
-  EXPECT_EQ("map", map_info.name());
-  EXPECT_EQ(INT64_MAX, map_info.load_bias());
-  EXPECT_EQ(0UL, map_info.elf_offset());
-  EXPECT_TRUE(map_info.elf().get() == nullptr);
+  EXPECT_EQ(prev_map.get(), map_info->prev_map().get());
+  EXPECT_EQ(1UL, map_info->start());
+  EXPECT_EQ(2UL, map_info->end());
+  EXPECT_EQ(3UL, map_info->offset());
+  EXPECT_EQ(4UL, map_info->flags());
+  EXPECT_EQ("map", map_info->name());
+  EXPECT_EQ(UINT64_MAX, map_info->load_bias());
+  EXPECT_EQ(0UL, map_info->elf_offset());
+  EXPECT_TRUE(map_info->elf().get() == nullptr);
 }
 
 TEST(MapInfoTest, maps_constructor_string) {
   std::string name("string_map");
-  MapInfo prev_map(nullptr, nullptr, 0, 0, 0, 0, "");
-  MapInfo map_info(&prev_map, &prev_map, 1, 2, 3, 4, name);
+  auto prev_map = MapInfo::Create(0, 0, 0, 0, "");
+  auto map_info = MapInfo::Create(prev_map, 1, 2, 3, 4, name);
 
-  EXPECT_EQ(&prev_map, map_info.prev_map());
-  EXPECT_EQ(1UL, map_info.start());
-  EXPECT_EQ(2UL, map_info.end());
-  EXPECT_EQ(3UL, map_info.offset());
-  EXPECT_EQ(4UL, map_info.flags());
-  EXPECT_EQ("string_map", map_info.name());
-  EXPECT_EQ(INT64_MAX, map_info.load_bias());
-  EXPECT_EQ(0UL, map_info.elf_offset());
-  EXPECT_TRUE(map_info.elf().get() == nullptr);
+  EXPECT_EQ(prev_map, map_info->prev_map());
+  EXPECT_EQ(1UL, map_info->start());
+  EXPECT_EQ(2UL, map_info->end());
+  EXPECT_EQ(3UL, map_info->offset());
+  EXPECT_EQ(4UL, map_info->flags());
+  EXPECT_EQ("string_map", map_info->name());
+  EXPECT_EQ(UINT64_MAX, map_info->load_bias());
+  EXPECT_EQ(0UL, map_info->elf_offset());
+  EXPECT_TRUE(map_info->elf().get() == nullptr);
+}
+
+TEST(MapInfoTest, real_map_check) {
+  auto map1 = MapInfo::Create(0, 0x1000, 0, PROT_READ, "fake.so");
+  auto map2 = MapInfo::Create(map1, 0, 0, 0, 0, "");
+  auto map3 = MapInfo::Create(map2, 0x1000, 0x2000, 0x1000, PROT_READ | PROT_EXEC, "fake.so");
+
+  EXPECT_EQ(nullptr, map1->prev_map());
+  EXPECT_EQ(nullptr, map1->GetPrevRealMap());
+  EXPECT_EQ(map2, map1->next_map());
+  EXPECT_EQ(map3, map1->GetNextRealMap());
+
+  EXPECT_EQ(map1, map2->prev_map());
+  EXPECT_EQ(nullptr, map2->GetPrevRealMap());
+  EXPECT_EQ(map3, map2->next_map());
+  EXPECT_EQ(nullptr, map2->GetNextRealMap());
+
+  EXPECT_EQ(map2, map3->prev_map());
+  EXPECT_EQ(map1, map3->GetPrevRealMap());
+  EXPECT_EQ(nullptr, map3->next_map());
+  EXPECT_EQ(nullptr, map3->GetNextRealMap());
+
+  // Verify that if the middle map is not blank, then the Get{Next,Prev}RealMap
+  // functions return nullptrs.
+  map2->set_offset(1);
+  EXPECT_EQ(nullptr, map1->GetPrevRealMap());
+  EXPECT_EQ(nullptr, map1->GetNextRealMap());
+  EXPECT_EQ(nullptr, map3->GetPrevRealMap());
+  EXPECT_EQ(nullptr, map3->GetNextRealMap());
+  map2->set_offset(0);
+  EXPECT_EQ(map3, map1->GetNextRealMap());
+
+  map2->set_flags(1);
+  EXPECT_EQ(nullptr, map1->GetPrevRealMap());
+  EXPECT_EQ(nullptr, map1->GetNextRealMap());
+  EXPECT_EQ(nullptr, map3->GetPrevRealMap());
+  EXPECT_EQ(nullptr, map3->GetNextRealMap());
+  map2->set_flags(0);
+  EXPECT_EQ(map3, map1->GetNextRealMap());
+
+  map2->set_name("something");
+  EXPECT_EQ(nullptr, map1->GetPrevRealMap());
+  EXPECT_EQ(nullptr, map1->GetNextRealMap());
+  EXPECT_EQ(nullptr, map3->GetPrevRealMap());
+  EXPECT_EQ(nullptr, map3->GetNextRealMap());
+  map2->set_name("");
+  EXPECT_EQ(map3, map1->GetNextRealMap());
+
+  // Verify that if the Get{Next,Prev}RealMap names must match.
+  map1->set_name("another");
+  EXPECT_EQ(nullptr, map1->GetPrevRealMap());
+  EXPECT_EQ(nullptr, map1->GetNextRealMap());
+  EXPECT_EQ(nullptr, map3->GetPrevRealMap());
+  EXPECT_EQ(nullptr, map3->GetNextRealMap());
+  map1->set_name("fake.so");
+  EXPECT_EQ(map3, map1->GetNextRealMap());
+
+  map3->set_name("another");
+  EXPECT_EQ(nullptr, map1->GetPrevRealMap());
+  EXPECT_EQ(nullptr, map1->GetNextRealMap());
+  EXPECT_EQ(nullptr, map3->GetPrevRealMap());
+  EXPECT_EQ(nullptr, map3->GetNextRealMap());
+  map3->set_name("fake.so");
+  EXPECT_EQ(map3, map1->GetNextRealMap());
 }
 
 TEST(MapInfoTest, get_function_name) {
@@ -64,18 +129,18 @@
   elf->FakeSetInterface(interface);
   interface->FakePushFunctionData(FunctionData("function", 1000));
 
-  MapInfo map_info(nullptr, nullptr, 1, 2, 3, 4, "");
-  map_info.set_elf(elf);
+  auto map_info = MapInfo::Create(1, 2, 3, 4, "");
+  map_info->set_elf(elf);
 
   SharedString name;
   uint64_t offset;
-  ASSERT_TRUE(map_info.GetFunctionName(1000, &name, &offset));
+  ASSERT_TRUE(map_info->GetFunctionName(1000, &name, &offset));
   EXPECT_EQ("function", name);
   EXPECT_EQ(1000UL, offset);
 }
 
 TEST(MapInfoTest, multiple_thread_get_elf_fields) {
-  MapInfo map_info(nullptr, nullptr, 0, 0, 0, 0, "");
+  auto map_info = MapInfo::Create(0, 0, 0, 0, "");
 
   static constexpr size_t kNumConcurrentThreads = 100;
   MapInfo::ElfFields* elf_fields[kNumConcurrentThreads];
@@ -89,7 +154,7 @@
     std::thread* thread = new std::thread([i, &wait, &map_info, &elf_fields]() {
       while (wait)
         ;
-      elf_fields[i] = &map_info.GetElfFields();
+      elf_fields[i] = &map_info->GetElfFields();
     });
     threads.push_back(thread);
   }
@@ -102,11 +167,29 @@
   }
 
   // Now verify that all of elf fields are exactly the same and valid.
-  MapInfo::ElfFields* expected_elf_fields = &map_info.GetElfFields();
+  MapInfo::ElfFields* expected_elf_fields = &map_info->GetElfFields();
   ASSERT_TRUE(expected_elf_fields != nullptr);
   for (size_t i = 0; i < kNumConcurrentThreads; i++) {
     EXPECT_EQ(expected_elf_fields, elf_fields[i]) << "Thread " << i << " mismatched.";
   }
 }
 
+TEST(MapInfoTest, elf_file_not_readable) {
+  auto map_info_readable = MapInfo::Create(0, 0x1000, 0, PROT_READ, "fake.so");
+  map_info_readable->set_memory_backed_elf(true);
+  ASSERT_TRUE(map_info_readable->ElfFileNotReadable());
+
+  auto map_info_no_name = MapInfo::Create(0, 0x1000, 0, PROT_READ, "");
+  map_info_no_name->set_memory_backed_elf(true);
+  ASSERT_FALSE(map_info_no_name->ElfFileNotReadable());
+
+  auto map_info_bracket = MapInfo::Create(0, 0x2000, 0, PROT_READ, "[vdso]");
+  map_info_bracket->set_memory_backed_elf(true);
+  ASSERT_FALSE(map_info_bracket->ElfFileNotReadable());
+
+  auto map_info_memfd = MapInfo::Create(0, 0x3000, 0, PROT_READ, "/memfd:jit-cache");
+  map_info_memfd->set_memory_backed_elf(true);
+  ASSERT_FALSE(map_info_memfd->ElfFileNotReadable());
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 309bdc2..8226b11 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -32,7 +32,7 @@
     ASSERT_FALSE(maps.Parse()) << "Failed on: " + line;
   } else {
     ASSERT_TRUE(maps.Parse()) << "Failed on: " + line;
-    MapInfo* element = maps.Get(0);
+    MapInfo* element = maps.Get(0).get();
     ASSERT_TRUE(element != nullptr) << "Failed on: " + line;
     info->set_start(element->start());
     info->set_end(element->end());
@@ -47,18 +47,36 @@
   Maps maps;
 
   maps.Add(0x1000, 0x2000, 0, PROT_READ, "fake_map", 0);
-  maps.Add(0x3000, 0x4000, 0x10, 0, "", 0x1234);
-  maps.Add(0x5000, 0x6000, 1, 2, "fake_map2", static_cast<uint64_t>(-1));
+  maps.Add(0x3000, 0x4000, 0, 0, "", 0x1234);
+  maps.Add(0x5000, 0x6000, 1, 2, "fake_map", static_cast<uint64_t>(-1));
 
   ASSERT_EQ(3U, maps.Total());
-  MapInfo* info = maps.Get(0);
-  ASSERT_EQ(0x1000U, info->start());
-  ASSERT_EQ(0x2000U, info->end());
-  ASSERT_EQ(0U, info->offset());
-  ASSERT_EQ(PROT_READ, info->flags());
-  ASSERT_EQ("fake_map", info->name());
-  ASSERT_EQ(0U, info->elf_offset());
-  ASSERT_EQ(0U, info->load_bias().load());
+  auto info1 = maps.Get(0);
+  auto info2 = maps.Get(1);
+  auto info3 = maps.Get(2);
+
+  EXPECT_EQ(nullptr, info1->prev_map());
+  EXPECT_EQ(nullptr, info1->GetPrevRealMap());
+  EXPECT_EQ(info2, info1->next_map());
+  EXPECT_EQ(info3, info1->GetNextRealMap());
+
+  EXPECT_EQ(info1, info2->prev_map());
+  EXPECT_EQ(nullptr, info2->GetPrevRealMap());
+  EXPECT_EQ(info3, info2->next_map());
+  EXPECT_EQ(nullptr, info2->GetNextRealMap());
+
+  EXPECT_EQ(info2, info3->prev_map());
+  EXPECT_EQ(info1, info3->GetPrevRealMap());
+  EXPECT_EQ(nullptr, info3->next_map());
+  EXPECT_EQ(nullptr, info3->GetNextRealMap());
+
+  ASSERT_EQ(0x1000U, info1->start());
+  ASSERT_EQ(0x2000U, info1->end());
+  ASSERT_EQ(0U, info1->offset());
+  ASSERT_EQ(PROT_READ, info1->flags());
+  ASSERT_EQ("fake_map", info1->name());
+  ASSERT_EQ(0U, info1->elf_offset());
+  ASSERT_EQ(0U, info1->load_bias().load());
 }
 
 TEST(MapsTest, map_move) {
@@ -71,7 +89,7 @@
   Maps maps2 = std::move(maps);
 
   ASSERT_EQ(3U, maps2.Total());
-  MapInfo* info = maps2.Get(0);
+  auto info = maps2.Get(0);
   ASSERT_EQ(0x1000U, info->start());
   ASSERT_EQ(0x2000U, info->end());
   ASSERT_EQ(0U, info->offset());
@@ -82,28 +100,28 @@
 }
 
 TEST(MapsTest, verify_parse_line) {
-  MapInfo info(nullptr, nullptr, 0, 0, 0, 0, "");
+  auto info = MapInfo::Create(0, 0, 0, 0, "");
 
-  VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
-  EXPECT_EQ(1U, info.start());
-  EXPECT_EQ(2U, info.end());
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags());
-  EXPECT_EQ(3U, info.offset());
-  EXPECT_EQ("", info.name());
+  VerifyLine("01-02 rwxp 03 04:05 06\n", info.get());
+  EXPECT_EQ(1U, info->start());
+  EXPECT_EQ(2U, info->end());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags());
+  EXPECT_EQ(3U, info->offset());
+  EXPECT_EQ("", info->name());
 
-  VerifyLine("0a-0b ---s 0c 0d:0e 06 /fake/name\n", &info);
-  EXPECT_EQ(0xaU, info.start());
-  EXPECT_EQ(0xbU, info.end());
-  EXPECT_EQ(0U, info.flags());
-  EXPECT_EQ(0xcU, info.offset());
-  EXPECT_EQ("/fake/name", info.name());
+  VerifyLine("0a-0b ---s 0c 0d:0e 06 /fake/name\n", info.get());
+  EXPECT_EQ(0xaU, info->start());
+  EXPECT_EQ(0xbU, info->end());
+  EXPECT_EQ(0U, info->flags());
+  EXPECT_EQ(0xcU, info->offset());
+  EXPECT_EQ("/fake/name", info->name());
 
-  VerifyLine("01-02   rwxp   03    04:05    06    /fake/name/again\n", &info);
-  EXPECT_EQ(1U, info.start());
-  EXPECT_EQ(2U, info.end());
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags());
-  EXPECT_EQ(3U, info.offset());
-  EXPECT_EQ("/fake/name/again", info.name());
+  VerifyLine("01-02   rwxp   03    04:05    06    /fake/name/again\n", info.get());
+  EXPECT_EQ(1U, info->start());
+  EXPECT_EQ(2U, info->end());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags());
+  EXPECT_EQ(3U, info->offset());
+  EXPECT_EQ("/fake/name/again", info->name());
 
   VerifyLine("-00 rwxp 00 00:00 0\n", nullptr);
   VerifyLine("00- rwxp 00 00:00 0\n", nullptr);
@@ -155,19 +173,19 @@
 }
 
 TEST(MapsTest, verify_large_values) {
-  MapInfo info(nullptr, nullptr, 0, 0, 0, 0, "");
+  auto info = MapInfo::Create(0, 0, 0, 0, "");
 #if defined(__LP64__)
-  VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
-  EXPECT_EQ(0xfabcdef012345678UL, info.start());
-  EXPECT_EQ(0xf12345678abcdef8UL, info.end());
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags());
-  EXPECT_EQ(0xf0b0d0f010305070UL, info.offset());
+  VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", info.get());
+  EXPECT_EQ(0xfabcdef012345678UL, info->start());
+  EXPECT_EQ(0xf12345678abcdef8UL, info->end());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags());
+  EXPECT_EQ(0xf0b0d0f010305070UL, info->offset());
 #else
-  VerifyLine("f2345678-fabcdef8 rwxp f0305070 00:00 0\n", &info);
-  EXPECT_EQ(0xf2345678UL, info.start());
-  EXPECT_EQ(0xfabcdef8UL, info.end());
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags());
-  EXPECT_EQ(0xf0305070UL, info.offset());
+  VerifyLine("f2345678-fabcdef8 rwxp f0305070 00:00 0\n", info.get());
+  EXPECT_EQ(0xf2345678UL, info->start());
+  EXPECT_EQ(0xfabcdef8UL, info->end());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags());
+  EXPECT_EQ(0xf0305070UL, info->offset());
 #endif
 }
 
@@ -182,7 +200,7 @@
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(5U, maps.Total());
 
-  MapInfo* info = maps.Get(0);
+  auto info = maps.Get(0);
   ASSERT_TRUE(info != nullptr);
   EXPECT_EQ(PROT_NONE, info->flags());
   EXPECT_EQ(0x1000U, info->start());
@@ -234,7 +252,7 @@
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(3U, maps.Total());
 
-  MapInfo* info = maps.Get(0);
+  auto info = maps.Get(0);
   ASSERT_TRUE(info != nullptr);
   EXPECT_EQ("", info->name());
   EXPECT_EQ(0x7b29b000U, info->start());
@@ -269,7 +287,7 @@
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(2U, maps.Total());
 
-  MapInfo* info = maps.Get(0);
+  auto info = maps.Get(0);
   ASSERT_TRUE(info != nullptr);
   EXPECT_EQ(0U, info->offset());
   EXPECT_EQ(0xa000U, info->start());
@@ -334,7 +352,7 @@
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(4U, maps.Total());
 
-  MapInfo* info = maps.Get(0);
+  auto info = maps.Get(0);
   ASSERT_TRUE(info != nullptr);
   EXPECT_TRUE(info->flags() & 0x8000);
   EXPECT_EQ("/dev/", info->name());
@@ -367,7 +385,7 @@
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(3U, maps.Total());
 
-  MapInfo* info = maps.Get(0);
+  auto info = maps.Get(0);
   ASSERT_TRUE(info != nullptr);
   EXPECT_EQ(0x7b29b000U, info->start());
   EXPECT_EQ(0x7b29e000U, info->end());
@@ -409,7 +427,7 @@
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(3U, maps.Total());
 
-  MapInfo* info = maps.Get(0);
+  auto info = maps.Get(0);
   ASSERT_TRUE(info != nullptr);
   EXPECT_EQ(0x7b29b000U, info->start());
   EXPECT_EQ(0x7b29e000U, info->end());
@@ -501,7 +519,7 @@
   EXPECT_EQ(index, maps.Total());
   // Verify all of the maps.
   for (size_t i = 0; i < index; i++) {
-    MapInfo* info = maps.Get(i);
+    auto info = maps.Get(i);
     ASSERT_TRUE(info != nullptr) << "Failed verifying index " + std::to_string(i);
     EXPECT_EQ(i * 4096, info->start()) << "Failed verifying index " + std::to_string(i);
     EXPECT_EQ((i + 1) * 4096, info->end()) << "Failed verifying index " + std::to_string(i);
@@ -551,7 +569,7 @@
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(5000U, maps.Total());
   for (size_t i = 0; i < 5000; i++) {
-    MapInfo* info = maps.Get(i);
+    auto info = maps.Get(i);
     EXPECT_EQ(start + i * 4096, info->start()) << "Failed at map " + std::to_string(i);
     EXPECT_EQ(start + (i + 1) * 4096, info->end()) << "Failed at map " + std::to_string(i);
     std::string name = "/fake" + std::to_string(i) + ".so";
@@ -576,7 +594,7 @@
   EXPECT_TRUE(maps.Find(0xf000) == nullptr);
   EXPECT_TRUE(maps.Find(0xf010) == nullptr);
 
-  MapInfo* info = maps.Find(0x1000);
+  auto info = maps.Find(0x1000);
   ASSERT_TRUE(info != nullptr);
   EXPECT_EQ(0x1000U, info->start());
   EXPECT_EQ(0x2000U, info->end());
@@ -617,4 +635,75 @@
   EXPECT_EQ("/system/lib/fake5.so", info->name());
 }
 
+TEST(MapsTest, sort) {
+  Maps maps;
+
+  maps.Add(0x8000, 0x9000, 0, 0, "", 0);
+  maps.Add(0x7000, 0x8000, 0, 0, "lib.so", 0);
+  maps.Add(0x6000, 0x7000, 0, 0, "", 0);
+  maps.Add(0x5000, 0x6000, 0, 0, "lib.so", 0);
+  maps.Add(0x4000, 0x5000, 0, 0, "", 0);
+  maps.Add(0x3000, 0x4000, 0, 0, "", 0);
+  maps.Add(0x2000, 0x3000, 0, 0, "lib.so", 0);
+  maps.Add(0x1000, 0x2000, 0, 0, "", 0);
+
+  maps.Sort();
+
+  EXPECT_EQ(0x1000UL, maps.Get(0)->start());
+  EXPECT_EQ(nullptr, maps.Get(0)->prev_map());
+  EXPECT_EQ(maps.Get(1), maps.Get(0)->next_map());
+  EXPECT_EQ(nullptr, maps.Get(0)->GetPrevRealMap());
+  EXPECT_EQ(nullptr, maps.Get(0)->GetNextRealMap());
+
+  EXPECT_EQ(0x2000UL, maps.Get(1)->start());
+  EXPECT_EQ(maps.Get(0), maps.Get(1)->prev_map());
+  EXPECT_EQ(maps.Get(2), maps.Get(1)->next_map());
+  EXPECT_EQ(nullptr, maps.Get(1)->GetPrevRealMap());
+  EXPECT_EQ(maps.Get(4), maps.Get(1)->GetNextRealMap());
+
+  EXPECT_EQ(0x3000UL, maps.Get(2)->start());
+  EXPECT_EQ(maps.Get(1), maps.Get(2)->prev_map());
+  EXPECT_EQ(maps.Get(3), maps.Get(2)->next_map());
+  EXPECT_EQ(nullptr, maps.Get(2)->GetPrevRealMap());
+  EXPECT_EQ(nullptr, maps.Get(2)->GetNextRealMap());
+
+  EXPECT_EQ(0x4000UL, maps.Get(3)->start());
+  EXPECT_EQ(maps.Get(2), maps.Get(3)->prev_map());
+  EXPECT_EQ(maps.Get(4), maps.Get(3)->next_map());
+  EXPECT_EQ(nullptr, maps.Get(3)->GetPrevRealMap());
+  EXPECT_EQ(nullptr, maps.Get(3)->GetNextRealMap());
+
+  EXPECT_EQ(0x5000UL, maps.Get(4)->start());
+  EXPECT_EQ(maps.Get(3), maps.Get(4)->prev_map());
+  EXPECT_EQ(maps.Get(5), maps.Get(4)->next_map());
+  EXPECT_EQ(maps.Get(1), maps.Get(4)->GetPrevRealMap());
+  EXPECT_EQ(maps.Get(6), maps.Get(4)->GetNextRealMap());
+
+  EXPECT_EQ(0x6000UL, maps.Get(5)->start());
+  EXPECT_EQ(maps.Get(4), maps.Get(5)->prev_map());
+  EXPECT_EQ(maps.Get(6), maps.Get(5)->next_map());
+  EXPECT_EQ(nullptr, maps.Get(5)->GetPrevRealMap());
+  EXPECT_EQ(nullptr, maps.Get(5)->GetNextRealMap());
+
+  EXPECT_EQ(0x7000UL, maps.Get(6)->start());
+  EXPECT_EQ(maps.Get(5), maps.Get(6)->prev_map());
+  EXPECT_EQ(maps.Get(7), maps.Get(6)->next_map());
+  EXPECT_EQ(maps.Get(4), maps.Get(6)->GetPrevRealMap());
+  EXPECT_EQ(nullptr, maps.Get(6)->GetNextRealMap());
+
+  EXPECT_EQ(0x8000UL, maps.Get(7)->start());
+  EXPECT_EQ(maps.Get(6), maps.Get(7)->prev_map());
+  EXPECT_EQ(nullptr, maps.Get(7)->next_map());
+  EXPECT_EQ(nullptr, maps.Get(7)->GetPrevRealMap());
+  EXPECT_EQ(nullptr, maps.Get(7)->GetNextRealMap());
+}
+
+TEST(MapsTest, sort_empty) {
+  Maps maps;
+
+  maps.Sort();
+
+  EXPECT_EQ(0ULL, maps.Total());
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryBufferTest.cpp b/libunwindstack/tests/MemoryBufferTest.cpp
index ffd6817..91b20dd 100644
--- a/libunwindstack/tests/MemoryBufferTest.cpp
+++ b/libunwindstack/tests/MemoryBufferTest.cpp
@@ -103,7 +103,14 @@
   ASSERT_TRUE(memory_->Resize(256));
 
   ASSERT_TRUE(memory_->Resize(1024));
+}
 
+extern "C" void __hwasan_init() __attribute__((weak));
+
+TEST_F(MemoryBufferTest, Resize_too_large) {
+  if (&__hwasan_init != 0) {
+    GTEST_SKIP() << "Tests fails hwasan allocation size too large check.";
+  }
   ASSERT_FALSE(memory_->Resize(SIZE_MAX));
 }
 
diff --git a/libunwindstack/tests/MemoryCacheTest.cpp b/libunwindstack/tests/MemoryCacheTest.cpp
index 3bd3e4d..ae0c61b 100644
--- a/libunwindstack/tests/MemoryCacheTest.cpp
+++ b/libunwindstack/tests/MemoryCacheTest.cpp
@@ -21,7 +21,7 @@
 #include <gtest/gtest.h>
 
 #include "MemoryCache.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/MemoryMteTest.cpp b/libunwindstack/tests/MemoryMteTest.cpp
index 9aab0c0..8129394 100644
--- a/libunwindstack/tests/MemoryMteTest.cpp
+++ b/libunwindstack/tests/MemoryMteTest.cpp
@@ -25,6 +25,7 @@
 
 #include "MemoryLocal.h"
 #include "MemoryRemote.h"
+#include "PidUtils.h"
 #include "TestUtils.h"
 
 namespace unwindstack {
@@ -68,14 +69,14 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(TestAttach(pid));
+  ASSERT_TRUE(Attach(pid));
 
   MemoryRemote remote(pid);
 
   EXPECT_EQ(1, remote.ReadTag(mapping));
   EXPECT_EQ(0, remote.ReadTag(mapping + 16));
 
-  ASSERT_TRUE(TestDetach(pid));
+  ASSERT_TRUE(Detach(pid));
 }
 
 TEST(MemoryMteTest, local_read_tag) {
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index 2d4f141..7a374eb 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -21,8 +21,8 @@
 
 #include <gtest/gtest.h>
 
-#include "MemoryFake.h"
 #include "MemoryRange.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/MemoryRangesTest.cpp b/libunwindstack/tests/MemoryRangesTest.cpp
index e4e9fc4..cf3250a 100644
--- a/libunwindstack/tests/MemoryRangesTest.cpp
+++ b/libunwindstack/tests/MemoryRangesTest.cpp
@@ -20,8 +20,8 @@
 
 #include <gtest/gtest.h>
 
-#include "MemoryFake.h"
 #include "MemoryRange.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
@@ -86,4 +86,10 @@
   }
 }
 
+TEST_F(MemoryRangesTest, duplicate_last_addr) {
+  MemoryRanges ranges;
+  ASSERT_TRUE(ranges.Insert(new MemoryRange(nullptr, 0x1000, 0x2000, 0x1000)));
+  ASSERT_FALSE(ranges.Insert(new MemoryRange(nullptr, 0x2000, 0x1000, 0x2000)));
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index 621893b..ed58eb9 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -32,8 +32,9 @@
 
 #include "MemoryRemote.h"
 
-#include "MemoryFake.h"
+#include "PidUtils.h"
 #include "TestUtils.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
@@ -49,7 +50,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(TestAttach(pid));
+  ASSERT_TRUE(Attach(pid));
 
   MemoryRemote remote(pid);
 
@@ -59,7 +60,7 @@
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_TRUE(TestDetach(pid));
+  ASSERT_TRUE(Detach(pid));
 }
 
 TEST(MemoryRemoteTest, read_large) {
@@ -78,7 +79,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(TestAttach(pid));
+  ASSERT_TRUE(Attach(pid));
 
   MemoryRemote remote(pid);
 
@@ -88,7 +89,7 @@
     ASSERT_EQ(i / getpagesize(), dst[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_TRUE(TestDetach(pid));
+  ASSERT_TRUE(Detach(pid));
 }
 
 TEST(MemoryRemoteTest, read_partial) {
@@ -111,7 +112,7 @@
   // Unmap from our process.
   ASSERT_EQ(0, munmap(mapping, 3 * getpagesize()));
 
-  ASSERT_TRUE(TestAttach(pid));
+  ASSERT_TRUE(Attach(pid));
 
   MemoryRemote remote(pid);
 
@@ -132,7 +133,7 @@
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_TRUE(TestDetach(pid));
+  ASSERT_TRUE(Detach(pid));
 }
 
 TEST(MemoryRemoteTest, read_fail) {
@@ -152,7 +153,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(TestAttach(pid));
+  ASSERT_TRUE(Attach(pid));
 
   MemoryRemote remote(pid);
 
@@ -171,7 +172,7 @@
 
   ASSERT_EQ(0, munmap(src, pagesize));
 
-  ASSERT_TRUE(TestDetach(pid));
+  ASSERT_TRUE(Detach(pid));
 }
 
 TEST(MemoryRemoteTest, read_overflow) {
@@ -184,7 +185,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(TestAttach(pid));
+  ASSERT_TRUE(Attach(pid));
 
   MemoryRemote remote(pid);
 
@@ -192,7 +193,7 @@
   std::vector<uint8_t> dst(200);
   ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200));
 
-  ASSERT_TRUE(TestDetach(pid));
+  ASSERT_TRUE(Detach(pid));
 }
 
 TEST(MemoryRemoteTest, read_illegal) {
@@ -204,7 +205,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(TestAttach(pid));
+  ASSERT_TRUE(Attach(pid));
 
   MemoryRemote remote(pid);
 
@@ -212,7 +213,7 @@
   ASSERT_FALSE(remote.ReadFully(0, dst.data(), 1));
   ASSERT_FALSE(remote.ReadFully(0, dst.data(), 100));
 
-  ASSERT_TRUE(TestDetach(pid));
+  ASSERT_TRUE(Detach(pid));
 }
 
 TEST(MemoryRemoteTest, read_mprotect_hole) {
@@ -233,7 +234,7 @@
 
   ASSERT_EQ(0, munmap(mapping, 3 * page_size));
 
-  ASSERT_TRUE(TestAttach(pid));
+  ASSERT_TRUE(Attach(pid));
 
   MemoryRemote remote(pid);
   std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
@@ -247,7 +248,7 @@
     ASSERT_EQ(0xCC, dst[i]);
   }
 
-  ASSERT_TRUE(TestDetach(pid));
+  ASSERT_TRUE(Detach(pid));
 }
 
 TEST(MemoryRemoteTest, read_munmap_hole) {
@@ -270,7 +271,7 @@
   ASSERT_EQ(0, munmap(mapping, page_size));
   ASSERT_EQ(0, munmap(static_cast<char*>(mapping) + 2 * page_size, page_size));
 
-  ASSERT_TRUE(TestAttach(pid));
+  ASSERT_TRUE(Attach(pid));
 
   MemoryRemote remote(pid);
   std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
@@ -283,7 +284,7 @@
     ASSERT_EQ(0xCC, dst[i]);
   }
 
-  ASSERT_TRUE(TestDetach(pid));
+  ASSERT_TRUE(Detach(pid));
 }
 
 // Verify that the memory remote object chooses a memory read function
@@ -307,7 +308,7 @@
 
   ASSERT_EQ(0, munmap(mapping, 2 * page_size));
 
-  ASSERT_TRUE(TestAttach(pid));
+  ASSERT_TRUE(Attach(pid));
 
   // We know that process_vm_readv of a mprotect'd PROT_NONE region will fail.
   // Read from the PROT_NONE area first to force the choice of ptrace.
@@ -336,7 +337,7 @@
   ASSERT_EQ(sizeof(value), bytes);
   ASSERT_EQ(0xfcfcfcfcU, value);
 
-  ASSERT_TRUE(TestDetach(pid));
+  ASSERT_TRUE(Detach(pid));
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
index 8a8eb24..268e536 100644
--- a/libunwindstack/tests/MemoryTest.cpp
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -24,7 +24,7 @@
 
 #include <unwindstack/Memory.h>
 
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/MemoryThreadCacheTest.cpp b/libunwindstack/tests/MemoryThreadCacheTest.cpp
index 2c499bf..0ecfba9 100644
--- a/libunwindstack/tests/MemoryThreadCacheTest.cpp
+++ b/libunwindstack/tests/MemoryThreadCacheTest.cpp
@@ -22,7 +22,7 @@
 #include <gtest/gtest.h>
 
 #include "MemoryCache.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
@@ -164,17 +164,23 @@
   }
 }
 
-TEST_F(MemoryThreadCacheTest, read_uncached_due_to_error) {
+static void ExhaustPthreadKeys(std::vector<pthread_key_t>* keys) {
   // Use up all of the keys to force the next attempt to create one to fail.
   static constexpr size_t kMaxKeysToCreate = 10000;
-  std::vector<pthread_key_t> keys(kMaxKeysToCreate);
+  keys->resize(kMaxKeysToCreate);
   for (size_t i = 0; i < kMaxKeysToCreate; i++) {
-    if (pthread_key_create(&keys[i], nullptr) != 0) {
-      keys.resize(i);
+    if (pthread_key_create(&(*keys)[i], nullptr) != 0) {
+      keys->resize(i);
       break;
     }
   }
-  ASSERT_NE(kMaxKeysToCreate, keys.size()) << "Cannot exist pthread keys.";
+  ASSERT_NE(0U, keys->size()) << "No keys created.";
+  ASSERT_LT(keys->size(), kMaxKeysToCreate) << "Cannot use up pthread keys.";
+}
+
+TEST_F(MemoryThreadCacheTest, read_uncached_due_to_error) {
+  std::vector<pthread_key_t> keys;
+  ASSERT_NO_FATAL_FAILURE(ExhaustPthreadKeys(&keys));
 
   MemoryFake* fake = new MemoryFake;
   MemoryThreadCache memory(fake);
@@ -199,4 +205,17 @@
   }
 }
 
+TEST_F(MemoryThreadCacheTest, clear_cache_when_no_cache) {
+  std::vector<pthread_key_t> keys;
+  ASSERT_NO_FATAL_FAILURE(ExhaustPthreadKeys(&keys));
+
+  MemoryFake* fake = new MemoryFake;
+  MemoryThreadCache memory(fake);
+  memory.Clear();
+
+  for (pthread_key_t& key : keys) {
+    pthread_key_delete(key);
+  }
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsRemoteTest.cpp b/libunwindstack/tests/RegsRemoteTest.cpp
new file mode 100644
index 0000000..2427501
--- /dev/null
+++ b/libunwindstack/tests/RegsRemoteTest.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 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 <signal.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Regs.h>
+
+#include "PidUtils.h"
+
+namespace unwindstack {
+
+class RegsRemoteTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    if ((pid_ = fork()) == 0) {
+      volatile bool run = true;
+      while (!run) {
+      }
+      exit(1);
+    }
+    ASSERT_TRUE(pid_ != -1);
+    ASSERT_TRUE(Attach(pid_));
+  }
+
+  void TearDown() override {
+    if (pid_ == -1) {
+      return;
+    }
+    EXPECT_TRUE(Detach(pid_));
+    kill(pid_, SIGKILL);
+    waitpid(pid_, nullptr, 0);
+  }
+
+  pid_t pid_ = -1;
+};
+
+TEST_F(RegsRemoteTest, remote_get) {
+  std::unique_ptr<Regs> regs(Regs::RemoteGet(pid_));
+#if defined(__arm__)
+  ASSERT_EQ(ARCH_ARM, regs->Arch());
+#elif defined(__aarch64__)
+  ASSERT_EQ(ARCH_ARM64, regs->Arch());
+#elif defined(__i386__)
+  ASSERT_EQ(ARCH_X86, regs->Arch());
+#elif defined(__x86_64__)
+  ASSERT_EQ(ARCH_X86_64, regs->Arch());
+#else
+  ASSERT_EQ(nullptr, regs.get());
+#endif
+}
+
+TEST_F(RegsRemoteTest, remote_get_arch) {
+#if defined(__arm__)
+  ASSERT_EQ(ARCH_ARM, Regs::RemoteGetArch(pid_));
+#elif defined(__aarch64__)
+  ASSERT_EQ(ARCH_ARM64, Regs::RemoteGetArch(pid_));
+#elif defined(__i386__)
+  ASSERT_EQ(ARCH_X86, Regs::RemoteGetArch(pid_));
+#elif defined(__x86_64__)
+  ASSERT_EQ(ARCH_X86_64, Regs::RemoteGetArch(pid_));
+#else
+  ASSERT_EQ(ARCH_NONE, Regs::RemoteGetArch(pid_));
+#endif
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
index eac12ca..77e930f 100644
--- a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
+++ b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
@@ -32,7 +32,7 @@
 #include <unwindstack/RegsX86.h>
 #include <unwindstack/RegsX86_64.h>
 
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 716a847..7c10d9e 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -16,6 +16,8 @@
 
 #include <stdint.h>
 
+#include <memory>
+
 #include <gtest/gtest.h>
 
 #include <unwindstack/Elf.h>
@@ -29,8 +31,8 @@
 #include <unwindstack/RegsMips64.h>
 
 #include "ElfFake.h"
-#include "MemoryFake.h"
 #include "RegsFake.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
@@ -169,27 +171,27 @@
 }
 
 TEST_F(RegsTest, elf_invalid) {
-  MapInfo map_info(nullptr, nullptr, 0x1000, 0x2000, 0, 0, "");
+  auto map_info = MapInfo::Create(0x1000, 0x2000, 0, 0, "");
   Elf* invalid_elf = new Elf(nullptr);
-  map_info.set_elf(invalid_elf);
+  map_info->set_elf(invalid_elf);
 
-  EXPECT_EQ(0x500U, invalid_elf->GetRelPc(0x1500, &map_info));
+  EXPECT_EQ(0x500U, invalid_elf->GetRelPc(0x1500, map_info.get()));
   EXPECT_EQ(2U, GetPcAdjustment(0x500U, invalid_elf, ARCH_ARM));
   EXPECT_EQ(2U, GetPcAdjustment(0x511U, invalid_elf, ARCH_ARM));
 
-  EXPECT_EQ(0x600U, invalid_elf->GetRelPc(0x1600, &map_info));
+  EXPECT_EQ(0x600U, invalid_elf->GetRelPc(0x1600, map_info.get()));
   EXPECT_EQ(4U, GetPcAdjustment(0x600U, invalid_elf, ARCH_ARM64));
 
-  EXPECT_EQ(0x700U, invalid_elf->GetRelPc(0x1700, &map_info));
+  EXPECT_EQ(0x700U, invalid_elf->GetRelPc(0x1700, map_info.get()));
   EXPECT_EQ(1U, GetPcAdjustment(0x700U, invalid_elf, ARCH_X86));
 
-  EXPECT_EQ(0x800U, invalid_elf->GetRelPc(0x1800, &map_info));
+  EXPECT_EQ(0x800U, invalid_elf->GetRelPc(0x1800, map_info.get()));
   EXPECT_EQ(1U, GetPcAdjustment(0x800U, invalid_elf, ARCH_X86_64));
 
-  EXPECT_EQ(0x900U, invalid_elf->GetRelPc(0x1900, &map_info));
+  EXPECT_EQ(0x900U, invalid_elf->GetRelPc(0x1900, map_info.get()));
   EXPECT_EQ(8U, GetPcAdjustment(0x900U, invalid_elf, ARCH_MIPS));
 
-  EXPECT_EQ(0xa00U, invalid_elf->GetRelPc(0x1a00, &map_info));
+  EXPECT_EQ(0xa00U, invalid_elf->GetRelPc(0x1a00, map_info.get()));
   EXPECT_EQ(8U, GetPcAdjustment(0xa00U, invalid_elf, ARCH_MIPS64));
 }
 
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index 89147a2..c4f6f9b 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -34,8 +34,8 @@
 
 #include <unwindstack/Memory.h>
 
-#include "MemoryFake.h"
 #include "Symbols.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/TestLocal.cpp b/libunwindstack/tests/TestLocal.cpp
index fa0baff..25bd42f 100644
--- a/libunwindstack/tests/TestLocal.cpp
+++ b/libunwindstack/tests/TestLocal.cpp
@@ -14,26 +14,30 @@
  * limitations under the License.
  */
 
-#include <unwindstack/LocalUnwinder.h>
+#include <stdint.h>
+#include <stdlib.h>
 
-#include <vector>
+#include "TestUtils.h"
 
-extern "C" void TestlibLevel4(void* unwinder_data, void* frame_data) {
-  unwindstack::LocalUnwinder* unwinder =
-      reinterpret_cast<unwindstack::LocalUnwinder*>(unwinder_data);
-  std::vector<unwindstack::LocalFrameData>* frame_info =
-      reinterpret_cast<std::vector<unwindstack::LocalFrameData>*>(frame_data);
-  unwinder->Unwind(frame_info, 256);
+// The loop in this function is only guaranteed to not be optimized away by the compiler
+// if optimizations are turned off. This is partially because the compiler doesn't have
+// any idea about the function since it is retrieved using dlsym.
+//
+// In an effort to defend against the compiler:
+//  1. The loop iteration variable is volatile.
+//  2. A call to this function should be wrapped in TestUtils::DoNotOptimize().
+extern "C" int BusyWait() {
+  for (size_t i = 0; i < 1000000;) {
+    unwindstack::DoNotOptimize(i++);
+  }
+  return 0;
 }
 
-extern "C" void TestlibLevel3(void* unwinder_data, void* frame_data) {
-  TestlibLevel4(unwinder_data, frame_data);
-}
-
-extern "C" void TestlibLevel2(void* unwinder_data, void* frame_data) {
-  TestlibLevel3(unwinder_data, frame_data);
-}
-
-extern "C" void TestlibLevel1(void* unwinder_data, void* frame_data) {
-  TestlibLevel2(unwinder_data, frame_data);
+// Do a loop that guarantees the terminating leaf frame will be in
+// the this library and not a function from a different library.
+extern "C" void WaitForever() {
+  bool run = true;
+  while (run) {
+    unwindstack::DoNotOptimize(run = true);
+  }
 }
diff --git a/libunwindstack/tests/TestUtils.cpp b/libunwindstack/tests/TestUtils.cpp
index ecc2d45..1f748ba 100644
--- a/libunwindstack/tests/TestUtils.cpp
+++ b/libunwindstack/tests/TestUtils.cpp
@@ -14,11 +14,16 @@
  * limitations under the License.
  */
 
+#include <dlfcn.h>
 #include <malloc.h>
 #include <stdint.h>
 
+#include <string>
+
 #include <gtest/gtest.h>
 
+#include "TestUtils.h"
+
 namespace unwindstack {
 
 void TestCheckForLeaks(void (*unwind_func)(void*), void* data) {
@@ -42,4 +47,17 @@
   }
 }
 
+void* GetTestLibHandle() {
+  std::string testlib(testing::internal::GetArgvs()[0]);
+  auto const value = testlib.find_last_of('/');
+  if (value != std::string::npos) {
+    testlib = testlib.substr(0, value + 1);
+  } else {
+    testlib = "";
+  }
+  testlib += "libunwindstack_local.so";
+
+  return dlopen(testlib.c_str(), RTLD_NOW);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
index 0685006..19cd693 100644
--- a/libunwindstack/tests/TestUtils.h
+++ b/libunwindstack/tests/TestUtils.h
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
-#define _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+#pragma once
 
 #include <signal.h>
-#include <sys/ptrace.h>
+#include <stdint.h>
 #include <sys/types.h>
 #include <sys/wait.h>
-#include <unistd.h>
 
 namespace unwindstack {
 
@@ -37,34 +35,15 @@
   pid_t pid_;
 };
 
-inline bool TestQuiescePid(pid_t pid) {
-  siginfo_t si;
-  bool ready = false;
-  // Wait for up to 5 seconds.
-  for (size_t i = 0; i < 5000; i++) {
-    if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
-      ready = true;
-      break;
-    }
-    usleep(1000);
-  }
-  return ready;
-}
-
-inline bool TestAttach(pid_t pid) {
-  if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
-    return false;
-  }
-
-  return TestQuiescePid(pid);
-}
-
-inline bool TestDetach(pid_t pid) {
-  return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
-}
-
 void TestCheckForLeaks(void (*unwind_func)(void*), void* data);
 
-}  // namespace unwindstack
+void* GetTestLibHandle();
 
-#endif  // _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+// TODO(b/148307629): Once we incorporate google benchmark library into
+// GoogleTest, we can call benchmark::DoNotOptimize here instead.
+template <class Tp>
+static inline void DoNotOptimize(Tp const& value) {
+  asm volatile("" : : "r,m"(value) : "memory");
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index c834d0d..0d6fa1d 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -14,218 +14,108 @@
  * limitations under the License.
  */
 
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <sys/mman.h>
-#include <unistd.h>
 
 #include <gtest/gtest.h>
 
+#include <cstddef>
+#include <fstream>
+#include <memory>
+#include <sstream>
 #include <string>
-#include <unordered_map>
-#include <vector>
 
-#include <android-base/file.h>
-
-#include <unwindstack/JitDebug.h>
-#include <unwindstack/MachineArm.h>
-#include <unwindstack/MachineArm64.h>
-#include <unwindstack/MachineX86.h>
-#include <unwindstack/MachineX86_64.h>
-#include <unwindstack/Maps.h>
-#include <unwindstack/RegsArm.h>
+#include <unwindstack/Arch.h>
+#include <unwindstack/Memory.h>
 #include <unwindstack/RegsArm64.h>
-#include <unwindstack/RegsX86.h>
-#include <unwindstack/RegsX86_64.h>
 #include <unwindstack/Unwinder.h>
 
-#include "ElfTestUtils.h"
-#include "MemoryFake.h"
-#include "MemoryOffline.h"
 #include "TestUtils.h"
+#include "utils/MemoryFake.h"
+#include "utils/OfflineUnwindUtils.h"
 
+// This collection of tests exercises Unwinder::Unwind for offline unwinds.
+//
+// See `libunwindstack/utils/OfflineUnwindUtils.h` for more info on offline unwinds
+// and b/192012600 for additional information regarding offline unwind benchmarks.
 namespace unwindstack {
-
-static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
-  MemoryOffline* memory = new MemoryOffline;
-  ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
-  parts->Add(memory);
-}
+namespace {
 
 class UnwindOfflineTest : public ::testing::Test {
+ public:
+  bool GetExpectedSamplesFrameInfo(
+      std::string* expected_frame_info, std::string* error_msg,
+      const std::string& sample_name = OfflineUnwindUtils::kSingleSample) {
+    const std::string* a_frame_info_path = offline_utils_.GetFrameInfoFilepath(sample_name);
+    if (a_frame_info_path == nullptr) {
+      std::stringstream err_stream;
+      err_stream << "Unable to get frame info filepath for invalid sample name " << sample_name
+                 << ".\n";
+      *error_msg = err_stream.str();
+      return false;
+    }
+
+    std::ifstream in(*a_frame_info_path);
+    std::stringstream buffer;
+    buffer << in.rdbuf();
+    *expected_frame_info = buffer.str();
+    return true;
+  }
+
+  void ConsecutiveUnwindTest(const std::vector<UnwindSampleInfo>& sample_infos) {
+    std::string error_msg;
+    if (!offline_utils_.Init(sample_infos, &error_msg)) FAIL() << error_msg;
+
+    for (const auto& sample_info : sample_infos) {
+      const std::string& sample_name = sample_info.offline_files_dir;
+      // Need to change to sample directory for Unwinder to properly init ELF objects.
+      // See more info at OfflineUnwindUtils::ChangeToSampleDirectory.
+      if (!offline_utils_.ChangeToSampleDirectory(&error_msg, sample_name)) FAIL() << error_msg;
+
+      Unwinder unwinder =
+          Unwinder(128, offline_utils_.GetMaps(sample_name), offline_utils_.GetRegs(sample_name),
+                   offline_utils_.GetProcessMemory(sample_name));
+      if (sample_info.memory_flag == ProcessMemoryFlag::kIncludeJitMemory) {
+        unwinder.SetJitDebug(offline_utils_.GetJitDebug(sample_name));
+      }
+      unwinder.Unwind();
+
+      size_t expected_num_frames;
+      if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg, sample_name))
+        FAIL() << error_msg;
+      std::string expected_frame_info;
+      if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg, sample_name))
+        FAIL() << error_msg;
+
+      std::string actual_frame_info = DumpFrames(unwinder);
+      ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << actual_frame_info;
+      EXPECT_EQ(expected_frame_info, actual_frame_info);
+    }
+  }
+
  protected:
-  void TearDown() override {
-    if (cwd_ != nullptr) {
-      ASSERT_EQ(0, chdir(cwd_));
-    }
-    free(cwd_);
-  }
+  void TearDown() override { offline_utils_.ReturnToCurrentWorkingDirectory(); }
 
-  void Init(const char* file_dir, ArchEnum arch, bool add_stack = true) {
-    dir_ = TestGetFileDirectory() + "offline/" + file_dir;
-
-    std::string data;
-    ASSERT_TRUE(android::base::ReadFileToString((dir_ + "maps.txt"), &data));
-
-    maps_.reset(new BufferMaps(data.c_str()));
-    ASSERT_TRUE(maps_->Parse());
-
-    if (add_stack) {
-      std::string stack_name(dir_ + "stack.data");
-      struct stat st;
-      if (stat(stack_name.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
-        std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
-        ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
-        process_memory_.reset(stack_memory.release());
-      } else {
-        std::unique_ptr<MemoryOfflineParts> stack_memory(new MemoryOfflineParts);
-        for (size_t i = 0;; i++) {
-          stack_name = dir_ + "stack" + std::to_string(i) + ".data";
-          if (stat(stack_name.c_str(), &st) == -1 || !S_ISREG(st.st_mode)) {
-            ASSERT_TRUE(i != 0) << "No stack data files found.";
-            break;
-          }
-          AddMemory(stack_name, stack_memory.get());
-        }
-        process_memory_.reset(stack_memory.release());
-      }
-    }
-
-    switch (arch) {
-      case ARCH_ARM: {
-        RegsArm* regs = new RegsArm;
-        regs_.reset(regs);
-        ReadRegs<uint32_t>(regs, arm_regs_);
-        break;
-      }
-      case ARCH_ARM64: {
-        RegsArm64* regs = new RegsArm64;
-        regs_.reset(regs);
-        ReadRegs<uint64_t>(regs, arm64_regs_);
-        break;
-      }
-      case ARCH_X86: {
-        RegsX86* regs = new RegsX86;
-        regs_.reset(regs);
-        ReadRegs<uint32_t>(regs, x86_regs_);
-        break;
-      }
-      case ARCH_X86_64: {
-        RegsX86_64* regs = new RegsX86_64;
-        regs_.reset(regs);
-        ReadRegs<uint64_t>(regs, x86_64_regs_);
-        break;
-      }
-      default:
-        ASSERT_TRUE(false) << "Unknown arch " << std::to_string(arch);
-    }
-    cwd_ = getcwd(nullptr, 0);
-    // Make dir_ an absolute directory.
-    if (dir_.empty() || dir_[0] != '/') {
-      dir_ = std::string(cwd_) + '/' + dir_;
-    }
-    ASSERT_EQ(0, chdir(dir_.c_str()));
-
-    if (process_memory_ == nullptr) {
-      process_memory_.reset(new MemoryFake);
-    }
-  }
-
-  template <typename AddressType>
-  void ReadRegs(RegsImpl<AddressType>* regs,
-                const std::unordered_map<std::string, uint32_t>& name_to_reg) {
-    FILE* fp = fopen((dir_ + "regs.txt").c_str(), "r");
-    ASSERT_TRUE(fp != nullptr);
-    while (!feof(fp)) {
-      uint64_t value;
-      char reg_name[100];
-      ASSERT_EQ(2, fscanf(fp, "%s %" SCNx64 "\n", reg_name, &value));
-      std::string name(reg_name);
-      if (!name.empty()) {
-        // Remove the : from the end.
-        name.resize(name.size() - 1);
-      }
-      auto entry = name_to_reg.find(name);
-      ASSERT_TRUE(entry != name_to_reg.end()) << "Unknown register named " << name;
-      (*regs)[entry->second] = value;
-    }
-    fclose(fp);
-  }
-
-  static std::unordered_map<std::string, uint32_t> arm_regs_;
-  static std::unordered_map<std::string, uint32_t> arm64_regs_;
-  static std::unordered_map<std::string, uint32_t> x86_regs_;
-  static std::unordered_map<std::string, uint32_t> x86_64_regs_;
-
-  char* cwd_ = nullptr;
-  std::string dir_;
-  std::unique_ptr<Regs> regs_;
-  std::unique_ptr<Maps> maps_;
-  std::shared_ptr<Memory> process_memory_;
+  OfflineUnwindUtils offline_utils_;
 };
 
-std::unordered_map<std::string, uint32_t> UnwindOfflineTest::arm_regs_ = {
-    {"r0", ARM_REG_R0},  {"r1", ARM_REG_R1}, {"r2", ARM_REG_R2},   {"r3", ARM_REG_R3},
-    {"r4", ARM_REG_R4},  {"r5", ARM_REG_R5}, {"r6", ARM_REG_R6},   {"r7", ARM_REG_R7},
-    {"r8", ARM_REG_R8},  {"r9", ARM_REG_R9}, {"r10", ARM_REG_R10}, {"r11", ARM_REG_R11},
-    {"ip", ARM_REG_R12}, {"sp", ARM_REG_SP}, {"lr", ARM_REG_LR},   {"pc", ARM_REG_PC},
-};
-
-std::unordered_map<std::string, uint32_t> UnwindOfflineTest::arm64_regs_ = {
-    {"x0", ARM64_REG_R0},      {"x1", ARM64_REG_R1},   {"x2", ARM64_REG_R2},
-    {"x3", ARM64_REG_R3},      {"x4", ARM64_REG_R4},   {"x5", ARM64_REG_R5},
-    {"x6", ARM64_REG_R6},      {"x7", ARM64_REG_R7},   {"x8", ARM64_REG_R8},
-    {"x9", ARM64_REG_R9},      {"x10", ARM64_REG_R10}, {"x11", ARM64_REG_R11},
-    {"x12", ARM64_REG_R12},    {"x13", ARM64_REG_R13}, {"x14", ARM64_REG_R14},
-    {"x15", ARM64_REG_R15},    {"x16", ARM64_REG_R16}, {"x17", ARM64_REG_R17},
-    {"x18", ARM64_REG_R18},    {"x19", ARM64_REG_R19}, {"x20", ARM64_REG_R20},
-    {"x21", ARM64_REG_R21},    {"x22", ARM64_REG_R22}, {"x23", ARM64_REG_R23},
-    {"x24", ARM64_REG_R24},    {"x25", ARM64_REG_R25}, {"x26", ARM64_REG_R26},
-    {"x27", ARM64_REG_R27},    {"x28", ARM64_REG_R28}, {"x29", ARM64_REG_R29},
-    {"sp", ARM64_REG_SP},      {"lr", ARM64_REG_LR},   {"pc", ARM64_REG_PC},
-    {"pst", ARM64_REG_PSTATE},
-};
-
-std::unordered_map<std::string, uint32_t> UnwindOfflineTest::x86_regs_ = {
-    {"eax", X86_REG_EAX}, {"ebx", X86_REG_EBX}, {"ecx", X86_REG_ECX},
-    {"edx", X86_REG_EDX}, {"ebp", X86_REG_EBP}, {"edi", X86_REG_EDI},
-    {"esi", X86_REG_ESI}, {"esp", X86_REG_ESP}, {"eip", X86_REG_EIP},
-};
-
-std::unordered_map<std::string, uint32_t> UnwindOfflineTest::x86_64_regs_ = {
-    {"rax", X86_64_REG_RAX}, {"rbx", X86_64_REG_RBX}, {"rcx", X86_64_REG_RCX},
-    {"rdx", X86_64_REG_RDX}, {"r8", X86_64_REG_R8},   {"r9", X86_64_REG_R9},
-    {"r10", X86_64_REG_R10}, {"r11", X86_64_REG_R11}, {"r12", X86_64_REG_R12},
-    {"r13", X86_64_REG_R13}, {"r14", X86_64_REG_R14}, {"r15", X86_64_REG_R15},
-    {"rdi", X86_64_REG_RDI}, {"rsi", X86_64_REG_RSI}, {"rbp", X86_64_REG_RBP},
-    {"rsp", X86_64_REG_RSP}, {"rip", X86_64_REG_RIP},
-};
-
-static std::string DumpFrames(Unwinder& unwinder) {
-  std::string str;
-  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
-    str += unwinder.FormatFrame(i) + "\n";
-  }
-  return str;
-}
-
 TEST_F(UnwindOfflineTest, pc_straddle_arm) {
-  ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "straddle_arm/", .arch = ARCH_ARM}, &error_msg))
+    FAIL() << error_msg;
 
-  std::unique_ptr<Regs> regs_copy(regs_->Clone());
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Regs* regs = offline_utils_.GetRegs();
+  std::unique_ptr<Regs> regs_copy(regs->Clone());
+  Unwinder unwinder(128, offline_utils_.GetMaps(), regs, offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
   std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 0001a9f8  libc.so (abort+64)\n"
-      "  #01 pc 00006a1b  libbase.so (android::base::DefaultAborter(char const*)+6)\n"
-      "  #02 pc 00007441  libbase.so (android::base::LogMessage::~LogMessage()+748)\n"
-      "  #03 pc 00015147  /does/not/exist/libhidlbase.so\n",
-      frame_info);
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0xf31ea9f8U, unwinder.frames()[0].pc);
   EXPECT_EQ(0xe9c866f8U, unwinder.frames()[0].sp);
   EXPECT_EQ(0xf2da0a1bU, unwinder.frames()[1].pc);
@@ -241,7 +131,7 @@
   unwinder.Unwind();
 
   frame_info = DumpFrames(unwinder);
-  ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ(
       "  #00 pc 0001a9f8  libc.so (abort+64) (BuildId: 2dd0d4ba881322a0edabeed94808048c)\n"
       "  #01 pc 00006a1b  libbase.so (android::base::DefaultAborter(char const*)+6) (BuildId: "
@@ -253,20 +143,23 @@
 }
 
 TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
-  ASSERT_NO_FATAL_FAILURE(Init("gnu_debugdata_arm/", ARCH_ARM));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "gnu_debugdata_arm/", .arch = ARCH_ARM},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
   std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(2U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 0006dc49  libandroid_runtime.so "
-      "(android::AndroidRuntime::javaThreadShell(void*)+80)\n"
-      "  #01 pc 0006dce5  libandroid_runtime.so "
-      "(android::AndroidRuntime::javaCreateThreadEtc(int (*)(void*), void*, char const*, int, "
-      "unsigned int, void**))\n",
-      frame_info);
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0xf1f6dc49U, unwinder.frames()[0].pc);
   EXPECT_EQ(0xd8fe6930U, unwinder.frames()[0].sp);
   EXPECT_EQ(0xf1f6dce5U, unwinder.frames()[1].pc);
@@ -274,23 +167,23 @@
 }
 
 TEST_F(UnwindOfflineTest, pc_straddle_arm64) {
-  ASSERT_NO_FATAL_FAILURE(Init("straddle_arm64/", ARCH_ARM64));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "straddle_arm64/", .arch = ARCH_ARM64},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
   std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(6U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 0000000000429fd8  libunwindstack_test (SignalInnerFunction+24)\n"
-      "  #01 pc 000000000042a078  libunwindstack_test (SignalMiddleFunction+8)\n"
-      "  #02 pc 000000000042a08c  libunwindstack_test (SignalOuterFunction+8)\n"
-      "  #03 pc 000000000042d8fc  libunwindstack_test "
-      "(unwindstack::RemoteThroughSignal(int, unsigned int)+20)\n"
-      "  #04 pc 000000000042d8d8  libunwindstack_test "
-      "(unwindstack::UnwindTest_remote_through_signal_Test::TestBody()+32)\n"
-      "  #05 pc 0000000000455d70  libunwindstack_test (testing::Test::Run()+392)\n",
-      frame_info);
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0x64d09d4fd8U, unwinder.frames()[0].pc);
   EXPECT_EQ(0x7fe0d84040U, unwinder.frames()[0].sp);
   EXPECT_EQ(0x64d09d5078U, unwinder.frames()[1].pc);
@@ -306,166 +199,26 @@
 }
 
 TEST_F(UnwindOfflineTest, jit_debug_x86) {
-  ASSERT_NO_FATAL_FAILURE(Init("jit_debug_x86/", ARCH_X86));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "jit_debug_x86/",
+                            .arch = ARCH_X86,
+                            .memory_flag = ProcessMemoryFlag::kIncludeJitMemory},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  MemoryOfflineParts* memory = new MemoryOfflineParts;
-  AddMemory(dir_ + "descriptor.data", memory);
-  AddMemory(dir_ + "stack.data", memory);
-  for (size_t i = 0; i < 7; i++) {
-    AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
-    AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
-  }
-  process_memory_.reset(memory);
-
-  std::unique_ptr<JitDebug> jit_debug = CreateJitDebug(regs_->Arch(), process_memory_);
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
-  unwinder.SetJitDebug(jit_debug.get());
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
+  unwinder.SetJitDebug(offline_utils_.GetJitDebug());
   unwinder.Unwind();
 
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
   std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(69U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 00068fb8  libarttestd.so (art::CauseSegfault()+72)\n"
-      "  #01 pc 00067f00  libarttestd.so (Java_Main_unwindInProcess+10032)\n"
-      "  #02 pc 000021a8  137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
-      "boolean)+136)\n"
-      "  #03 pc 0000fe80  anonymous:ee74c000 (boolean Main.bar(boolean)+64)\n"
-      "  #04 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
-      "  #05 pc 00146ab5  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+885)\n"
-      "  #06 pc 0039cf0d  libartd.so "
-      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
-      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
-      "  #07 pc 00392552  libartd.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+354)\n"
-      "  #08 pc 0039399a  libartd.so "
-      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
-      "const&, art::ShadowFrame*)+234)\n"
-      "  #09 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
-      "  #10 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #11 pc 0000fe03  anonymous:ee74c000 (int Main.compare(Main, Main)+51)\n"
-      "  #12 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
-      "  #13 pc 00146ab5  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+885)\n"
-      "  #14 pc 0039cf0d  libartd.so "
-      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
-      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
-      "  #15 pc 00392552  libartd.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+354)\n"
-      "  #16 pc 0039399a  libartd.so "
-      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
-      "const&, art::ShadowFrame*)+234)\n"
-      "  #17 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
-      "  #18 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #19 pc 0000fd3b  anonymous:ee74c000 (int Main.compare(java.lang.Object, "
-      "java.lang.Object)+107)\n"
-      "  #20 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
-      "  #21 pc 00146ab5  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+885)\n"
-      "  #22 pc 0039cf0d  libartd.so "
-      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
-      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
-      "  #23 pc 00392552  libartd.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+354)\n"
-      "  #24 pc 0039399a  libartd.so "
-      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
-      "const&, art::ShadowFrame*)+234)\n"
-      "  #25 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
-      "  #26 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #27 pc 0000fbdb  anonymous:ee74c000 (int "
-      "java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
-      "java.util.Comparator)+331)\n"
-      "  #28 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)\n"
-      "  #29 pc 00146acb  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+907)\n"
-      "  #30 pc 0039cf0d  libartd.so "
-      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
-      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
-      "  #31 pc 00392552  libartd.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+354)\n"
-      "  #32 pc 0039399a  libartd.so "
-      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
-      "const&, art::ShadowFrame*)+234)\n"
-      "  #33 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
-      "  #34 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #35 pc 0000f624  anonymous:ee74c000 (boolean Main.foo()+164)\n"
-      "  #36 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
-      "  #37 pc 00146ab5  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+885)\n"
-      "  #38 pc 0039cf0d  libartd.so "
-      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
-      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
-      "  #39 pc 00392552  libartd.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+354)\n"
-      "  #40 pc 0039399a  libartd.so "
-      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
-      "const&, art::ShadowFrame*)+234)\n"
-      "  #41 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
-      "  #42 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #43 pc 0000eedb  anonymous:ee74c000 (void Main.runPrimary()+59)\n"
-      "  #44 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
-      "  #45 pc 00146ab5  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+885)\n"
-      "  #46 pc 0039cf0d  libartd.so "
-      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
-      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
-      "  #47 pc 00392552  libartd.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+354)\n"
-      "  #48 pc 0039399a  libartd.so "
-      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
-      "const&, art::ShadowFrame*)+234)\n"
-      "  #49 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
-      "  #50 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #51 pc 0000ac21  anonymous:ee74c000 (void Main.main(java.lang.String[])+97)\n"
-      "  #52 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)\n"
-      "  #53 pc 00146acb  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+907)\n"
-      "  #54 pc 0039cf0d  libartd.so "
-      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
-      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
-      "  #55 pc 00392552  libartd.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+354)\n"
-      "  #56 pc 0039399a  libartd.so "
-      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
-      "const&, art::ShadowFrame*)+234)\n"
-      "  #57 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
-      "  #58 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #59 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)\n"
-      "  #60 pc 00146acb  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+907)\n"
-      "  #61 pc 005aac95  libartd.so "
-      "(art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, "
-      "art::ArgArray*, art::JValue*, char const*)+85)\n"
-      "  #62 pc 005aab5a  libartd.so "
-      "(art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, "
-      "_jmethodID*, char*)+362)\n"
-      "  #63 pc 0048a3dd  libartd.so "
-      "(art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+125)\n"
-      "  #64 pc 0018448c  libartd.so "
-      "(art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, char*, "
-      "art::Primitive::Type, art::InvokeType)+1964)\n"
-      "  #65 pc 0017cf06  libartd.so "
-      "(art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+70)\n"
-      "  #66 pc 00001d8c  dalvikvm32 "
-      "(_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+60)\n"
-      "  #67 pc 00001a80  dalvikvm32 (main+1312)\n"
-      "  #68 pc 00018275  libc.so\n",
-      frame_info);
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0xeb89bfb8U, unwinder.frames()[0].pc);
   EXPECT_EQ(0xffeb5280U, unwinder.frames()[0].sp);
   EXPECT_EQ(0xeb89af00U, unwinder.frames()[1].pc);
@@ -607,174 +360,26 @@
 }
 
 TEST_F(UnwindOfflineTest, jit_debug_arm) {
-  ASSERT_NO_FATAL_FAILURE(Init("jit_debug_arm/", ARCH_ARM));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "jit_debug_arm/",
+                            .arch = ARCH_ARM,
+                            .memory_flag = ProcessMemoryFlag::kIncludeJitMemory},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  MemoryOfflineParts* memory = new MemoryOfflineParts;
-  AddMemory(dir_ + "descriptor.data", memory);
-  AddMemory(dir_ + "descriptor1.data", memory);
-  AddMemory(dir_ + "stack.data", memory);
-  for (size_t i = 0; i < 7; i++) {
-    AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
-    AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
-  }
-  process_memory_.reset(memory);
-
-  std::unique_ptr<JitDebug> jit_debug = CreateJitDebug(regs_->Arch(), process_memory_);
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
-  unwinder.SetJitDebug(jit_debug.get());
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
+  unwinder.SetJitDebug(offline_utils_.GetJitDebug());
   unwinder.Unwind();
 
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
   std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 00018a5e  libarttestd.so (Java_Main_unwindInProcess+866)\n"
-      "  #01 pc 0000212d  137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
-      "boolean)+92)\n"
-      "  #02 pc 00011cb1  anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
-      "  #03 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
-      "  #04 pc 00467129  libartd.so (art_quick_invoke_stub+228)\n"
-      "  #05 pc 000bf7a9  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+864)\n"
-      "  #06 pc 00247833  libartd.so "
-      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
-      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
-      "  #07 pc 0022e935  libartd.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+244)\n"
-      "  #08 pc 0022f71d  libartd.so "
-      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
-      "const&, art::ShadowFrame*)+128)\n"
-      "  #09 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
-      "  #10 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
-      "  #11 pc 00011c31  anonymous:e2796000 (int Main.compare(Main, Main)+64)\n"
-      "  #12 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
-      "  #13 pc 00467129  libartd.so (art_quick_invoke_stub+228)\n"
-      "  #14 pc 000bf7a9  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+864)\n"
-      "  #15 pc 00247833  libartd.so "
-      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
-      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
-      "  #16 pc 0022e935  libartd.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+244)\n"
-      "  #17 pc 0022f71d  libartd.so "
-      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
-      "const&, art::ShadowFrame*)+128)\n"
-      "  #18 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
-      "  #19 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
-      "  #20 pc 00011b77  anonymous:e2796000 (int Main.compare(java.lang.Object, "
-      "java.lang.Object)+118)\n"
-      "  #21 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
-      "  #22 pc 00467129  libartd.so (art_quick_invoke_stub+228)\n"
-      "  #23 pc 000bf7a9  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+864)\n"
-      "  #24 pc 00247833  libartd.so "
-      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
-      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
-      "  #25 pc 0022e935  libartd.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+244)\n"
-      "  #26 pc 0022f71d  libartd.so "
-      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
-      "const&, art::ShadowFrame*)+128)\n"
-      "  #27 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
-      "  #28 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
-      "  #29 pc 00011a29  anonymous:e2796000 (int "
-      "java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
-      "java.util.Comparator)+304)\n"
-      "  #30 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
-      "  #31 pc 0046722f  libartd.so (art_quick_invoke_static_stub+226)\n"
-      "  #32 pc 000bf7bb  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+882)\n"
-      "  #33 pc 00247833  libartd.so "
-      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
-      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
-      "  #34 pc 0022e935  libartd.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+244)\n"
-      "  #35 pc 0022f71d  libartd.so "
-      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
-      "const&, art::ShadowFrame*)+128)\n"
-      "  #36 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
-      "  #37 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
-      "  #38 pc 0001139b  anonymous:e2796000 (boolean Main.foo()+178)\n"
-      "  #39 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
-      "  #40 pc 00467129  libartd.so (art_quick_invoke_stub+228)\n"
-      "  #41 pc 000bf7a9  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+864)\n"
-      "  #42 pc 00247833  libartd.so "
-      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
-      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
-      "  #43 pc 0022e935  libartd.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+244)\n"
-      "  #44 pc 0022f71d  libartd.so "
-      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
-      "const&, art::ShadowFrame*)+128)\n"
-      "  #45 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
-      "  #46 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
-      "  #47 pc 00010aa7  anonymous:e2796000 (void Main.runPrimary()+70)\n"
-      "  #48 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
-      "  #49 pc 00467129  libartd.so (art_quick_invoke_stub+228)\n"
-      "  #50 pc 000bf7a9  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+864)\n"
-      "  #51 pc 00247833  libartd.so "
-      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
-      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
-      "  #52 pc 0022e935  libartd.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+244)\n"
-      "  #53 pc 0022f71d  libartd.so "
-      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
-      "const&, art::ShadowFrame*)+128)\n"
-      "  #54 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
-      "  #55 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
-      "  #56 pc 0000ba99  anonymous:e2796000 (void Main.main(java.lang.String[])+144)\n"
-      "  #57 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
-      "  #58 pc 0046722f  libartd.so (art_quick_invoke_static_stub+226)\n"
-      "  #59 pc 000bf7bb  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+882)\n"
-      "  #60 pc 00247833  libartd.so "
-      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
-      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
-      "  #61 pc 0022e935  libartd.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+244)\n"
-      "  #62 pc 0022f71d  libartd.so "
-      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
-      "const&, art::ShadowFrame*)+128)\n"
-      "  #63 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
-      "  #64 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
-      "  #65 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
-      "  #66 pc 0046722f  libartd.so (art_quick_invoke_static_stub+226)\n"
-      "  #67 pc 000bf7bb  libartd.so "
-      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
-      "const*)+882)\n"
-      "  #68 pc 003b292d  libartd.so "
-      "(art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, "
-      "art::ArgArray*, art::JValue*, char const*)+52)\n"
-      "  #69 pc 003b26c3  libartd.so "
-      "(art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, "
-      "_jmethodID*, std::__va_list)+210)\n"
-      "  #70 pc 00308411  libartd.so "
-      "(art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+76)\n"
-      "  #71 pc 000e6a9f  libartd.so "
-      "(art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, "
-      "std::__va_list, art::Primitive::Type, art::InvokeType)+1486)\n"
-      "  #72 pc 000e19b9  libartd.so "
-      "(art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+40)\n"
-      "  #73 pc 0000159f  dalvikvm32 "
-      "(_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+30)\n"
-      "  #74 pc 00001349  dalvikvm32 (main+896)\n"
-      "  #75 pc 000850c9  libc.so\n",
-      frame_info);
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0xdfe66a5eU, unwinder.frames()[0].pc);
   EXPECT_EQ(0xff85d180U, unwinder.frames()[0].sp);
   EXPECT_EQ(0xe044712dU, unwinder.frames()[1].pc);
@@ -930,12 +535,17 @@
 }
 
 struct LeakType {
-  LeakType(Maps* maps, Regs* regs, std::shared_ptr<Memory>& process_memory)
-      : maps(maps), regs(regs), process_memory(process_memory) {}
+  LeakType(Maps* maps, Regs* regs, std::shared_ptr<Memory>& process_memory,
+           size_t expected_num_frames)
+      : maps(maps),
+        regs(regs),
+        process_memory(process_memory),
+        expected_num_frames(expected_num_frames) {}
 
   Maps* maps;
   Regs* regs;
   std::shared_ptr<Memory>& process_memory;
+  size_t expected_num_frames;
 };
 
 static void OfflineUnwind(void* data) {
@@ -947,23 +557,23 @@
   Unwinder unwinder(128, leak_data->maps, regs_copy.get(), leak_data->process_memory);
   unwinder.SetJitDebug(jit_debug.get());
   unwinder.Unwind();
-  ASSERT_EQ(76U, unwinder.NumFrames());
+  ASSERT_EQ(leak_data->expected_num_frames, unwinder.NumFrames());
 }
 
 TEST_F(UnwindOfflineTest, unwind_offline_check_for_leaks) {
-  ASSERT_NO_FATAL_FAILURE(Init("jit_debug_arm/", ARCH_ARM));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "jit_debug_arm/",
+                            .arch = ARCH_ARM,
+                            .memory_flag = ProcessMemoryFlag::kIncludeJitMemory},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  MemoryOfflineParts* memory = new MemoryOfflineParts;
-  AddMemory(dir_ + "descriptor.data", memory);
-  AddMemory(dir_ + "descriptor1.data", memory);
-  AddMemory(dir_ + "stack.data", memory);
-  for (size_t i = 0; i < 7; i++) {
-    AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
-    AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
-  }
-  process_memory_.reset(memory);
+  std::shared_ptr<Memory> process_memory = offline_utils_.GetProcessMemory();
 
-  LeakType data(maps_.get(), regs_.get(), process_memory_);
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  LeakType data(offline_utils_.GetMaps(), offline_utils_.GetRegs(), process_memory,
+                expected_num_frames);
   TestCheckForLeaks(OfflineUnwind, &data);
 }
 
@@ -971,20 +581,23 @@
 // fallback to iterating over the cies/fdes and ignore the eh_frame_hdr.
 // No .gnu_debugdata section in the elf file, so no symbols.
 TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
-  ASSERT_NO_FATAL_FAILURE(Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "bad_eh_frame_hdr_arm64/", .arch = ARCH_ARM64},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
   std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 0000000000000550  waiter64\n"
-      "  #01 pc 0000000000000568  waiter64\n"
-      "  #02 pc 000000000000057c  waiter64\n"
-      "  #03 pc 0000000000000590  waiter64\n"
-      "  #04 pc 00000000000a8e98  libc.so (__libc_init+88)\n",
-      frame_info);
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0x60a9fdf550U, unwinder.frames()[0].pc);
   EXPECT_EQ(0x7fdd141990U, unwinder.frames()[0].sp);
   EXPECT_EQ(0x60a9fdf568U, unwinder.frames()[1].pc);
@@ -1000,20 +613,23 @@
 // The elf has bad eh_frame unwind information for the pcs. If eh_frame
 // is used first, the unwind will not match the expected output.
 TEST_F(UnwindOfflineTest, debug_frame_first_x86) {
-  ASSERT_NO_FATAL_FAILURE(Init("debug_frame_first_x86/", ARCH_X86));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "debug_frame_first_x86/", .arch = ARCH_X86},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
   std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 00000685  waiter (call_level3+53)\n"
-      "  #01 pc 000006b7  waiter (call_level2+23)\n"
-      "  #02 pc 000006d7  waiter (call_level1+23)\n"
-      "  #03 pc 000006f7  waiter (main+23)\n"
-      "  #04 pc 00018275  libc.so\n",
-      frame_info);
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0x56598685U, unwinder.frames()[0].pc);
   EXPECT_EQ(0xffcf9e38U, unwinder.frames()[0].sp);
   EXPECT_EQ(0x565986b7U, unwinder.frames()[1].pc);
@@ -1028,20 +644,23 @@
 
 // Make sure that a pc that is at the beginning of an fde unwinds correctly.
 TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) {
-  ASSERT_NO_FATAL_FAILURE(Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "eh_frame_hdr_begin_x86_64/", .arch = ARCH_X86_64},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
   std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 0000000000000a80  unwind_test64 (calling3)\n"
-      "  #01 pc 0000000000000dd9  unwind_test64 (calling2+633)\n"
-      "  #02 pc 000000000000121e  unwind_test64 (calling1+638)\n"
-      "  #03 pc 00000000000013ed  unwind_test64 (main+13)\n"
-      "  #04 pc 00000000000202b0  libc.so\n",
-      frame_info);
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0x561550b17a80U, unwinder.frames()[0].pc);
   EXPECT_EQ(0x7ffcc8596ce8U, unwinder.frames()[0].sp);
   EXPECT_EQ(0x561550b17dd9U, unwinder.frames()[1].pc);
@@ -1055,71 +674,26 @@
 }
 
 TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) {
-  ASSERT_NO_FATAL_FAILURE(Init("art_quick_osr_stub_arm/", ARCH_ARM));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "art_quick_osr_stub_arm/",
+                            .arch = ARCH_ARM,
+                            .memory_flag = ProcessMemoryFlag::kIncludeJitMemory},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  MemoryOfflineParts* memory = new MemoryOfflineParts;
-  AddMemory(dir_ + "descriptor.data", memory);
-  AddMemory(dir_ + "stack.data", memory);
-  for (size_t i = 0; i < 2; i++) {
-    AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
-    AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
-  }
-  process_memory_.reset(memory);
-
-  std::unique_ptr<JitDebug> jit_debug = CreateJitDebug(regs_->Arch(), process_memory_);
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
-  unwinder.SetJitDebug(jit_debug.get());
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
+  unwinder.SetJitDebug(offline_utils_.GetJitDebug());
   unwinder.Unwind();
 
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
   std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(25U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 0000c788  <anonymous:d0250000> "
-      "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity.access$000)\n"
-      "  #01 pc 0000cdd5  <anonymous:d0250000> "
-      "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)\n"
-      "  #02 pc 004135bb  libart.so (art_quick_osr_stub+42)\n"
-      "  #03 pc 002657a5  libart.so "
-      "(art::jit::Jit::MaybeDoOnStackReplacement(art::Thread*, art::ArtMethod*, unsigned int, int, "
-      "art::JValue*)+876)\n"
-      "  #04 pc 004021a7  libart.so (MterpMaybeDoOnStackReplacement+86)\n"
-      "  #05 pc 00412474  libart.so (ExecuteMterpImpl+66164)\n"
-      "  #06 pc cd8365b0  <unknown>\n"  // symbol in dex file
-      "  #07 pc 001d7f1b  libart.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+374)\n"
-      "  #08 pc 001dc593  libart.so "
-      "(art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, "
-      "art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+154)\n"
-      "  #09 pc 001f4d01  libart.so "
-      "(bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, "
-      "art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+732)\n"
-      "  #10 pc 003fe427  libart.so (MterpInvokeInterface+1354)\n"
-      "  #11 pc 00405b94  libart.so (ExecuteMterpImpl+14740)\n"
-      "  #12 pc 7004873e  <unknown>\n"  // symbol in dex file
-      "  #13 pc 001d7f1b  libart.so "
-      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
-      "art::ShadowFrame&, art::JValue, bool)+374)\n"
-      "  #14 pc 001dc4d5  libart.so "
-      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
-      "const&, art::ShadowFrame*)+92)\n"
-      "  #15 pc 003f25ab  libart.so (artQuickToInterpreterBridge+970)\n"
-      "  #16 pc 00417aff  libart.so (art_quick_to_interpreter_bridge+30)\n"
-      "  #17 pc 00413575  libart.so (art_quick_invoke_stub_internal+68)\n"
-      "  #18 pc 00418531  libart.so (art_quick_invoke_stub+236)\n"
-      "  #19 pc 000b468d  libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned "
-      "int, art::JValue*, char const*)+136)\n"
-      "  #20 pc 00362f49  libart.so "
-      "(art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable "
-      "const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char "
-      "const*)+52)\n"
-      "  #21 pc 00363cd9  libart.so "
-      "(art::InvokeVirtualOrInterfaceWithJValues(art::ScopedObjectAccessAlreadyRunnable const&, "
-      "_jobject*, _jmethodID*, jvalue*)+332)\n"
-      "  #22 pc 003851dd  libart.so (art::Thread::CreateCallback(void*)+868)\n"
-      "  #23 pc 00062925  libc.so (__pthread_start(void*)+22)\n"
-      "  #24 pc 0001de39  libc.so (__start_thread+24)\n",
-      frame_info);
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0xd025c788U, unwinder.frames()[0].pc);
   EXPECT_EQ(0xcd4ff140U, unwinder.frames()[0].sp);
   EXPECT_EQ(0xd025cdd5U, unwinder.frames()[1].pc);
@@ -1173,31 +747,28 @@
 }
 
 TEST_F(UnwindOfflineTest, jit_map_arm) {
-  ASSERT_NO_FATAL_FAILURE(Init("jit_map_arm/", ARCH_ARM));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "jit_map_arm/", .arch = ARCH_ARM}, &error_msg))
+    FAIL() << error_msg;
 
-  maps_->Add(0xd025c788, 0xd025c9f0, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
-             "jit_map0.so", 0);
-  maps_->Add(0xd025cd98, 0xd025cff4, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
-             "jit_map1.so", 0);
-  maps_->Sort();
+  Maps* maps = offline_utils_.GetMaps();
+  maps->Add(0xd025c788, 0xd025c9f0, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
+            "jit_map0.so", 0);
+  maps->Add(0xd025cd98, 0xd025cff4, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
+            "jit_map1.so", 0);
+  maps->Sort();
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, maps, offline_utils_.GetRegs(), offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
   std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(6U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 00000000  jit_map0.so "
-      "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity.access$000)\n"
-      "  #01 pc 0000003d  jit_map1.so "
-      "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)\n"
-      "  #02 pc 004135bb  libart.so (art_quick_osr_stub+42)\n"
-
-      "  #03 pc 003851dd  libart.so (art::Thread::CreateCallback(void*)+868)\n"
-      "  #04 pc 00062925  libc.so (__pthread_start(void*)+22)\n"
-      "  #05 pc 0001de39  libc.so (__start_thread+24)\n",
-      frame_info);
-
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0xd025c788U, unwinder.frames()[0].pc);
   EXPECT_EQ(0xcd4ff140U, unwinder.frames()[0].sp);
   EXPECT_EQ(0xd025cdd5U, unwinder.frames()[1].pc);
@@ -1213,39 +784,22 @@
 }
 
 TEST_F(UnwindOfflineTest, offset_arm) {
-  ASSERT_NO_FATAL_FAILURE(Init("offset_arm/", ARCH_ARM));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "offset_arm/", .arch = ARCH_ARM}, &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
-  std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(19U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 0032bfa0  libunwindstack_test (SignalInnerFunction+40)\n"
-      "  #01 pc 0032bfeb  libunwindstack_test (SignalMiddleFunction+2)\n"
-      "  #02 pc 0032bff3  libunwindstack_test (SignalOuterFunction+2)\n"
-      "  #03 pc 0032fed3  libunwindstack_test "
-      "(unwindstack::SignalCallerHandler(int, siginfo*, void*)+26)\n"
-      "  #04 pc 0002652c  libc.so (__restore)\n"
-      "  #05 pc 00000000  <unknown>\n"
-      "  #06 pc 0032c2d9  libunwindstack_test (InnerFunction+736)\n"
-      "  #07 pc 0032cc4f  libunwindstack_test (MiddleFunction+42)\n"
-      "  #08 pc 0032cc81  libunwindstack_test (OuterFunction+42)\n"
-      "  #09 pc 0032e547  libunwindstack_test "
-      "(unwindstack::RemoteThroughSignal(int, unsigned int)+270)\n"
-      "  #10 pc 0032ed99  libunwindstack_test "
-      "(unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+16)\n"
-      "  #11 pc 00354453  libunwindstack_test (testing::Test::Run()+154)\n"
-      "  #12 pc 00354de7  libunwindstack_test (testing::TestInfo::Run()+194)\n"
-      "  #13 pc 00355105  libunwindstack_test (testing::TestCase::Run()+180)\n"
-      "  #14 pc 0035a215  libunwindstack_test "
-      "(testing::internal::UnitTestImpl::RunAllTests()+664)\n"
-      "  #15 pc 00359f4f  libunwindstack_test (testing::UnitTest::Run()+110)\n"
-      "  #16 pc 0034d3db  libunwindstack_test (main+38)\n"
-      "  #17 pc 00092c0d  libc.so (__libc_init+48)\n"
-      "  #18 pc 0004202f  libunwindstack_test (_start_main+38)\n",
-      frame_info);
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
 
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0x2e55fa0U, unwinder.frames()[0].pc);
   EXPECT_EQ(0xf43d2cccU, unwinder.frames()[0].sp);
   EXPECT_EQ(0x2e55febU, unwinder.frames()[1].pc);
@@ -1289,24 +843,23 @@
 // Test using a non-zero load bias library that has the fde entries
 // encoded as 0xb, which is not set as pc relative.
 TEST_F(UnwindOfflineTest, debug_frame_load_bias_arm) {
-  ASSERT_NO_FATAL_FAILURE(Init("debug_frame_load_bias_arm/", ARCH_ARM));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "debug_frame_load_bias_arm/", .arch = ARCH_ARM},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
-  std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(8U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 0005138c  libc.so (__ioctl+8)\n"
-      "  #01 pc 0002140f  libc.so (ioctl+30)\n"
-      "  #02 pc 00039535  libbinder.so (android::IPCThreadState::talkWithDriver(bool)+204)\n"
-      "  #03 pc 00039633  libbinder.so (android::IPCThreadState::getAndExecuteCommand()+10)\n"
-      "  #04 pc 00039b57  libbinder.so (android::IPCThreadState::joinThreadPool(bool)+38)\n"
-      "  #05 pc 00000c21  mediaserver (main+104)\n"
-      "  #06 pc 00084b89  libc.so (__libc_init+48)\n"
-      "  #07 pc 00000b77  mediaserver (_start_main+38)\n",
-      frame_info);
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
 
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0xf0be238cU, unwinder.frames()[0].pc);
   EXPECT_EQ(0xffd4a638U, unwinder.frames()[0].sp);
   EXPECT_EQ(0xf0bb240fU, unwinder.frames()[1].pc);
@@ -1326,25 +879,23 @@
 }
 
 TEST_F(UnwindOfflineTest, shared_lib_in_apk_arm64) {
-  ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_arm64/", ARCH_ARM64));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "shared_lib_in_apk_arm64/", .arch = ARCH_ARM64},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
-  std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 000000000014ccbc  linker64 (__dl_syscall+28)\n"
-      "  #01 pc 000000000005426c  linker64 "
-      "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
-      "  #02 pc 00000000000008c0  vdso.so (__kernel_rt_sigreturn)\n"
-      "  #03 pc 00000000000846f4  libc.so (abort+172)\n"
-      "  #04 pc 0000000000084ad4  libc.so (__assert2+36)\n"
-      "  #05 pc 000000000003d5b4  ANGLEPrebuilt.apk!libfeature_support_angle.so (offset 0x4000) "
-      "(ANGLEGetUtilityAPI+56)\n"
-      "  #06 pc 000000000007fe68  libc.so (__libc_init)\n",
-      frame_info);
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
 
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
   EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
   EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
@@ -1363,27 +914,31 @@
 }
 
 TEST_F(UnwindOfflineTest, shared_lib_in_apk_memory_only_arm64) {
-  ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_memory_only_arm64/", ARCH_ARM64));
+  std::string error_msg;
+  if (!offline_utils_.Init(
+          {.offline_files_dir = "shared_lib_in_apk_memory_only_arm64/", .arch = ARCH_ARM64},
+          &error_msg))
+    FAIL() << error_msg;
   // Add the memory that represents the shared library.
-  MemoryOfflineParts* memory = reinterpret_cast<MemoryOfflineParts*>(process_memory_.get());
-  AddMemory(dir_ + "lib_mem.data", memory);
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  std::shared_ptr<Memory> process_memory = offline_utils_.GetProcessMemory();
+  MemoryOfflineParts* memory = reinterpret_cast<MemoryOfflineParts*>(process_memory.get());
+  const std::string* offline_files_path = offline_utils_.GetOfflineFilesPath();
+  if (offline_files_path == nullptr) FAIL() << "GetOfflineFilesPath() failed.";
+
+  if (!AddMemory(*offline_files_path + "lib_mem.data", memory, &error_msg)) FAIL() << error_msg;
+
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(), process_memory);
   unwinder.Unwind();
 
-  std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 000000000014ccbc  linker64 (__dl_syscall+28)\n"
-      "  #01 pc 000000000005426c  linker64 "
-      "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
-      "  #02 pc 00000000000008c0  vdso.so (__kernel_rt_sigreturn)\n"
-      "  #03 pc 00000000000846f4  libc.so (abort+172)\n"
-      "  #04 pc 0000000000084ad4  libc.so (__assert2+36)\n"
-      "  #05 pc 000000000003d5b4  ANGLEPrebuilt.apk (offset 0x21d5000)\n"
-      "  #06 pc 000000000007fe68  libc.so (__libc_init)\n",
-      frame_info);
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
 
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
   EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
   EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
@@ -1402,29 +957,24 @@
 }
 
 TEST_F(UnwindOfflineTest, shared_lib_in_apk_single_map_arm64) {
-  ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_single_map_arm64/", ARCH_ARM64));
+  std::string error_msg;
+  if (!offline_utils_.Init(
+          {.offline_files_dir = "shared_lib_in_apk_single_map_arm64/", .arch = ARCH_ARM64},
+          &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
-  std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(13U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 00000000000814bc  libc.so (syscall+28)\n"
-      "  #01 pc 00000000008cdf5c  test.apk (offset 0x5000)\n"
-      "  #02 pc 00000000008cde9c  test.apk (offset 0x5000)\n"
-      "  #03 pc 00000000008cdd70  test.apk (offset 0x5000)\n"
-      "  #04 pc 00000000008ce408  test.apk (offset 0x5000)\n"
-      "  #05 pc 00000000008ce8d8  test.apk (offset 0x5000)\n"
-      "  #06 pc 00000000008ce814  test.apk (offset 0x5000)\n"
-      "  #07 pc 00000000008bcf60  test.apk (offset 0x5000)\n"
-      "  #08 pc 0000000000133024  test.apk (offset 0x5000)\n"
-      "  #09 pc 0000000000134ad0  test.apk (offset 0x5000)\n"
-      "  #10 pc 0000000000134b64  test.apk (offset 0x5000)\n"
-      "  #11 pc 00000000000e406c  libc.so (__pthread_start(void*)+36)\n"
-      "  #12 pc 0000000000085e18  libc.so (__start_thread+64)\n",
-      frame_info);
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
 
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0x7cbe0b14bcULL, unwinder.frames()[0].pc);
   EXPECT_EQ(0x7be4f077d0ULL, unwinder.frames()[0].sp);
   EXPECT_EQ(0x7be6715f5cULL, unwinder.frames()[1].pc);
@@ -1454,26 +1004,42 @@
 }
 
 TEST_F(UnwindOfflineTest, invalid_elf_offset_arm) {
-  ASSERT_NO_FATAL_FAILURE(Init("invalid_elf_offset_arm/", ARCH_ARM, false));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "invalid_elf_offset_arm/",
+                            .arch = ARCH_ARM,
+                            .memory_flag = ProcessMemoryFlag::kNoMemory},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+
   std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(1U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ("  #00 pc 00aa7508  invalid.apk (offset 0x12e4000)\n", frame_info);
   EXPECT_EQ(0xc898f508, unwinder.frames()[0].pc);
   EXPECT_EQ(0xc2044218, unwinder.frames()[0].sp);
 }
 
 TEST_F(UnwindOfflineTest, load_bias_ro_rx_x86_64) {
-  ASSERT_NO_FATAL_FAILURE(Init("load_bias_ro_rx_x86_64/", ARCH_X86_64));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "load_bias_ro_rx_x86_64/", .arch = ARCH_X86_64},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+
   std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(17U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ(
       "  #00 pc 00000000000e9dd4  libc.so (__write+20)\n"
       "  #01 pc 000000000007ab9c  libc.so (_IO_file_write+44)\n"
@@ -1544,28 +1110,24 @@
 }
 
 TEST_F(UnwindOfflineTest, load_bias_different_section_bias_arm64) {
-  ASSERT_NO_FATAL_FAILURE(Init("load_bias_different_section_bias_arm64/", ARCH_ARM64));
+  std::string error_msg;
+  if (!offline_utils_.Init(
+          {.offline_files_dir = "load_bias_different_section_bias_arm64/", .arch = ARCH_ARM64},
+          &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
-  std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(12U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 00000000000d59bc  linker64 (__dl_syscall+28)\n"
-      "  #01 pc 00000000000554e8  linker64 (__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1148)\n"
-      "  #02 pc 00000000000008c0  vdso (__kernel_rt_sigreturn)\n"
-      "  #03 pc 000000000007f3e8  libc.so (abort+168)\n"
-      "  #04 pc 00000000000459fc  test (std::__ndk1::__throw_bad_cast()+4)\n"
-      "  #05 pc 0000000000056d80  test (testing::Test::Run()+88)\n"
-      "  #06 pc 000000000005724c  test (testing::TestInfo::Run()+112)\n"
-      "  #07 pc 0000000000057558  test (testing::TestSuite::Run()+116)\n"
-      "  #08 pc 000000000005bffc  test (testing::internal::UnitTestImpl::RunAllTests()+464)\n"
-      "  #09 pc 000000000005bd9c  test (testing::UnitTest::Run()+116)\n"
-      "  #10 pc 00000000000464e4  test (main+144)\n"
-      "  #11 pc 000000000007aa34  libc.so (__libc_init+108)\n",
-      frame_info);
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
 
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0x7112cb99bcULL, unwinder.frames()[0].pc);
   EXPECT_EQ(0x7112bdbbf0ULL, unwinder.frames()[0].sp);
   EXPECT_EQ(0x7112c394e8ULL, unwinder.frames()[1].pc);
@@ -1593,27 +1155,23 @@
 }
 
 TEST_F(UnwindOfflineTest, eh_frame_bias_x86) {
-  ASSERT_NO_FATAL_FAILURE(Init("eh_frame_bias_x86/", ARCH_X86));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "eh_frame_bias_x86/", .arch = ARCH_X86},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
-  std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(11U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc ffffe430  vdso.so (__kernel_vsyscall+16)\n"
-      "  #01 pc 00082a4b  libc.so (__epoll_pwait+43)\n"
-      "  #02 pc 000303a3  libc.so (epoll_pwait+115)\n"
-      "  #03 pc 000303ed  libc.so (epoll_wait+45)\n"
-      "  #04 pc 00010ea2  tombstoned (epoll_dispatch+226)\n"
-      "  #05 pc 0000c5e7  tombstoned (event_base_loop+1095)\n"
-      "  #06 pc 0000c193  tombstoned (event_base_dispatch+35)\n"
-      "  #07 pc 00005c77  tombstoned (main+884)\n"
-      "  #08 pc 00015f66  libc.so (__libc_init+102)\n"
-      "  #09 pc 0000360e  tombstoned (_start+98)\n"
-      "  #10 pc 00000001  <unknown>\n",
-      frame_info);
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
 
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0xffffe430ULL, unwinder.frames()[0].pc);
   EXPECT_EQ(0xfffe1a30ULL, unwinder.frames()[0].sp);
   EXPECT_EQ(0xeb585a4bULL, unwinder.frames()[1].pc);
@@ -1639,37 +1197,23 @@
 }
 
 TEST_F(UnwindOfflineTest, signal_load_bias_arm) {
-  ASSERT_NO_FATAL_FAILURE(Init("signal_load_bias_arm/", ARCH_ARM));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "signal_load_bias_arm/", .arch = ARCH_ARM},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
-  std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(17U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 0029ef9e  libunwindstack_unit_test (SignalInnerFunction+10)\n"
-      "  #01 pc 0029efa7  libunwindstack_unit_test (SignalMiddleFunction+2)\n"
-      "  #02 pc 0029efaf  libunwindstack_unit_test (SignalOuterFunction+2)\n"
-      "  #03 pc 002a280b  libunwindstack_unit_test (unwindstack::SignalCallerHandler(int, "
-      "siginfo*, void*)+10)\n"
-      "  #04 pc 00058bd4  libc.so (__restore)\n"
-      "  #05 pc 0029f01e  libunwindstack_unit_test (InnerFunction+106)\n"
-      "  #06 pc 0029f633  libunwindstack_unit_test (MiddleFunction+16)\n"
-      "  #07 pc 0029f64b  libunwindstack_unit_test (OuterFunction+16)\n"
-      "  #08 pc 002a1711  libunwindstack_unit_test (unwindstack::RemoteThroughSignal(int, unsigned "
-      "int)+260)\n"
-      "  #09 pc 002a1603  libunwindstack_unit_test "
-      "(unwindstack::UnwindTest_remote_through_signal_Test::TestBody()+10)\n"
-      "  #10 pc 002c8fe3  libunwindstack_unit_test (testing::Test::Run()+130)\n"
-      "  #11 pc 002c9b25  libunwindstack_unit_test (testing::TestInfo::Run()+184)\n"
-      "  #12 pc 002c9e27  libunwindstack_unit_test (testing::TestSuite::Run()+202)\n"
-      "  #13 pc 002d193d  libunwindstack_unit_test "
-      "(testing::internal::UnitTestImpl::RunAllTests()+660)\n"
-      "  #14 pc 002d160b  libunwindstack_unit_test (testing::UnitTest::Run()+134)\n"
-      "  #15 pc 002de035  libunwindstack_unit_test (IsolateMain+680)\n"
-      "  #16 pc 00058155  libc.so (__libc_init+68)\n",
-      frame_info);
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
 
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0xb6955f9eULL, unwinder.frames()[0].pc);
   EXPECT_EQ(0xf2790ce8ULL, unwinder.frames()[0].sp);
   EXPECT_EQ(0xb6955fa7ULL, unwinder.frames()[1].pc);
@@ -1707,25 +1251,22 @@
 }
 
 TEST_F(UnwindOfflineTest, empty_arm64) {
-  ASSERT_NO_FATAL_FAILURE(Init("empty_arm64/", ARCH_ARM64));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "empty_arm64/", .arch = ARCH_ARM64}, &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
-  std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 00000000000963a4  libc.so (__ioctl+4)\n"
-      "  #01 pc 000000000005344c  libc.so (ioctl+140)\n"
-      "  #02 pc 0000000000050ce4  libbinder.so "
-      "(android::IPCThreadState::talkWithDriver(bool)+308)\n"
-      "  #03 pc 0000000000050e98  libbinder.so "
-      "(android::IPCThreadState::getAndExecuteCommand()+24)\n"
-      "  #04 pc 00000000000516ac  libbinder.so (android::IPCThreadState::joinThreadPool(bool)+60)\n"
-      "  #05 pc 00000000000443b0  netd (main+1056)\n"
-      "  #06 pc 0000000000045594  libc.so (__libc_init+108)\n",
-      frame_info);
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
 
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0x72a02203a4U, unwinder.frames()[0].pc);
   EXPECT_EQ(0x7ffb6c0b50U, unwinder.frames()[0].sp);
   EXPECT_EQ(0x72a01dd44cU, unwinder.frames()[1].pc);
@@ -1746,40 +1287,22 @@
 // that the signal handler match does not occur and it uses the
 // fde to do the unwind.
 TEST_F(UnwindOfflineTest, signal_fde_x86) {
-  ASSERT_NO_FATAL_FAILURE(Init("signal_fde_x86/", ARCH_X86));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "signal_fde_x86/", .arch = ARCH_X86}, &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
-  std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(20U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 007914d9  libunwindstack_test (SignalInnerFunction+25)\n"
-      "  #01 pc 007914fc  libunwindstack_test (SignalMiddleFunction+28)\n"
-      "  #02 pc 0079152c  libunwindstack_test (SignalOuterFunction+28)\n"
-      "  #03 pc 0079af62  libunwindstack_test (unwindstack::SignalCallerHandler(int, siginfo*, "
-      "void*)+50)\n"
-      "  #04 pc 00058fb0  libc.so (__restore)\n"
-      "  #05 pc 00000000  <unknown>\n"
-      "  #06 pc 0079161a  libunwindstack_test (InnerFunction+218)\n"
-      "  #07 pc 007923aa  libunwindstack_test (MiddleFunction+42)\n"
-      "  #08 pc 007923ea  libunwindstack_test (OuterFunction+42)\n"
-      "  #09 pc 00797444  libunwindstack_test (unwindstack::RemoteThroughSignal(int, unsigned "
-      "int)+868)\n"
-      "  #10 pc 007985b8  libunwindstack_test "
-      "(unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+56)\n"
-      "  #11 pc 00817a19  libunwindstack_test\n"
-      "  #12 pc 008178c5  libunwindstack_test (testing::Test::Run()+277)\n"
-      "  #13 pc 00818d3e  libunwindstack_test (testing::TestInfo::Run()+318)\n"
-      "  #14 pc 008198b4  libunwindstack_test (testing::TestSuite::Run()+436)\n"
-      "  #15 pc 00828cb0  libunwindstack_test "
-      "(testing::internal::UnitTestImpl::RunAllTests()+1216)\n"
-      "  #16 pc 0082870f  libunwindstack_test (testing::UnitTest::Run()+367)\n"
-      "  #17 pc 0084031e  libunwindstack_test (IsolateMain+2334)\n"
-      "  #18 pc 0083f9e9  libunwindstack_test (main+41)\n"
-      "  #19 pc 00050646  libc.so (__libc_init+118)\n",
-      frame_info);
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
 
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0x5ae0d4d9U, unwinder.frames()[0].pc);
   EXPECT_EQ(0xecb37188U, unwinder.frames()[0].sp);
   EXPECT_EQ(0x5ae0d4fcU, unwinder.frames()[1].pc);
@@ -1826,38 +1349,23 @@
 // that the signal handler match does not occur and it uses the
 // fde to do the unwind.
 TEST_F(UnwindOfflineTest, signal_fde_x86_64) {
-  ASSERT_NO_FATAL_FAILURE(Init("signal_fde_x86_64/", ARCH_X86_64));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "signal_fde_x86_64/", .arch = ARCH_X86_64},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
-  std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(18U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 000000000058415b  libunwindstack_test (SignalInnerFunction+11)\n"
-      "  #01 pc 0000000000584168  libunwindstack_test (SignalMiddleFunction+8)\n"
-      "  #02 pc 0000000000584178  libunwindstack_test (SignalOuterFunction+8)\n"
-      "  #03 pc 000000000058ac77  libunwindstack_test (unwindstack::SignalCallerHandler(int, "
-      "siginfo*, void*)+23)\n"
-      "  #04 pc 0000000000057d10  libc.so (__restore_rt)\n"
-      "  #05 pc 0000000000000000  <unknown>\n"
-      "  #06 pc 0000000000584244  libunwindstack_test (InnerFunction+196)\n"
-      "  #07 pc 0000000000584b44  libunwindstack_test (MiddleFunction+20)\n"
-      "  #08 pc 0000000000584b64  libunwindstack_test (OuterFunction+20)\n"
-      "  #09 pc 0000000000588457  libunwindstack_test (unwindstack::RemoteThroughSignal(int, "
-      "unsigned int)+583)\n"
-      "  #10 pc 0000000000588f67  libunwindstack_test "
-      "(unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+23)\n"
-      "  #11 pc 00000000005d9c38  libunwindstack_test (testing::Test::Run()+216)\n"
-      "  #12 pc 00000000005daf9a  libunwindstack_test (testing::TestInfo::Run()+266)\n"
-      "  #13 pc 00000000005dba46  libunwindstack_test (testing::TestSuite::Run()+390)\n"
-      "  #14 pc 00000000005ea4c6  libunwindstack_test "
-      "(testing::internal::UnitTestImpl::RunAllTests()+1190)\n"
-      "  #15 pc 00000000005e9f61  libunwindstack_test (testing::UnitTest::Run()+337)\n"
-      "  #16 pc 0000000000600155  libunwindstack_test (IsolateMain+2037)\n"
-      "  #17 pc 000000000004e405  libc.so (__libc_init+101)\n",
-      frame_info);
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
 
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0x5bb41271e15bU, unwinder.frames()[0].pc);
   EXPECT_EQ(0x707eb5aa8320U, unwinder.frames()[0].sp);
   EXPECT_EQ(0x5bb41271e168U, unwinder.frames()[1].pc);
@@ -1897,45 +1405,25 @@
 }
 
 TEST_F(UnwindOfflineTest, pauth_pc_arm64) {
-  ASSERT_NO_FATAL_FAILURE(Init("pauth_pc_arm64/", ARCH_ARM64));
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "pauth_pc_arm64/", .arch = ARCH_ARM64},
+                           &error_msg))
+    FAIL() << error_msg;
 
-  static_cast<RegsArm64*>(regs_.get())->SetPACMask(0x007fff8000000000ULL);
+  static_cast<RegsArm64*>(offline_utils_.GetRegs())->SetPACMask(0x007fff8000000000ULL);
 
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
   unwinder.Unwind();
 
-  std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(26U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 00000000000404a8  toybox (do_print+28)\n"
-      "  #01 pc 0000000000040270  toybox (do_find+5072)\n"
-      "  #02 pc 000000000002c640  toybox (dirtree_handle_callback+40)\n"
-      "  #03 pc 000000000002c588  toybox (dirtree_recurse+200)\n"
-      "  #04 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)\n"
-      "  #05 pc 000000000002c588  toybox (dirtree_recurse+200)\n"
-      "  #06 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)\n"
-      "  #07 pc 000000000002c588  toybox (dirtree_recurse+200)\n"
-      "  #08 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)\n"
-      "  #09 pc 000000000002c588  toybox (dirtree_recurse+200)\n"
-      "  #10 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)\n"
-      "  #11 pc 000000000002c588  toybox (dirtree_recurse+200)\n"
-      "  #12 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)\n"
-      "  #13 pc 000000000002c588  toybox (dirtree_recurse+200)\n"
-      "  #14 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)\n"
-      "  #15 pc 000000000002c588  toybox (dirtree_recurse+200)\n"
-      "  #16 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)\n"
-      "  #17 pc 000000000002c588  toybox (dirtree_recurse+200)\n"
-      "  #18 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)\n"
-      "  #19 pc 000000000002c588  toybox (dirtree_recurse+200)\n"
-      "  #20 pc 000000000002c6a8  toybox (dirtree_handle_callback+144)\n"
-      "  #21 pc 000000000003ee54  toybox (find_main+272)\n"
-      "  #22 pc 0000000000034834  toybox (toy_exec_which+88)\n"
-      "  #23 pc 00000000000342cc  toybox (toybox_main+148)\n"
-      "  #24 pc 00000000000348b4  toybox (main+120)\n"
-      "  #25 pc 00000000000499d8  libc.so "
-      "(__libc_init+112)\n",
-      frame_info);
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
 
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
   EXPECT_EQ(0x5c390884a8U, unwinder.frames()[0].pc);
   EXPECT_EQ(0x7ff3511750U, unwinder.frames()[0].sp);
   EXPECT_EQ(0x5c39088270U, unwinder.frames()[1].pc);
@@ -1990,4 +1478,185 @@
   EXPECT_EQ(0x7ff3511e70U, unwinder.frames()[25].sp);
 }
 
+TEST_F(UnwindOfflineTest, profiler_like_multi_process) {
+  ConsecutiveUnwindTest(std::vector<UnwindSampleInfo>{
+      {.offline_files_dir = "bluetooth_arm64/pc_1/", .arch = ARCH_ARM64},
+      {.offline_files_dir = "jit_debug_arm/",
+       .arch = ARCH_ARM,
+       .memory_flag = ProcessMemoryFlag::kIncludeJitMemory},
+      {.offline_files_dir = "photos_reset_arm64/", .arch = ARCH_ARM64},
+      {.offline_files_dir = "youtube_compiled_arm64/", .arch = ARCH_ARM64},
+      {.offline_files_dir = "yt_music_arm64/", .arch = ARCH_ARM64},
+      {.offline_files_dir = "maps_compiled_arm64/28656_oat_odex_jar/", .arch = ARCH_ARM64}});
+}
+
+TEST_F(UnwindOfflineTest, profiler_like_single_process_multi_thread) {
+  ConsecutiveUnwindTest(std::vector<UnwindSampleInfo>{
+      {.offline_files_dir = "maps_compiled_arm64/28656_oat_odex_jar/", .arch = ARCH_ARM64},
+      {.offline_files_dir = "maps_compiled_arm64/28613_main-thread/", .arch = ARCH_ARM64},
+      {.offline_files_dir = "maps_compiled_arm64/28644/", .arch = ARCH_ARM64},
+      {.offline_files_dir = "maps_compiled_arm64/28648/", .arch = ARCH_ARM64},
+      {.offline_files_dir = "maps_compiled_arm64/28667/", .arch = ARCH_ARM64}});
+}
+
+TEST_F(UnwindOfflineTest, profiler_like_single_thread_diverse_pcs) {
+  ConsecutiveUnwindTest(std::vector<UnwindSampleInfo>{
+      {.offline_files_dir = "bluetooth_arm64/pc_1/", .arch = ARCH_ARM64},
+      {.offline_files_dir = "bluetooth_arm64/pc_2/", .arch = ARCH_ARM64},
+      {.offline_files_dir = "bluetooth_arm64/pc_3/", .arch = ARCH_ARM64},
+      {.offline_files_dir = "bluetooth_arm64/pc_4/", .arch = ARCH_ARM64}});
+}
+
+static void VerifyApkRORX(Unwinder& unwinder) {
+  EXPECT_EQ(0x7426d2e030U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7fe740cc90U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x7426d2e08cU, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7fe740ccd0U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x7426d2e0b8U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7fe740ccf0U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x7426d2e0e4U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7fe740cd10U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x603b0c5154U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7fe740cd30U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x76b6df0b10U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0x7fe740cdb0U, unwinder.frames()[5].sp);
+}
+
+TEST_F(UnwindOfflineTest, apk_rorx_arm64) {
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "apk_rorx_arm64/", .arch = ARCH_ARM64},
+                           &error_msg))
+    FAIL() << error_msg;
+
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
+  unwinder.Unwind();
+
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
+
+  VerifyApkRORX(unwinder);
+}
+
+TEST_F(UnwindOfflineTest, apk_rorx_unreadable_arm64) {
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "apk_rorx_unreadable_arm64/", .arch = ARCH_ARM64},
+                           &error_msg))
+    FAIL() << error_msg;
+
+  // Create a process memory object that holds the apk data in memory
+  // along with the stack data.
+  MemoryOffline* stack_memory = new MemoryOffline;
+  ASSERT_TRUE(stack_memory->Init("stack.data", 0));
+
+  MemoryOffline* apk_memory = new MemoryOffline;
+  auto info1 = offline_utils_.GetMaps()->Find(0x7426d2d000);
+  ASSERT_TRUE(info1 != nullptr);
+  auto info2 = offline_utils_.GetMaps()->Find(0x7426d2e000);
+  ASSERT_TRUE(info2 != nullptr);
+  ASSERT_TRUE(
+      apk_memory->Init("fake.apk", info1->offset(), info1->start(), info2->end() - info1->start()));
+
+  std::unique_ptr<MemoryOfflineParts> parts(new MemoryOfflineParts);
+  parts->Add(stack_memory);
+  parts->Add(apk_memory);
+
+  std::shared_ptr<Memory> process_memory(parts.release());
+
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(), process_memory);
+  unwinder.Unwind();
+
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
+
+  VerifyApkRORX(unwinder);
+}
+
+static void VerifyApkRX(Unwinder& unwinder) {
+  EXPECT_EQ(0x7cb0e6266cU, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7fe563be90U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x7cb0e626c0U, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7fe563bed0U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x7cb0e626ecU, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7fe563bef0U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x7cb0e62718U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7fe563bf10U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x5e004f0154U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7fe563bf30U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x7f41124b10U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0x7fe563bfb0U, unwinder.frames()[5].sp);
+}
+
+TEST_F(UnwindOfflineTest, apk_rx_arm64) {
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "apk_rx_arm64/", .arch = ARCH_ARM64}, &error_msg))
+    FAIL() << error_msg;
+
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+                    offline_utils_.GetProcessMemory());
+  unwinder.Unwind();
+
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
+
+  VerifyApkRX(unwinder);
+}
+
+TEST_F(UnwindOfflineTest, apk_rx_unreadable_arm64) {
+  std::string error_msg;
+  if (!offline_utils_.Init({.offline_files_dir = "apk_rx_unreadable_arm64/", .arch = ARCH_ARM64},
+                           &error_msg))
+    FAIL() << error_msg;
+
+  // Create a process memory object that holds the apk data in memory
+  // along with the stack data.
+  MemoryOffline* stack_memory = new MemoryOffline;
+  ASSERT_TRUE(stack_memory->Init("stack.data", 0));
+
+  MemoryOffline* apk_memory = new MemoryOffline;
+  auto info = offline_utils_.GetMaps()->Find(0x7cb0e62000);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_TRUE(
+      apk_memory->Init("fake.apk", info->offset(), info->start(), info->end() - info->start()));
+
+  std::unique_ptr<MemoryOfflineParts> parts(new MemoryOfflineParts);
+  parts->Add(stack_memory);
+  parts->Add(apk_memory);
+
+  std::shared_ptr<Memory> process_memory(parts.release());
+
+  Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(), process_memory);
+  unwinder.Unwind();
+
+  size_t expected_num_frames;
+  if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+  std::string expected_frame_info;
+  if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(expected_frame_info, frame_info);
+
+  VerifyApkRX(unwinder);
+}
+
+}  // namespace
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index e42125d..9a5a96f 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -40,6 +40,7 @@
 #include <unwindstack/Unwinder.h>
 
 #include "MemoryRemote.h"
+#include "PidUtils.h"
 #include "TestUtils.h"
 
 namespace unwindstack {
@@ -52,15 +53,17 @@
   TEST_TYPE_REMOTE_WITH_INVALID_CALL,
 };
 
-static std::atomic_bool g_ready;
 static volatile bool g_ready_for_remote;
 static volatile bool g_signal_ready_for_remote;
-static std::atomic_bool g_finish;
+// In order to avoid the compiler not emitting the unwind entries for
+// the InnerFunction code that loops waiting for g_finish, always make
+// g_finish a volatile instead of an atomic. This issue was only ever
+// observerd on the arm architecture.
+static volatile bool g_finish;
 static std::atomic_uintptr_t g_ucontext;
 static std::atomic_int g_waiters;
 
 static void ResetGlobals() {
-  g_ready = false;
   g_ready_for_remote = false;
   g_signal_ready_for_remote = false;
   g_finish = false;
@@ -76,7 +79,7 @@
 
 static void SignalHandler(int, siginfo_t*, void* sigcontext) {
   g_ucontext = reinterpret_cast<uintptr_t>(sigcontext);
-  while (!g_finish.load()) {
+  while (!g_finish) {
   }
 }
 
@@ -109,8 +112,9 @@
 
   return std::string(
              "Unwind completed without finding all frames\n"
-             "  Looking for function: ") +
-         function_names.front() + "\n" + "Unwind data:\n" + unwind;
+             "  Unwinder error: ") +
+         unwinder->LastErrorCodeString() + "\n" +
+         "  Looking for function: " + function_names.front() + "\n" + "Unwind data:\n" + unwind;
 }
 
 static void VerifyUnwindFrames(Unwinder* unwinder,
@@ -125,6 +129,14 @@
   }
 
   ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, unwinder);
+
+  // Verify that the load bias of every map with a MapInfo is has been initialized.
+  for (auto& frame : unwinder->frames()) {
+    if (frame.map_info == nullptr) {
+      continue;
+    }
+    ASSERT_NE(UINT64_MAX, frame.map_info->GetLoadBias()) << "Frame " << frame.num << " failed";
+  }
 }
 
 static void VerifyUnwind(Unwinder* unwinder, std::vector<const char*> expected_function_names) {
@@ -150,7 +162,7 @@
   switch (test_type) {
     case TEST_TYPE_LOCAL_WAIT_FOR_FINISH: {
       g_waiters++;
-      while (!g_finish.load()) {
+      while (!g_finish) {
       }
       break;
     }
@@ -158,7 +170,6 @@
     case TEST_TYPE_REMOTE:
     case TEST_TYPE_REMOTE_WITH_INVALID_CALL: {
       g_ready_for_remote = true;
-      g_ready = true;
       if (test_type == TEST_TYPE_REMOTE_WITH_INVALID_CALL) {
         void (*crash_func)() = nullptr;
         crash_func();
@@ -226,33 +237,15 @@
   TestCheckForLeaks(LocalUnwind, &test_type);
 }
 
-void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) {
-  *completed = false;
-  // Need to sleep before attempting first ptrace. Without this, on the
-  // host it becomes impossible to attach and ptrace sets errno to EPERM.
-  usleep(1000);
-  for (size_t i = 0; i < 1000; i++) {
-    if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
-      ASSERT_TRUE(TestQuiescePid(pid))
-          << "Waiting for process to quiesce failed: " << strerror(errno);
-
-      MemoryRemote memory(pid);
-      // Read the remote value to see if we are ready.
-      bool value;
-      if (memory.ReadFully(addr, &value, sizeof(value)) && value) {
-        *completed = true;
-      }
-      if (!*completed || !leave_attached) {
-        ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
-      }
-      if (*completed) {
-        break;
-      }
-    } else {
-      ASSERT_EQ(ESRCH, errno) << "ptrace attach failed with unexpected error: " << strerror(errno);
+static bool WaitForRemote(pid_t pid, bool leave_attached, uint64_t addr) {
+  MemoryRemote memory(pid);
+  return RunWhenQuiesced(pid, leave_attached, [addr, &memory]() {
+    bool value;
+    if (memory.ReadFully(addr, &value, sizeof(value)) && value) {
+      return PID_RUN_PASS;
     }
-    usleep(5000);
-  }
+    return PID_RUN_KEEP_GOING;
+  });
 }
 
 TEST_F(UnwindTest, remote) {
@@ -264,9 +257,7 @@
   ASSERT_NE(-1, pid);
   TestScopedPidReaper reap(pid);
 
-  bool completed;
-  WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
-  ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+  ASSERT_TRUE(WaitForRemote(pid, true, reinterpret_cast<uint64_t>(&g_ready_for_remote)));
 
   RemoteMaps maps(pid);
   ASSERT_TRUE(maps.Parse());
@@ -275,8 +266,7 @@
 
   VerifyUnwind(pid, &maps, regs.get(), kFunctionOrder);
 
-  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
-      << "ptrace detach failed with unexpected error: " << strerror(errno);
+  ASSERT_TRUE(Detach(pid));
 }
 
 TEST_F(UnwindTest, unwind_from_pid_remote) {
@@ -288,9 +278,7 @@
   ASSERT_NE(-1, pid);
   TestScopedPidReaper reap(pid);
 
-  bool completed;
-  WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
-  ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+  ASSERT_TRUE(WaitForRemote(pid, true, reinterpret_cast<uint64_t>(&g_ready_for_remote)));
 
   std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
   ASSERT_TRUE(regs.get() != nullptr);
@@ -300,10 +288,7 @@
 
   VerifyUnwind(&unwinder, kFunctionOrder);
 
-  // Verify that calling the same object works again.
-
-  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
-      << "ptrace detach failed with unexpected error: " << strerror(errno);
+  ASSERT_TRUE(Detach(pid));
 }
 
 static void RemoteCheckForLeaks(void (*unwind_func)(void*)) {
@@ -315,14 +300,11 @@
   ASSERT_NE(-1, pid);
   TestScopedPidReaper reap(pid);
 
-  bool completed;
-  WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
-  ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+  ASSERT_TRUE(WaitForRemote(pid, true, reinterpret_cast<uint64_t>(&g_ready_for_remote)));
 
   TestCheckForLeaks(unwind_func, &pid);
 
-  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
-      << "ptrace detach failed with unexpected error: " << strerror(errno);
+  ASSERT_TRUE(Detach(pid));
 }
 
 static void RemoteUnwind(void* data) {
@@ -368,8 +350,8 @@
   act.sa_sigaction = SignalHandler;
   act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
   ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
-  // Wait for the tid to get set.
-  for (size_t i = 0; i < 100; i++) {
+  // Wait 20 seconds for the tid to get set.
+  for (time_t start_time = time(nullptr); time(nullptr) - start_time < 20;) {
     if (tid.load() != 0) {
       break;
     }
@@ -378,9 +360,9 @@
   ASSERT_NE(0, tid.load());
   ASSERT_EQ(0, tgkill(getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
 
-  // Wait for context data.
+  // Wait 20 seconds for context data.
   void* ucontext;
-  for (size_t i = 0; i < 2000; i++) {
+  for (time_t start_time = time(nullptr); time(nullptr) - start_time < 20;) {
     ucontext = reinterpret_cast<void*>(g_ucontext.load());
     if (ucontext != nullptr) {
       break;
@@ -416,14 +398,11 @@
   ASSERT_NE(-1, pid);
   TestScopedPidReaper reap(pid);
 
-  bool completed;
   if (signal != SIGSEGV) {
-    WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed);
-    ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+    ASSERT_TRUE(WaitForRemote(pid, false, reinterpret_cast<uint64_t>(&g_ready_for_remote)));
     ASSERT_EQ(0, kill(pid, SIGUSR1));
   }
-  WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_signal_ready_for_remote), true, &completed);
-  ASSERT_TRUE(completed) << "Timed out waiting for remote process to be in signal handler.";
+  ASSERT_TRUE(WaitForRemote(pid, true, reinterpret_cast<uint64_t>(&g_signal_ready_for_remote)));
 
   RemoteMaps maps(pid);
   ASSERT_TRUE(maps.Parse());
@@ -432,8 +411,7 @@
 
   VerifyUnwind(pid, &maps, regs.get(), kFunctionSignalOrder);
 
-  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
-      << "ptrace detach failed with unexpected error: " << strerror(errno);
+  ASSERT_TRUE(Detach(pid));
 }
 
 TEST_F(UnwindTest, remote_through_signal) {
@@ -468,8 +446,8 @@
   size_t frames[kNumConcurrentThreads];
   for (size_t i = 0; i < kNumConcurrentThreads; i++) {
     std::thread* thread = new std::thread([i, &frames, &maps, &process_memory, &wait]() {
-      while (wait)
-        ;
+      while (wait) {
+      }
       std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
       RegsGetLocal(regs.get());
 
@@ -496,8 +474,8 @@
     OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH);
   });
 
-  while (tid.load() == 0)
-    ;
+  while (tid.load() == 0) {
+  }
 
   ThreadUnwinder unwinder(512);
   ASSERT_TRUE(unwinder.Init());
@@ -508,6 +486,34 @@
   thread.join();
 }
 
+TEST_F(UnwindTest, thread_unwind_copy_regs) {
+  ResetGlobals();
+
+  std::atomic_int tid(0);
+  std::thread thread([&tid]() {
+    tid = android::base::GetThreadId();
+    OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH);
+  });
+
+  while (tid.load() == 0) {
+  }
+
+  ThreadUnwinder unwinder(512);
+  ASSERT_TRUE(unwinder.Init());
+  std::unique_ptr<Regs> initial_regs;
+  unwinder.UnwindWithSignal(SIGRTMIN, tid, &initial_regs);
+  ASSERT_TRUE(initial_regs != nullptr);
+  // Verify the initial registers match the first frame pc/sp.
+  ASSERT_TRUE(unwinder.NumFrames() != 0);
+  auto initial_frame = unwinder.frames()[0];
+  ASSERT_EQ(initial_regs->pc(), initial_frame.pc);
+  ASSERT_EQ(initial_regs->sp(), initial_frame.sp);
+  VerifyUnwindFrames(&unwinder, kFunctionOrder);
+
+  g_finish = true;
+  thread.join();
+}
+
 TEST_F(UnwindTest, thread_unwind_with_external_maps) {
   ResetGlobals();
 
@@ -517,8 +523,8 @@
     OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH);
   });
 
-  while (tid.load() == 0)
-    ;
+  while (tid.load() == 0) {
+  }
 
   LocalMaps maps;
   ASSERT_TRUE(maps.Parse());
@@ -543,28 +549,44 @@
   EXPECT_EQ(ERROR_UNSUPPORTED, unwinder.LastErrorCode());
 }
 
+TEST_F(UnwindTest, thread_unwind_cur_thread) {
+  std::thread thread([]() {
+    ThreadUnwinder unwinder(512);
+    ASSERT_TRUE(unwinder.Init());
+    unwinder.UnwindWithSignal(SIGRTMIN, android::base::GetThreadId());
+    EXPECT_EQ(0U, unwinder.NumFrames());
+    EXPECT_EQ(ERROR_UNSUPPORTED, unwinder.LastErrorCode());
+  });
+  thread.join();
+}
+
+TEST_F(UnwindTest, thread_unwind_cur_pid_from_thread) {
+  std::thread thread([]() {
+    ThreadUnwinder unwinder(512);
+    ASSERT_TRUE(unwinder.Init());
+    unwinder.UnwindWithSignal(SIGRTMIN, getpid());
+    EXPECT_NE(0U, unwinder.NumFrames());
+    EXPECT_NE(ERROR_UNSUPPORTED, unwinder.LastErrorCode());
+  });
+  thread.join();
+}
+
 static std::thread* CreateUnwindThread(std::atomic_int& tid, ThreadUnwinder& unwinder,
                                        std::atomic_bool& start_unwinding,
                                        std::atomic_int& unwinders) {
   return new std::thread([&tid, &unwinder, &start_unwinding, &unwinders]() {
-    while (!start_unwinding.load())
-      ;
+    while (!start_unwinding.load()) {
+    }
 
     ThreadUnwinder thread_unwinder(512, &unwinder);
-    thread_unwinder.UnwindWithSignal(SIGRTMIN, tid);
-#if defined(__arm__)
-    // On arm, there is a chance of winding up in case that doesn't unwind.
-    // Identify that case and allow unwinding in that case.
-    for (size_t i = 0; i < 10; i++) {
-      auto frames = thread_unwinder.frames();
-      if (frames.size() > 1 && frames[frames.size() - 1].pc < 1000 &&
-          frames[frames.size() - 2].function_name == "InnerFunction") {
-        thread_unwinder.UnwindWithSignal(SIGRTMIN, tid);
-      } else {
+    // Allow the unwind to timeout since this will be doing multiple
+    // unwinds at once.
+    for (size_t i = 0; i < 3; i++) {
+      thread_unwinder.UnwindWithSignal(SIGRTMIN, tid);
+      if (thread_unwinder.LastErrorCode() != ERROR_THREAD_TIMEOUT) {
         break;
       }
     }
-#endif
     VerifyUnwindFrames(&thread_unwinder, kFunctionOrder);
     ++unwinders;
   });
@@ -580,8 +602,8 @@
     OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH);
   });
 
-  while (g_waiters.load() != 1)
-    ;
+  while (g_waiters.load() != 1) {
+  }
 
   ThreadUnwinder unwinder(512);
   ASSERT_TRUE(unwinder.Init());
@@ -594,8 +616,8 @@
   }
 
   start_unwinding = true;
-  while (unwinders.load() != kNumThreads)
-    ;
+  while (unwinders.load() != kNumThreads) {
+  }
 
   for (auto* thread : threads) {
     thread->join();
@@ -620,8 +642,8 @@
     threads.push_back(thread);
   }
 
-  while (g_waiters.load() != kNumThreads)
-    ;
+  while (g_waiters.load() != kNumThreads) {
+  }
 
   ThreadUnwinder unwinder(512);
   ASSERT_TRUE(unwinder.Init());
@@ -634,8 +656,8 @@
   }
 
   start_unwinding = true;
-  while (unwinders.load() != kNumThreads)
-    ;
+  while (unwinders.load() != kNumThreads) {
+  }
 
   for (auto* thread : unwinder_threads) {
     thread->join();
@@ -670,8 +692,8 @@
     threads.push_back(thread);
   }
 
-  while (g_waiters.load() != kNumThreads)
-    ;
+  while (g_waiters.load() != kNumThreads) {
+  }
 
   ThreadUnwinder unwinder(512, &maps);
   ASSERT_TRUE(unwinder.Init());
@@ -684,8 +706,8 @@
   }
 
   start_unwinding = true;
-  while (unwinders.load() != kNumThreads)
-    ;
+  while (unwinders.load() != kNumThreads) {
+  }
 
   for (auto* thread : unwinder_threads) {
     thread->join();
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 2884a40..2da3ee1 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -39,8 +39,8 @@
 
 #include "ElfFake.h"
 #include "ElfTestUtils.h"
-#include "MemoryFake.h"
-#include "RegsFake.h"
+#include "utils/MemoryFake.h"
+#include "utils/RegsFake.h"
 
 namespace unwindstack {
 
@@ -49,8 +49,8 @@
   static MapInfo* AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
                              const char* name, Elf* elf = nullptr) {
     std::string str_name(name);
-    maps_->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
-    MapInfo* map_info = maps_->Find(start);
+    maps_->Add(start, end, offset, flags, name);
+    MapInfo* map_info = maps_->Find(start).get();
     if (elf != nullptr) {
       map_info->set_elf(elf);
     }
@@ -99,9 +99,7 @@
 
     AddMapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
 
-    map_info =
-        AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
-    map_info->set_load_bias(0);
+    AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
 
     elf = new ElfFake(new MemoryFake);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
@@ -139,7 +137,7 @@
 
     map_info =
         AddMapInfo(0xd0000, 0xd1000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.apk");
-    map_info->set_load_bias(0);
+    map_info->set_elf_start_offset(0x1000);
 
     elf = new ElfFake(new MemoryFake);
     interface = new ElfInterfaceFake(nullptr);
@@ -208,7 +206,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -219,13 +216,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 
   frame = &unwinder.frames()[1];
   EXPECT_EQ(1U, frame->num);
@@ -234,13 +233,15 @@
   EXPECT_EQ(0x10010U, frame->sp);
   EXPECT_EQ("Frame1", frame->function_name);
   EXPECT_EQ(1U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 
   frame = &unwinder.frames()[2];
   EXPECT_EQ(2U, frame->num);
@@ -249,13 +250,15 @@
   EXPECT_EQ(0x10020U, frame->sp);
   EXPECT_EQ("Frame2", frame->function_name);
   EXPECT_EQ(2U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 }
 
 TEST_F(UnwinderTest, multiple_frames_dont_resolve_names) {
@@ -274,7 +277,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -285,13 +287,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 
   frame = &unwinder.frames()[1];
   EXPECT_EQ(1U, frame->num);
@@ -300,13 +304,15 @@
   EXPECT_EQ(0x10010U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 
   frame = &unwinder.frames()[2];
   EXPECT_EQ(2U, frame->num);
@@ -315,13 +321,15 @@
   EXPECT_EQ(0x10020U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 }
 
 TEST_F(UnwinderTest, non_zero_load_bias) {
@@ -335,7 +343,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -346,13 +353,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/fake/fake_load_bias.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0xa5000U, frame->map_start);
-  EXPECT_EQ(0xa6000U, frame->map_end);
-  EXPECT_EQ(0x5000U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/fake/fake_load_bias.so", frame->map_info->name());
+  EXPECT_EQ("/fake/fake_load_bias.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0xa5000U, frame->map_info->start());
+  EXPECT_EQ(0xa6000U, frame->map_info->end());
+  EXPECT_EQ(0x5000U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
 }
 
 TEST_F(UnwinderTest, non_zero_elf_offset) {
@@ -366,7 +375,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -377,13 +385,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/fake/fake_offset.oat", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0xa7000U, frame->map_start);
-  EXPECT_EQ(0xa8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/fake/fake_offset.oat", frame->map_info->name());
+  EXPECT_EQ("/fake/fake_offset.oat", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0xa7000U, frame->map_info->start());
+  EXPECT_EQ(0xa8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
 }
 
 TEST_F(UnwinderTest, non_zero_map_offset) {
@@ -397,7 +407,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -408,45 +417,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
-  EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
-  EXPECT_EQ(0x1d000U, frame->map_exact_offset);
-  EXPECT_EQ(0x43000U, frame->map_start);
-  EXPECT_EQ(0x44000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
-}
-
-TEST_F(UnwinderTest, disable_embedded_soname) {
-  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
-
-  regs_.set_pc(0x43000);
-  regs_.set_sp(0x10000);
-  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
-
-  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
-  unwinder.SetEmbeddedSoname(false);
-  unwinder.Unwind();
-  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
-  EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
-
-  ASSERT_EQ(1U, unwinder.NumFrames());
-
-  auto* frame = &unwinder.frames()[0];
-  EXPECT_EQ(0U, frame->num);
-  EXPECT_EQ(0U, frame->rel_pc);
-  EXPECT_EQ(0x43000U, frame->pc);
-  EXPECT_EQ(0x10000U, frame->sp);
-  EXPECT_EQ("Frame0", frame->function_name);
-  EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/fake/fake.apk", frame->map_name);
-  EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
-  EXPECT_EQ(0x1d000U, frame->map_exact_offset);
-  EXPECT_EQ(0x43000U, frame->map_start);
-  EXPECT_EQ(0x44000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/fake/fake.apk", frame->map_info->name());
+  EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0x1d000U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0x1d000U, frame->map_info->offset());
+  EXPECT_EQ(0x43000U, frame->map_info->start());
+  EXPECT_EQ(0x44000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 }
 
 // Verify that no attempt to continue after the step indicates it is done.
@@ -467,7 +446,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -478,13 +456,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 }
 
 // Verify the maximum frames to save.
@@ -501,25 +481,27 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(20U, unwinder.NumFrames());
 
   for (size_t i = 0; i < 20; i++) {
     auto* frame = &unwinder.frames()[i];
     EXPECT_EQ(i, frame->num);
-    EXPECT_EQ(i * 0x100, frame->rel_pc) << "Failed at frame " << i;
-    EXPECT_EQ(0x1000 + i * 0x100, frame->pc) << "Failed at frame " << i;
-    EXPECT_EQ(0x10000 + 0x10 * i, frame->sp) << "Failed at frame " << i;
-    EXPECT_EQ("Frame" + std::to_string(i), frame->function_name) << "Failed at frame " << i;
-    EXPECT_EQ(i, frame->function_offset) << "Failed at frame " << i;
-    EXPECT_EQ("/system/fake/libc.so", frame->map_name) << "Failed at frame " << i;
-    EXPECT_EQ(0U, frame->map_elf_start_offset) << "Failed at frame " << i;
-    EXPECT_EQ(0U, frame->map_exact_offset) << "Failed at frame " << i;
-    EXPECT_EQ(0x1000U, frame->map_start) << "Failed at frame " << i;
-    EXPECT_EQ(0x8000U, frame->map_end) << "Failed at frame " << i;
-    EXPECT_EQ(0U, frame->map_load_bias) << "Failed at frame " << i;
-    EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags) << "Failed at frame " << i;
+    SCOPED_TRACE(testing::Message() << "Failed at frame " << i);
+    EXPECT_EQ(i * 0x100, frame->rel_pc);
+    EXPECT_EQ(0x1000 + i * 0x100, frame->pc);
+    EXPECT_EQ(0x10000 + 0x10 * i, frame->sp);
+    EXPECT_EQ("Frame" + std::to_string(i), frame->function_name);
+    EXPECT_EQ(i, frame->function_offset);
+    ASSERT_TRUE(frame->map_info != nullptr);
+    EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+    EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+    EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+    EXPECT_EQ(0U, frame->map_info->offset());
+    EXPECT_EQ(0x1000U, frame->map_info->start());
+    EXPECT_EQ(0x8000U, frame->map_info->end());
+    EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+    EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
   }
 }
 
@@ -545,7 +527,6 @@
   unwinder.Unwind(&skip_libs);
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -556,13 +537,15 @@
   EXPECT_EQ(0x10050U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 
   frame = &unwinder.frames()[1];
   EXPECT_EQ(1U, frame->num);
@@ -571,13 +554,15 @@
   EXPECT_EQ(0x10060U, frame->sp);
   EXPECT_EQ("Frame1", frame->function_name);
   EXPECT_EQ(1U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x20000U, frame->map_start);
-  EXPECT_EQ(0x22000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libunwind.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libunwind.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x20000U, frame->map_info->start());
+  EXPECT_EQ(0x22000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 
   frame = &unwinder.frames()[2];
   EXPECT_EQ(2U, frame->num);
@@ -586,12 +571,14 @@
   EXPECT_EQ(0x10070U, frame->sp);
   EXPECT_EQ("Frame2", frame->function_name);
   EXPECT_EQ(2U, frame->function_offset);
-  EXPECT_EQ("/fake/libanother.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x23000U, frame->map_start);
-  EXPECT_EQ(0x24000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/fake/libanother.so", frame->map_info->name());
+  EXPECT_EQ("/fake/libanother.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x23000U, frame->map_info->start());
+  EXPECT_EQ(0x24000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 }
 
 // Verify SP in a non-existant map is okay.
@@ -608,7 +595,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(2U, unwinder.NumFrames());
 
@@ -619,12 +605,14 @@
   EXPECT_EQ(0x63000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 
   frame = &unwinder.frames()[1];
   EXPECT_EQ(1U, frame->num);
@@ -633,12 +621,14 @@
   EXPECT_EQ(0x50020U, frame->sp);
   EXPECT_EQ("Frame1", frame->function_name);
   EXPECT_EQ(1U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x20000U, frame->map_start);
-  EXPECT_EQ(0x22000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libunwind.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libunwind.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x20000U, frame->map_info->start());
+  EXPECT_EQ(0x22000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 }
 
 // Verify PC in a device stops the unwind.
@@ -657,7 +647,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 }
@@ -678,7 +667,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 }
@@ -694,7 +682,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -705,13 +692,7 @@
   EXPECT_EQ(0x13000U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0U, frame->map_start);
-  EXPECT_EQ(0U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(0, frame->map_flags);
+  ASSERT_TRUE(frame->map_info == nullptr);
 }
 
 // Verify that a speculative frame is added.
@@ -732,7 +713,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -743,13 +723,7 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0U, frame->map_start);
-  EXPECT_EQ(0U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(0, frame->map_flags);
+  ASSERT_TRUE(frame->map_info == nullptr);
 
   frame = &unwinder.frames()[1];
   EXPECT_EQ(1U, frame->num);
@@ -758,13 +732,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 
   frame = &unwinder.frames()[2];
   EXPECT_EQ(2U, frame->num);
@@ -773,13 +749,15 @@
   EXPECT_EQ(0x10020U, frame->sp);
   EXPECT_EQ("Frame1", frame->function_name);
   EXPECT_EQ(1U, frame->function_offset);
-  EXPECT_EQ("/fake/libanother.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x23000U, frame->map_start);
-  EXPECT_EQ(0x24000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/fake/libanother.so", frame->map_info->name());
+  EXPECT_EQ("/fake/libanother.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x23000U, frame->map_info->start());
+  EXPECT_EQ(0x24000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 }
 
 // Verify that a speculative frame is added then removed because no other
@@ -799,7 +777,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(2U, unwinder.NumFrames());
 
@@ -810,13 +787,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x20000U, frame->map_start);
-  EXPECT_EQ(0x22000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libunwind.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libunwind.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x20000U, frame->map_info->start());
+  EXPECT_EQ(0x22000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 
   frame = &unwinder.frames()[1];
   EXPECT_EQ(1U, frame->num);
@@ -825,13 +804,7 @@
   EXPECT_EQ(0x10010U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0U, frame->map_start);
-  EXPECT_EQ(0U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(0, frame->map_flags);
+  ASSERT_TRUE(frame->map_info == nullptr);
 }
 
 // Verify that a speculative frame is added and left if there are only
@@ -850,7 +823,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(2U, unwinder.NumFrames());
 
@@ -861,13 +833,7 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0U, frame->map_start);
-  EXPECT_EQ(0U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(0, frame->map_flags);
+  ASSERT_TRUE(frame->map_info == nullptr);
 
   frame = &unwinder.frames()[1];
   EXPECT_EQ(1U, frame->num);
@@ -876,13 +842,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 }
 
 // Verify that a speculative frame does not cause a crash when it wasn't
@@ -899,11 +867,35 @@
   unwinder.Unwind(&skip_names);
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(0U, unwinder.NumFrames());
 }
 
+// Verify that a speculative frame mapping to invalid map doesn't hide error
+// for the previous frame.
+TEST_F(UnwinderTest, speculative_frame_to_invalid_map_not_hide_prev_error) {
+  regs_.set_pc(0x100000);
+  regs_.set_sp(0x10000);
+  regs_.FakeSetReturnAddress(0x4);
+  regs_.FakeSetReturnAddressValid(true);
+
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_INVALID_ELF, unwinder.LastErrorCode());
+  EXPECT_EQ(WARNING_NONE, unwinder.warnings());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x300U, frame->rel_pc);
+  EXPECT_EQ(0x100000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  ASSERT_TRUE(frame->map_info != nullptr);
+}
+
 // Verify that an unwind stops when a frame is in given suffix.
 TEST_F(UnwinderTest, map_ignore_suffixes) {
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
@@ -923,11 +915,10 @@
   unwinder.Unwind(nullptr, &suffixes);
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(2U, unwinder.NumFrames());
   // Make sure the elf was not initialized.
-  MapInfo* map_info = maps_->Find(0x53000);
+  MapInfo* map_info = maps_->Find(0x53000).get();
   ASSERT_TRUE(map_info != nullptr);
   EXPECT_TRUE(map_info->elf() == nullptr);
 
@@ -938,13 +929,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 
   frame = &unwinder.frames()[1];
   EXPECT_EQ(1U, frame->num);
@@ -953,13 +946,15 @@
   EXPECT_EQ(0x10010U, frame->sp);
   EXPECT_EQ("Frame1", frame->function_name);
   EXPECT_EQ(1U, frame->function_offset);
-  EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
-  EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
-  EXPECT_EQ(0x1d000U, frame->map_exact_offset);
-  EXPECT_EQ(0x43000U, frame->map_start);
-  EXPECT_EQ(0x44000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/fake/fake.apk", frame->map_info->name());
+  EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0x1d000U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0x1d000U, frame->map_info->offset());
+  EXPECT_EQ(0x43000U, frame->map_info->start());
+  EXPECT_EQ(0x44000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 }
 
 // Verify that an unwind stops when the sp and pc don't change.
@@ -983,7 +978,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_REPEATED_FRAME, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -994,13 +988,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 
   frame = &unwinder.frames()[1];
   EXPECT_EQ(1U, frame->num);
@@ -1009,13 +1005,15 @@
   EXPECT_EQ(0x10010U, frame->sp);
   EXPECT_EQ("Frame1", frame->function_name);
   EXPECT_EQ(1U, frame->function_offset);
-  EXPECT_EQ("/fake/compressed.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x33000U, frame->map_start);
-  EXPECT_EQ(0x34000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/fake/compressed.so", frame->map_info->name());
+  EXPECT_EQ("/fake/compressed.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x33000U, frame->map_info->start());
+  EXPECT_EQ(0x34000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 
   frame = &unwinder.frames()[2];
   EXPECT_EQ(2U, frame->num);
@@ -1024,13 +1022,15 @@
   EXPECT_EQ(0x10020U, frame->sp);
   EXPECT_EQ("Frame2", frame->function_name);
   EXPECT_EQ(2U, frame->function_offset);
-  EXPECT_EQ("/fake/compressed.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x33000U, frame->map_start);
-  EXPECT_EQ(0x34000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/fake/compressed.so", frame->map_info->name());
+  EXPECT_EQ("/fake/compressed.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x33000U, frame->map_info->start());
+  EXPECT_EQ(0x34000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 }
 
 TEST_F(UnwinderTest, dex_pc_in_map) {
@@ -1043,7 +1043,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(2U, unwinder.NumFrames());
 
@@ -1054,13 +1053,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/fake/fake.vdex", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0xa3000U, frame->map_start);
-  EXPECT_EQ(0xa4000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/fake/fake.vdex", frame->map_info->name());
+  EXPECT_EQ("/fake/fake.vdex", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0xa3000U, frame->map_info->start());
+  EXPECT_EQ(0xa4000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
 
   frame = &unwinder.frames()[1];
   EXPECT_EQ(1U, frame->num);
@@ -1069,13 +1070,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 }
 
 TEST_F(UnwinderTest, dex_pc_in_map_non_zero_offset) {
@@ -1088,7 +1091,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(2U, unwinder.NumFrames());
 
@@ -1099,13 +1101,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/fake/fake.apk", frame->map_name);
-  EXPECT_EQ(0x1000U, frame->map_elf_start_offset);
-  EXPECT_EQ(0x1000U, frame->map_exact_offset);
-  EXPECT_EQ(0xd0000U, frame->map_start);
-  EXPECT_EQ(0xd1000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/fake/fake.apk", frame->map_info->name());
+  EXPECT_EQ("/fake/fake.apk", frame->map_info->GetFullName());
+  EXPECT_EQ(0x1000U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0x1000U, frame->map_info->offset());
+  EXPECT_EQ(0xd0000U, frame->map_info->start());
+  EXPECT_EQ(0xd1000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
 
   frame = &unwinder.frames()[1];
   EXPECT_EQ(1U, frame->num);
@@ -1114,13 +1118,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 }
 
 TEST_F(UnwinderTest, dex_pc_not_in_map) {
@@ -1133,7 +1139,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_DEX_PC_NOT_IN_MAP, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(2U, unwinder.NumFrames());
 
@@ -1144,13 +1149,7 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0U, frame->map_start);
-  EXPECT_EQ(0U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(0, frame->map_flags);
+  ASSERT_TRUE(frame->map_info == nullptr);
 
   frame = &unwinder.frames()[1];
   EXPECT_EQ(1U, frame->num);
@@ -1159,13 +1158,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 }
 
 TEST_F(UnwinderTest, dex_pc_not_in_map_valid_dex_files) {
@@ -1180,7 +1181,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_DEX_PC_NOT_IN_MAP, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(2U, unwinder.NumFrames());
 
@@ -1191,13 +1191,7 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0U, frame->map_start);
-  EXPECT_EQ(0U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(0, frame->map_flags);
+  ASSERT_TRUE(frame->map_info == nullptr);
 
   frame = &unwinder.frames()[1];
   EXPECT_EQ(1U, frame->num);
@@ -1206,13 +1200,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 }
 
 TEST_F(UnwinderTest, dex_pc_multiple_frames) {
@@ -1228,7 +1224,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -1239,13 +1234,14 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/fake/fake.vdex", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0xa3000U, frame->map_start);
-  EXPECT_EQ(0xa4000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/fake/fake.vdex", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0xa3000U, frame->map_info->start());
+  EXPECT_EQ(0xa4000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
 
   frame = &unwinder.frames()[1];
   EXPECT_EQ(1U, frame->num);
@@ -1254,13 +1250,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x1000U, frame->map_start);
-  EXPECT_EQ(0x8000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x1000U, frame->map_info->start());
+  EXPECT_EQ(0x8000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 
   frame = &unwinder.frames()[2];
   EXPECT_EQ(2U, frame->num);
@@ -1269,13 +1267,15 @@
   EXPECT_EQ(0x10010U, frame->sp);
   EXPECT_EQ("Frame1", frame->function_name);
   EXPECT_EQ(1U, frame->function_offset);
-  EXPECT_EQ("/fake/compressed.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0x33000U, frame->map_start);
-  EXPECT_EQ(0x34000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/fake/compressed.so", frame->map_info->name());
+  EXPECT_EQ("/fake/compressed.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0x33000U, frame->map_info->start());
+  EXPECT_EQ(0x34000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
 }
 
 TEST_F(UnwinderTest, dex_pc_max_frames) {
@@ -1288,7 +1288,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -1299,16 +1298,18 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/fake/fake.vdex", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0xa3000U, frame->map_start);
-  EXPECT_EQ(0xa4000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/fake/fake.vdex", frame->map_info->name());
+  EXPECT_EQ("/fake/fake.vdex", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0xa3000U, frame->map_info->start());
+  EXPECT_EQ(0xa4000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
 }
 
-TEST_F(UnwinderTest, elf_from_memory_not_file) {
+TEST_F(UnwinderTest, elf_file_not_readable) {
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
 
   regs_.set_pc(0xc0050);
@@ -1319,7 +1320,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_TRUE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -1330,13 +1330,16 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/fake/unreadable.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0xc0000U, frame->map_start);
-  EXPECT_EQ(0xc1000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_TRUE(frame->map_info->ElfFileNotReadable());
+  EXPECT_EQ("/fake/unreadable.so", frame->map_info->name());
+  EXPECT_EQ("/fake/unreadable.so", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0xc0000U, frame->map_info->start());
+  EXPECT_EQ(0xc1000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
 }
 
 TEST_F(UnwinderTest, elf_from_memory_but_no_valid_file_with_bracket) {
@@ -1350,7 +1353,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -1361,13 +1363,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("[vdso]", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0xc1000U, frame->map_start);
-  EXPECT_EQ(0xc2000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("[vdso]", frame->map_info->name());
+  EXPECT_EQ("[vdso]", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0xc1000U, frame->map_info->start());
+  EXPECT_EQ(0xc2000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
 }
 
 TEST_F(UnwinderTest, elf_from_memory_but_empty_filename) {
@@ -1381,7 +1385,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -1392,13 +1395,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0xc2000U, frame->map_start);
-  EXPECT_EQ(0xc3000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("", frame->map_info->name());
+  EXPECT_EQ("", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0xc2000U, frame->map_info->start());
+  EXPECT_EQ(0xc3000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
 }
 
 TEST_F(UnwinderTest, elf_from_memory_but_from_memfd) {
@@ -1412,7 +1417,6 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_NONE, unwinder.warnings());
-  EXPECT_FALSE(unwinder.elf_from_memory_not_file());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -1423,13 +1427,15 @@
   EXPECT_EQ(0x10000U, frame->sp);
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
-  EXPECT_EQ("/memfd:/jit-cache", frame->map_name);
-  EXPECT_EQ(0U, frame->map_elf_start_offset);
-  EXPECT_EQ(0U, frame->map_exact_offset);
-  EXPECT_EQ(0xc3000U, frame->map_start);
-  EXPECT_EQ(0xc4000U, frame->map_end);
-  EXPECT_EQ(0U, frame->map_load_bias);
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+  ASSERT_TRUE(frame->map_info != nullptr);
+  EXPECT_EQ("/memfd:/jit-cache", frame->map_info->name());
+  EXPECT_EQ("/memfd:/jit-cache", frame->map_info->GetFullName());
+  EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame->map_info->offset());
+  EXPECT_EQ(0xc3000U, frame->map_info->start());
+  EXPECT_EQ(0xc4000U, frame->map_info->end());
+  EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
 }
 
 // Verify format frame code.
@@ -1449,18 +1455,16 @@
   frame.sp = 0x1000;
   frame.function_name = "function";
   frame.function_offset = 100;
-  frame.map_name = "/fake/libfake.so";
-  frame.map_elf_start_offset = 0x2000;
-  frame.map_start = 0x3000;
-  frame.map_end = 0x6000;
-  frame.map_flags = PROT_READ;
+  auto map_info = MapInfo::Create(0x3000, 0x6000, 0, PROT_READ, "/fake/libfake.so");
+  map_info->set_elf_start_offset(0x2000);
+  frame.map_info = map_info;
 
   EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (offset 0x2000) (function+100)",
             unwinder64.FormatFrame(frame));
   EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (offset 0x2000) (function+100)",
             unwinder32.FormatFrame(frame));
 
-  frame.map_elf_start_offset = 0;
+  map_info->set_elf_start_offset(0);
   EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (function+100)",
             unwinder64.FormatFrame(frame));
   EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function+100)", unwinder32.FormatFrame(frame));
@@ -1479,12 +1483,11 @@
   EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so", unwinder64.FormatFrame(frame));
   EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so", unwinder32.FormatFrame(frame));
 
-  frame.map_name = "";
+  map_info->name() = "";
   EXPECT_EQ("  #01 pc 0000000000001000  <anonymous:3000>", unwinder64.FormatFrame(frame));
   EXPECT_EQ("  #01 pc 00001000  <anonymous:3000>", unwinder32.FormatFrame(frame));
 
-  frame.map_start = 0;
-  frame.map_end = 0;
+  frame.map_info = nullptr;
   EXPECT_EQ("  #01 pc 0000000000001000  <unknown>", unwinder64.FormatFrame(frame));
   EXPECT_EQ("  #01 pc 00001000  <unknown>", unwinder32.FormatFrame(frame));
 }
@@ -1501,11 +1504,9 @@
   frame.sp = 0x1000;
   frame.function_name = "function";
   frame.function_offset = 100;
-  frame.map_name = "/fake/libfake.so";
-  frame.map_elf_start_offset = 0;
-  frame.map_start = 0x3000;
-  frame.map_end = 0x6000;
-  frame.map_flags = PROT_READ;
+  frame.map_info = MapInfo::Create(0x3000, 0x6000, 0, PROT_READ, "/fake/libfake.so");
+  SharedString* build_id = new SharedString(std::string{0x46, 0x41, 0x4b, 0x45});
+  frame.map_info->set_build_id(build_id);
 
   EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function+100)", unwinder.FormatFrame(frame));
   unwinder.SetDisplayBuildID(true);
@@ -1611,13 +1612,15 @@
   frame = unwinder.BuildFrameFromPcOnly(0x100310);
   EXPECT_EQ(0x10030eU, frame.pc);
   EXPECT_EQ(0x60eU, frame.rel_pc);
-  EXPECT_EQ("/fake/jit.so", frame.map_name);
-  EXPECT_EQ(0x100U, frame.map_elf_start_offset);
-  EXPECT_EQ(0x200U, frame.map_exact_offset);
-  EXPECT_EQ(0x100000U, frame.map_start);
-  EXPECT_EQ(0x101000U, frame.map_end);
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame.map_flags);
-  EXPECT_EQ(0x300U, frame.map_load_bias);
+  ASSERT_TRUE(frame.map_info != nullptr);
+  EXPECT_EQ("/fake/jit.so", frame.map_info->name());
+  EXPECT_EQ("/fake/jit.so", frame.map_info->GetFullName());
+  EXPECT_EQ(0x100U, frame.map_info->elf_start_offset());
+  EXPECT_EQ(0x200U, frame.map_info->offset());
+  EXPECT_EQ(0x100000U, frame.map_info->start());
+  EXPECT_EQ(0x101000U, frame.map_info->end());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame.map_info->flags());
+  EXPECT_EQ(0U, frame.map_info->GetLoadBias());
   EXPECT_EQ("", frame.function_name);
   EXPECT_EQ(0U, frame.function_offset);
 }
@@ -1633,13 +1636,15 @@
   frame = unwinder.BuildFrameFromPcOnly(0x1010);
   EXPECT_EQ(0x100cU, frame.pc);
   EXPECT_EQ(0xcU, frame.rel_pc);
-  EXPECT_EQ("/system/fake/libc.so", frame.map_name);
-  EXPECT_EQ(0U, frame.map_elf_start_offset);
-  EXPECT_EQ(0U, frame.map_exact_offset);
-  EXPECT_EQ(0x1000U, frame.map_start);
-  EXPECT_EQ(0x8000U, frame.map_end);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_flags);
-  EXPECT_EQ(0U, frame.map_load_bias);
+  ASSERT_TRUE(frame.map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame.map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame.map_info->GetFullName());
+  EXPECT_EQ(0U, frame.map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame.map_info->offset());
+  EXPECT_EQ(0x1000U, frame.map_info->start());
+  EXPECT_EQ(0x8000U, frame.map_info->end());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_info->flags());
+  EXPECT_EQ(0U, frame.map_info->GetLoadBias());
   EXPECT_EQ("", frame.function_name);
   EXPECT_EQ(0U, frame.function_offset);
 
@@ -1650,13 +1655,15 @@
   frame = unwinder.BuildFrameFromPcOnly(0x1010);
   EXPECT_EQ(0x100cU, frame.pc);
   EXPECT_EQ(0xcU, frame.rel_pc);
-  EXPECT_EQ("/system/fake/libc.so", frame.map_name);
-  EXPECT_EQ(0U, frame.map_elf_start_offset);
-  EXPECT_EQ(0U, frame.map_exact_offset);
-  EXPECT_EQ(0x1000U, frame.map_start);
-  EXPECT_EQ(0x8000U, frame.map_end);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_flags);
-  EXPECT_EQ(0U, frame.map_load_bias);
+  ASSERT_TRUE(frame.map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame.map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame.map_info->GetFullName());
+  EXPECT_EQ(0U, frame.map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame.map_info->offset());
+  EXPECT_EQ(0x1000U, frame.map_info->start());
+  EXPECT_EQ(0x8000U, frame.map_info->end());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_info->flags());
+  EXPECT_EQ(0U, frame.map_info->GetLoadBias());
   EXPECT_EQ("", frame.function_name);
   EXPECT_EQ(0U, frame.function_offset);
 
@@ -1666,13 +1673,15 @@
   frame = unwinder.BuildFrameFromPcOnly(0x1010);
   EXPECT_EQ(0x100cU, frame.pc);
   EXPECT_EQ(0xcU, frame.rel_pc);
-  EXPECT_EQ("/system/fake/libc.so", frame.map_name);
-  EXPECT_EQ(0U, frame.map_elf_start_offset);
-  EXPECT_EQ(0U, frame.map_exact_offset);
-  EXPECT_EQ(0x1000U, frame.map_start);
-  EXPECT_EQ(0x8000U, frame.map_end);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_flags);
-  EXPECT_EQ(0U, frame.map_load_bias);
+  ASSERT_TRUE(frame.map_info != nullptr);
+  EXPECT_EQ("/system/fake/libc.so", frame.map_info->name());
+  EXPECT_EQ("/system/fake/libc.so", frame.map_info->GetFullName());
+  EXPECT_EQ(0U, frame.map_info->elf_start_offset());
+  EXPECT_EQ(0U, frame.map_info->offset());
+  EXPECT_EQ(0x1000U, frame.map_info->start());
+  EXPECT_EQ(0x8000U, frame.map_info->end());
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_info->flags());
+  EXPECT_EQ(0U, frame.map_info->GetLoadBias());
   EXPECT_EQ("Frame0", frame.function_name);
   EXPECT_EQ(10U, frame.function_offset);
 }
@@ -1737,21 +1746,22 @@
   FrameData frame = unwinder.BuildFrameFromPcOnly(0x100310);
   EXPECT_EQ(0x10030eU, frame.pc);
   EXPECT_EQ(0x60eU, frame.rel_pc);
-  EXPECT_EQ("/fake/jit.so", frame.map_name);
-  EXPECT_EQ(0x100U, frame.map_elf_start_offset);
-  EXPECT_EQ(0x200U, frame.map_exact_offset);
-  EXPECT_EQ(0x100000U, frame.map_start);
-  EXPECT_EQ(0x101000U, frame.map_end);
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame.map_flags);
-  EXPECT_EQ(0U, frame.map_load_bias);
+  ASSERT_TRUE(frame.map_info != nullptr);
+  EXPECT_EQ("/fake/jit.so", frame.map_info->name());
+  EXPECT_EQ("/fake/jit.so", frame.map_info->GetFullName());
+  EXPECT_EQ(0x100U, frame.map_info->elf_start_offset());
+  EXPECT_EQ(0x200U, frame.map_info->offset());
+  EXPECT_EQ(0x100000U, frame.map_info->start());
+  EXPECT_EQ(0x101000U, frame.map_info->end());
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame.map_info->flags());
+  EXPECT_EQ(0U, frame.map_info->GetLoadBias());
   EXPECT_EQ("FakeJitFunction", frame.function_name);
   EXPECT_EQ(0xeU, frame.function_offset);
 }
 
 TEST_F(UnwinderTest, unwinder_from_pid_set_process_memory) {
   auto process_memory = Memory::CreateProcessMemoryCached(getpid());
-  UnwinderFromPid unwinder(10, getpid());
-  unwinder.SetProcessMemory(process_memory);
+  UnwinderFromPid unwinder(10, getpid(), process_memory);
   unwinder.SetArch(unwindstack::Regs::CurrentArch());
   ASSERT_TRUE(unwinder.Init());
   ASSERT_EQ(process_memory.get(), unwinder.GetProcessMemory().get());
@@ -1771,6 +1781,22 @@
   ASSERT_DEATH(CreateJitDebug(ARCH_UNKNOWN, process_memory), "");
 }
 
+TEST_F(UnwinderTest, unwinder_from_pid_with_external_maps) {
+  LocalMaps map;
+  ASSERT_TRUE(map.Parse());
+
+  UnwinderFromPid unwinder1(10, getpid(), &map);
+  unwinder1.SetArch(Regs::CurrentArch());
+  ASSERT_EQ(&map, unwinder1.GetMaps());
+  ASSERT_TRUE(unwinder1.Init());
+  ASSERT_EQ(&map, unwinder1.GetMaps());
+
+  UnwinderFromPid unwinder2(10, getpid(), Regs::CurrentArch(), &map);
+  ASSERT_EQ(&map, unwinder2.GetMaps());
+  ASSERT_TRUE(unwinder2.Init());
+  ASSERT_EQ(&map, unwinder2.GetMaps());
+}
+
 TEST_F(UnwinderDeathTest, set_dex_files_error) {
   Maps maps;
   std::shared_ptr<Memory> process_memory(new MemoryFake);
diff --git a/libunwindstack/tests/VerifyBionicTerminationTest.cpp b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
index 37fd89b..ff105c4 100644
--- a/libunwindstack/tests/VerifyBionicTerminationTest.cpp
+++ b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
@@ -69,9 +69,10 @@
 static void VerifyReturnAddress(const FrameData& frame) {
   // Now go and find information about the register data and verify that the relative pc results in
   // an undefined register.
-  Elf elf(Memory::CreateFileMemory(frame.map_name, 0).release());
-  ASSERT_TRUE(elf.Init()) << "Failed to init elf object from " << frame.map_name.c_str();
-  ASSERT_TRUE(elf.valid()) << "Elf " << frame.map_name.c_str() << " is not valid.";
+  Elf elf(Memory::CreateFileMemory(frame.map_info->name(), 0).release());
+  ASSERT_TRUE(frame.map_info != nullptr);
+  ASSERT_TRUE(elf.Init()) << "Failed to init elf object from " << frame.map_info->name().c_str();
+  ASSERT_TRUE(elf.valid()) << "Elf " << frame.map_info->name().c_str() << " is not valid.";
   ElfInterface* interface = elf.interface();
 
   // Only check the eh_frame and the debug_frame since the undefined register
@@ -108,8 +109,9 @@
   const std::vector<FrameData>& frames = unwinder.frames();
   for (size_t i = 0; i < unwinder.NumFrames(); i++) {
     const FrameData& frame = frames[i];
-    if (frame.function_name == "__libc_init" && !frame.map_name.empty() &&
-        std::string("libc.so") == basename(frame.map_name.c_str())) {
+    if (frame.function_name == "__libc_init" && frame.map_info != nullptr &&
+        !frame.map_info->name().empty() &&
+        std::string("libc.so") == basename(frame.map_info->name().c_str())) {
       ASSERT_EQ(unwinder.NumFrames(), i + 1) << "__libc_init is not last frame.";
       ASSERT_NO_FATAL_FAILURE(VerifyReturnAddress(frame));
       found = true;
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so
deleted file mode 100644
index 09ba495..0000000
--- a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.so b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.so
deleted file mode 100644
index 39c9025..0000000
--- a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/libc.so b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/libc.so
deleted file mode 100644
index 78449bf..0000000
--- a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64 b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64
deleted file mode 100644
index 81bda1d..0000000
--- a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/libc.so b/libunwindstack/tests/files/offline/debug_frame_first_x86/libc.so
deleted file mode 100644
index 9c78790..0000000
--- a/libunwindstack/tests/files/offline/debug_frame_first_x86/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/waiter b/libunwindstack/tests/files/offline/debug_frame_first_x86/waiter
deleted file mode 100644
index b1fc024..0000000
--- a/libunwindstack/tests/files/offline/debug_frame_first_x86/waiter
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
deleted file mode 100644
index 4b7bf44..0000000
--- a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
deleted file mode 100644
index 013858e..0000000
--- a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
deleted file mode 100644
index 9e4a83f..0000000
--- a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so b/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so
deleted file mode 100644
index f3eb615..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned b/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned
deleted file mode 100644
index aefdb6b..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so b/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so
deleted file mode 100644
index c71dcfb..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so
deleted file mode 100644
index 46b6f45..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64 b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64
deleted file mode 100644
index ab0ef8f..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/libbinder.so b/libunwindstack/tests/files/offline/empty_arm64/libbinder.so
deleted file mode 100644
index f30384c..0000000
--- a/libunwindstack/tests/files/offline/empty_arm64/libbinder.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/libc.so b/libunwindstack/tests/files/offline/empty_arm64/libc.so
deleted file mode 100644
index b05dcaf..0000000
--- a/libunwindstack/tests/files/offline/empty_arm64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/netd b/libunwindstack/tests/files/offline/empty_arm64/netd
deleted file mode 100644
index 8a72e94..0000000
--- a/libunwindstack/tests/files/offline/empty_arm64/netd
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/libandroid_runtime.so b/libunwindstack/tests/files/offline/gnu_debugdata_arm/libandroid_runtime.so
deleted file mode 100644
index e4283e6..0000000
--- a/libunwindstack/tests/files/offline/gnu_debugdata_arm/libandroid_runtime.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/dalvikvm32 b/libunwindstack/tests/files/offline/jit_debug_arm/dalvikvm32
deleted file mode 100644
index def299e..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_arm/dalvikvm32
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libart.so b/libunwindstack/tests/files/offline/jit_debug_arm/libart.so
deleted file mode 100644
index 0527893..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_arm/libart.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libartd.so b/libunwindstack/tests/files/offline/jit_debug_arm/libartd.so
deleted file mode 100644
index 8559056..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_arm/libartd.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_arm/libarttestd.so
deleted file mode 100644
index 06dbf10..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_arm/libarttestd.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libc.so b/libunwindstack/tests/files/offline/jit_debug_arm/libc.so
deleted file mode 100644
index 9894e66..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_arm/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/dalvikvm32 b/libunwindstack/tests/files/offline/jit_debug_x86/dalvikvm32
deleted file mode 100644
index 76ffad9..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_x86/dalvikvm32
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/libartd.so b/libunwindstack/tests/files/offline/jit_debug_x86/libartd.so
deleted file mode 100644
index 92ed991..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_x86/libartd.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.so
deleted file mode 100644
index 5efae02..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/libc.so b/libunwindstack/tests/files/offline/jit_debug_x86/libc.so
deleted file mode 100644
index 9c78790..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_x86/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/jit_map0.so b/libunwindstack/tests/files/offline/jit_map_arm/jit_map0.so
deleted file mode 100644
index e667883..0000000
--- a/libunwindstack/tests/files/offline/jit_map_arm/jit_map0.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/jit_map1.so b/libunwindstack/tests/files/offline/jit_map_arm/jit_map1.so
deleted file mode 100644
index 9a1d714..0000000
--- a/libunwindstack/tests/files/offline/jit_map_arm/jit_map1.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/libart.so b/libunwindstack/tests/files/offline/jit_map_arm/libart.so
deleted file mode 100644
index 09ba495..0000000
--- a/libunwindstack/tests/files/offline/jit_map_arm/libart.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/libc.so b/libunwindstack/tests/files/offline/jit_map_arm/libc.so
deleted file mode 100644
index 39c9025..0000000
--- a/libunwindstack/tests/files/offline/jit_map_arm/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.so b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.so
deleted file mode 100644
index 7bb7156..0000000
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64 b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64
deleted file mode 100644
index 00a3896..0000000
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test
deleted file mode 100644
index 3a75b8f..0000000
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso
deleted file mode 100644
index 4940916..0000000
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so
deleted file mode 100644
index 63383d0..0000000
--- a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests
deleted file mode 100644
index a30e599..0000000
--- a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/libc.so b/libunwindstack/tests/files/offline/offset_arm/libc.so
deleted file mode 100644
index 9f5c8ca..0000000
--- a/libunwindstack/tests/files/offline/offset_arm/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
deleted file mode 100644
index 7a30bfa..0000000
--- a/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/pauth_pc_arm64/libc.so b/libunwindstack/tests/files/offline/pauth_pc_arm64/libc.so
deleted file mode 100644
index def2b02..0000000
--- a/libunwindstack/tests/files/offline/pauth_pc_arm64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/pauth_pc_arm64/toybox b/libunwindstack/tests/files/offline/pauth_pc_arm64/toybox
deleted file mode 100644
index 1d33935..0000000
--- a/libunwindstack/tests/files/offline/pauth_pc_arm64/toybox
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so
deleted file mode 100644
index 20008fd..0000000
--- a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64 b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64
deleted file mode 100644
index b90933b..0000000
--- a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so
deleted file mode 100644
index 205ebd4..0000000
--- a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so
deleted file mode 100644
index 20008fd..0000000
--- a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64 b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64
deleted file mode 100644
index b90933b..0000000
--- a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so
deleted file mode 100644
index 205ebd4..0000000
--- a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
deleted file mode 100644
index cac1dd9..0000000
--- a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/libc.so b/libunwindstack/tests/files/offline/signal_fde_x86/libc.so
deleted file mode 100644
index 5c882e4..0000000
--- a/libunwindstack/tests/files/offline/signal_fde_x86/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/libunwindstack_test b/libunwindstack/tests/files/offline/signal_fde_x86/libunwindstack_test
deleted file mode 100644
index 8dcff67..0000000
--- a/libunwindstack/tests/files/offline/signal_fde_x86/libunwindstack_test
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/libc.so b/libunwindstack/tests/files/offline/signal_fde_x86_64/libc.so
deleted file mode 100644
index cea7336..0000000
--- a/libunwindstack/tests/files/offline/signal_fde_x86_64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/libunwindstack_test b/libunwindstack/tests/files/offline/signal_fde_x86_64/libunwindstack_test
deleted file mode 100644
index c48e84e..0000000
--- a/libunwindstack/tests/files/offline/signal_fde_x86_64/libunwindstack_test
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/libc.so b/libunwindstack/tests/files/offline/signal_load_bias_arm/libc.so
deleted file mode 100644
index f046624..0000000
--- a/libunwindstack/tests/files/offline/signal_load_bias_arm/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/libunwindstack_unit_test b/libunwindstack/tests/files/offline/signal_load_bias_arm/libunwindstack_unit_test
deleted file mode 100644
index f460dd6..0000000
--- a/libunwindstack/tests/files/offline/signal_load_bias_arm/libunwindstack_unit_test
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm/libbase.so b/libunwindstack/tests/files/offline/straddle_arm/libbase.so
deleted file mode 100644
index d1f16ee..0000000
--- a/libunwindstack/tests/files/offline/straddle_arm/libbase.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm/libc.so b/libunwindstack/tests/files/offline/straddle_arm/libc.so
deleted file mode 100644
index 4dc19ca..0000000
--- a/libunwindstack/tests/files/offline/straddle_arm/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test b/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test
deleted file mode 100644
index 092fc3a..0000000
--- a/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.h b/libunwindstack/tests/fuzz/UnwinderComponentCreator.h
index fd3499a..8486418 100644
--- a/libunwindstack/tests/fuzz/UnwinderComponentCreator.h
+++ b/libunwindstack/tests/fuzz/UnwinderComponentCreator.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
-#define _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
+#pragma once
 
 #include <elf.h>
 #include <sys/mman.h>
@@ -36,7 +35,7 @@
 #include <unwindstack/RegsX86_64.h>
 
 #include "../ElfFake.h"
-#include "../MemoryFake.h"
+#include "utils/MemoryFake.h"
 
 #include "fuzzer/FuzzedDataProvider.h"
 
@@ -81,4 +80,3 @@
                                                    std::shared_ptr<unwindstack::Memory> memory,
                                                    uint max_libraries, uint max_library_length,
                                                    unwindstack::ArchEnum arch);
-#endif  // _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
diff --git a/libunwindstack/tests/fuzz/UnwinderFuzz.cpp b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
index f926ce7..41913f1 100644
--- a/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
+++ b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
@@ -23,9 +23,9 @@
 #include <unwindstack/Memory.h>
 #include <unwindstack/Unwinder.h>
 
-#include "../MemoryFake.h"
 #include "UnwinderComponentCreator.h"
 #include "fuzzer/FuzzedDataProvider.h"
+#include "utils/MemoryFake.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tools/share_common_elfs.sh b/libunwindstack/tools/share_common_elfs.sh
new file mode 100755
index 0000000..d6e79a1
--- /dev/null
+++ b/libunwindstack/tools/share_common_elfs.sh
@@ -0,0 +1,119 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 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.
+
+### Usage: shares_common_elfs.sh [-m] [-a] <path/to/dump/directory/or/elf>
+###
+### This script is used to eliminate copies of the same ELF file(s) across
+### subdirectories of 'offline_files/' by placing common ELF(s) in
+### 'offline_files/common'. The build id is appended to each ELF name to allow
+### for different versions of the same shared library. Unless -a is used, the
+### path must be a valid ELF file.
+###
+### Options:
+### -m
+###    If the ELF(s) does not currently reside in the 'common' directory, move
+###     the elf to 'common' and rename it.
+### -a
+###    Eliminate copies for all ELFs in a directory. The path specified point
+###    to a directory and NOT an individual ELF file.
+
+usage() {
+  grep '^###' $0 | sed -e 's/^###//'
+  exit 1
+}
+
+update_elf() {
+  local elf_path="$1"
+  local move_if_not_in_common="$2"
+
+  local basename=$(basename "$elf_path")
+  local maps_path=$(dirname "$elf_path")"/maps.txt"
+  local build_id=$(readelf -n "$elf_path" | grep -oP 'Build ID: \K([\w\d]+)')
+  local new_elf_name="${basename}_${build_id}"
+  local common_dir="${ANDROID_BUILD_TOP}/system/unwinding/libunwindstack/offline_files/common/"
+
+  if [[ -f "${common_dir}${new_elf_name}" ]]; then
+    # If the ELF is already found in the common directory, delete the local copy.
+    git rm "$elf_path"
+  elif [[ $move_if_not_in_common == true ]]; then
+    # If the ELF is not found in the common directory and the `move_if_not_common`
+    # flag was set, move the local ELF to the common directory.
+    git mv "$elf_path" "${common_dir}${new_elf_name}"
+  else
+    # The ELF was not found in the common directory so exit this function.
+    return 0
+  fi
+
+  # Replace the name of the elf we just deleted/moved to the relative
+  # path to that ELF in the common directory.
+  local elf_dir_path=$(dirname "$elf_path")
+  local rel_path_to_common=$(realpath --relative-to="$elf_dir_path" "$common_dir")
+  sed -i -e "s/${basename}/${rel_path_to_common}\/${new_elf_name}/g" "$maps_path"
+}
+
+is_an_elf() {
+  if [[ $(head -c 4 $1 | cut -c2-) == "ELF" ]]; then
+    echo true
+  else
+    echo false
+  fi
+}
+
+main () {
+  set -e # abort the script if some error occurs.
+  local move_if_not_in_common=false
+  local update_all_elfs=false
+  while getopts ":hma" arg; do
+    case $arg in
+      m)
+        move_if_not_in_common=true
+        ;;
+      a)
+        update_all_elfs=true
+        ;;
+      h | *)
+        usage
+        ;;
+    esac
+  done
+  path=${@:$OPTIND:1}
+  if [[ -z $path ]]; then
+    usage
+  fi
+
+  if [[ $update_all_elfs == true ]]; then
+    if [[ ! -d $path || "${path: -1}" != "/" ]]; then
+      echo "$path is not a valid path to a directory." >&2
+      usage
+    fi
+    for elf_path in "${path}"*; do
+      if [[ $(is_an_elf $elf_path) == true ]]; then
+        update_elf $elf_path $move_if_not_in_common
+      fi
+    done
+  else
+    if [[ ! -f $path ]]; then
+      echo "$path is not a valid path to a file." >&2
+      usage
+    elif [[ $(is_an_elf $path) == false ]]; then
+      echo "$path is not a valid ELF file." >&2
+    else
+      update_elf $path $move_if_not_in_common
+    fi
+  fi
+}
+
+main "$@"
\ No newline at end of file
diff --git a/libunwindstack/tools/strip.py b/libunwindstack/tools/strip.py
new file mode 100755
index 0000000..bb47de7
--- /dev/null
+++ b/libunwindstack/tools/strip.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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.
+#
+
+import argparse, subprocess, re, os, glob, array, gzip
+
+DESCRIPTION = "This tool reduces ELF size using stripping and compression"
+
+STRIP_SECTIONS = [".text", ".rodata"]
+
+READELF_FORMAT = """
+  \s+(?P<index>[0-9\[\] ]+)
+  \s+(?P<name>[a-z_.]+)
+  \s+(?P<type>[A-Z_]+)
+  \s+(?P<address>[0-9a-f]+)
+  \s+(?P<offset>[0-9a-f]+)
+  \s+(?P<size>[0-9a-f]+)
+"""
+
+def strip(path):
+  proc = subprocess.run(["readelf", "--file-header", "--sections", path],
+                        stdout=subprocess.PIPE, universal_newlines=True)
+  assert(proc.returncode == 0)  # readelf command failed
+  sections = {m["name"] : m for m in re.finditer(READELF_FORMAT, proc.stdout, re.VERBOSE)}
+  for name in STRIP_SECTIONS:
+    if name == ".text" and os.path.basename(path) in ["vdso", "vdso.so", "libc.so"]:
+      continue  # Stripping these libraries breaks signal handler unwinding.
+    section = sections.get(name)
+    if not section:
+      print("Warning: {} not found in {}".format(name, path))
+    if section and section["type"] != "NOBITS":
+      offset, size = int(section["offset"], 16), int(section["size"], 16) & ~1
+      with open(path, "r+b") as f:
+        f.seek(offset)
+        data = array.array('H')  # 16-bit unsigned integer array.
+        data.frombytes(f.read(size))
+        # Preserve top bits for thumb so that we can still determine instruction size.
+        is_thumb = (name == ".text" and re.search("Machine:\s+ARM", proc.stdout))
+        for i in range(len(data)):
+          data[i] = 0xffff if is_thumb and (data[i] & 0xe000) == 0xe000 else 0
+        f.seek(offset)
+        f.write(data.tobytes())
+
+  # gzip-compress the file to take advantage of the zeroed sections.
+  with open(path, 'rb') as src, gzip.open(path + ".gz", 'wb') as dst:
+    dst.write(src.read())
+  os.remove(path)
+
+def main():
+  parser = argparse.ArgumentParser(description=DESCRIPTION)
+  parser.add_argument('target', nargs='+', help="ELF file or whole directory to strip")
+  args = parser.parse_args()
+
+  for path in args.target:
+    if os.path.isdir(path):
+      for path in glob.glob(os.path.join(path, "**/*"), recursive=True):
+        if os.path.isfile(path) and open(path, "rb").read(4) == b"\x7FELF":
+          strip(path)
+    else:
+      strip(path)
+
+if __name__ == '__main__':
+  main()
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 4d3a5f4..4a2bf65 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -14,20 +14,14 @@
  * limitations under the License.
  */
 
+#include <cstdio>
 #define _GNU_SOURCE 1
-#include <errno.h>
 #include <inttypes.h>
-#include <signal.h>
-#include <stdint.h>
 #include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
 #include <sys/mman.h>
-#include <sys/ptrace.h>
-#include <sys/types.h>
-#include <unistd.h>
 
-#include <algorithm>
+#include <cstdlib>
+#include <filesystem>
 #include <memory>
 #include <string>
 #include <unordered_map>
@@ -36,13 +30,21 @@
 
 #include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
+#include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
 #include <unwindstack/Unwinder.h>
+#include "utils/ProcessTracer.h"
 
+#include <android-base/file.h>
+#include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 
+namespace {
+constexpr pid_t kMinPid = 1;
+constexpr int kAllCmdOptionsParsed = -1;
+
 struct map_info_t {
   uint64_t start;
   uint64_t end;
@@ -51,26 +53,46 @@
   std::string name;
 };
 
-static bool Attach(pid_t pid) {
-  if (ptrace(PTRACE_SEIZE, pid, 0, 0) == -1) {
+int usage(int exit_code) {
+  fprintf(stderr, "USAGE: unwind_for_offline [-t] [-e FILE] [-f[FILE]] <PID>\n\n");
+  fprintf(stderr, "OPTIONS:\n");
+  fprintf(stderr, "-t\n");
+  fprintf(stderr, "       Dump offline snapshot for all threads of <PID>.\n");
+  fprintf(stderr, "-e FILE\n");
+  fprintf(stderr, "       If FILE is a valid ELF file included in /proc/<PID>/maps,\n");
+  fprintf(stderr, "       unwind_for_offline will wait until the current frame (PC)\n");
+  fprintf(stderr, "       lies within the .so file given by FILE. FILE should be\n");
+  fprintf(stderr, "       base name of the path (the component following the final\n");
+  fprintf(stderr, "       '/') rather than the fully qualified path.\n");
+  fprintf(stderr, "-f [FILE]\n");
+  fprintf(stderr, "       Write info (e.g. frames and stack range) logs to a file\n");
+  fprintf(stderr, "       rather than to the stdout/stderr. If FILE is not\n");
+  fprintf(stderr, "       specified, the output file will be named 'output.txt'.\n");
+  return exit_code;
+}
+
+bool EnsureProcInDesiredElf(const std::string& elf_name, unwindstack::ProcessTracer& proc) {
+  if (proc.UsesSharedLibrary(proc.pid(), elf_name)) {
+    printf("Confirmed pid %d does use %s. Waiting for PC to lie within %s...\n", proc.pid(),
+           elf_name.c_str(), elf_name.c_str());
+    if (!proc.StopInDesiredElf(elf_name)) return false;
+  } else {
+    fprintf(stderr, "Process %d does not use library %s.\n", proc.pid(), elf_name.c_str());
     return false;
   }
+  return true;
+}
 
-  if (ptrace(PTRACE_INTERRUPT, pid, 0, 0) == -1) {
-    ptrace(PTRACE_DETACH, pid, 0, 0);
+bool CreateAndChangeDumpDir(std::filesystem::path thread_dir, pid_t tid, bool is_main_thread) {
+  std::string dir_name = std::to_string(tid);
+  if (is_main_thread) dir_name += "_main-thread";
+  thread_dir /= dir_name;
+  if (!std::filesystem::create_directory(thread_dir)) {
+    fprintf(stderr, "Failed to create directory for tid %d\n", tid);
     return false;
   }
-
-  // Allow at least 1 second to attach properly.
-  for (size_t i = 0; i < 1000; i++) {
-    siginfo_t si;
-    if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
-      return true;
-    }
-    usleep(1000);
-  }
-  printf("%d: Failed to stop.\n", pid);
-  return false;
+  std::filesystem::current_path(thread_dir);
+  return true;
 }
 
 bool SaveRegs(unwindstack::Regs* regs) {
@@ -86,7 +108,8 @@
   return true;
 }
 
-bool SaveStack(pid_t pid, const std::vector<std::pair<uint64_t, uint64_t>>& stacks) {
+bool SaveStack(pid_t pid, const std::vector<std::pair<uint64_t, uint64_t>>& stacks,
+               FILE* output_fp) {
   for (size_t i = 0; i < stacks.size(); i++) {
     std::string file_name;
     if (stacks.size() != 1) {
@@ -101,11 +124,11 @@
     std::vector<uint8_t> buffer(sp_end - sp_start);
     auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
     if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
-      printf("Unable to read stack data.\n");
+      fprintf(stderr, "Unable to read stack data.\n");
       return false;
     }
 
-    printf("Saving the stack 0x%" PRIx64 "-0x%" PRIx64 "\n", sp_start, sp_end);
+    fprintf(output_fp, "\nSaving the stack 0x%" PRIx64 "-0x%" PRIx64 "\n", sp_start, sp_end);
 
     std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file_name.c_str(), "w+"), &fclose);
     if (fp == nullptr) {
@@ -115,14 +138,15 @@
 
     size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
     if (bytes != sizeof(sp_start)) {
-      printf("Failed to write sp_start data: sizeof(sp_start) %zu, written %zu\n", sizeof(sp_start),
-             bytes);
+      fprintf(stderr, "Failed to write sp_start data: sizeof(sp_start) %zu, written %zu\n",
+              sizeof(sp_start), bytes);
       return false;
     }
 
     bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
     if (bytes != buffer.size()) {
-      printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes);
+      fprintf(stderr, "Failed to write all stack data: stack size %zu, written %zu\n",
+              buffer.size(), bytes);
       return false;
     }
   }
@@ -135,7 +159,8 @@
   if (info->name.empty()) {
     cur_name = android::base::StringPrintf("anonymous_%" PRIx64, info->start);
   } else {
-    cur_name = android::base::StringPrintf("%s_%" PRIx64, basename(info->name.c_str()), info->start);
+    cur_name = android::base::StringPrintf(
+        "%s_%" PRIx64, android::base::Basename(info->name).c_str(), info->start);
   }
 
   std::vector<uint8_t> buffer(info->end - info->start);
@@ -143,7 +168,8 @@
   // map, so read all that is readable.
   size_t bytes = memory->Read(info->start, buffer.data(), buffer.size());
   if (bytes == 0) {
-    printf("Cannot read data from address %" PRIx64 " length %zu\n", info->start, buffer.size());
+    fprintf(stderr, "Cannot read data from address %" PRIx64 " length %zu\n", info->start,
+            buffer.size());
     return false;
   }
 
@@ -155,7 +181,8 @@
 
   size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
   if (bytes_written != bytes) {
-    printf("Failed to write all data to file: bytes read %zu, written %zu\n", bytes, bytes_written);
+    fprintf(stderr, "Failed to write all data to file: bytes read %zu, written %zu\n", bytes,
+            bytes_written);
     return false;
   }
 
@@ -166,7 +193,7 @@
 }
 
 bool CopyElfFromFile(map_info_t* info, bool* file_copied) {
-  std::string cur_name = basename(info->name.c_str());
+  std::string cur_name = android::base::Basename(info->name);
   if (*file_copied) {
     info->name = cur_name;
     return true;
@@ -188,8 +215,8 @@
   while ((bytes = fread(buffer.data(), 1, buffer.size(), fp.get())) > 0) {
     size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
     if (bytes_written != bytes) {
-      printf("Bytes written doesn't match bytes read: read %zu, written %zu\n", bytes,
-             bytes_written);
+      fprintf(stderr, "Bytes written doesn't match bytes read: read %zu, written %zu\n", bytes,
+              bytes_written);
       return false;
     }
   }
@@ -225,29 +252,33 @@
     return;
   }
 
-  printf("Cannot save memory or file for map ");
+  fprintf(stderr, "Cannot save memory or file for map ");
   if (!info->name.empty()) {
-    printf("%s\n", info->name.c_str());
+    fprintf(stderr, "%s\n", info->name.c_str());
   } else {
-    printf("anonymous:%" PRIx64 "\n", info->start);
+    fprintf(stderr, "anonymous:%" PRIx64 "\n", info->start);
   }
 }
 
-int SaveData(pid_t pid) {
-  unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
+bool SaveData(pid_t tid, const std::filesystem::path& cwd, bool is_main_thread, FILE* output_fp) {
+  fprintf(output_fp, "-------------------- tid = %d %s--------------------\n", tid,
+          is_main_thread ? "(main thread) " : "--------------");
+  unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(tid);
   if (regs == nullptr) {
-    printf("Unable to get remote reg data.\n");
-    return 1;
+    fprintf(stderr, "Unable to get remote reg data.\n");
+    return false;
   }
 
+  if (!CreateAndChangeDumpDir(cwd, tid, is_main_thread)) return false;
+
   // Save the current state of the registers.
   if (!SaveRegs(regs)) {
-    return 1;
+    return false;
   }
 
   // Do an unwind so we know how much of the stack to save, and what
   // elf files are involved.
-  unwindstack::UnwinderFromPid unwinder(1024, pid);
+  unwindstack::UnwinderFromPid unwinder(1024, tid);
   unwinder.SetRegs(regs);
   uint64_t sp = regs->sp();
   unwinder.Unwind();
@@ -256,7 +287,7 @@
   std::vector<std::pair<uint64_t, uint64_t>> stacks;
   unwindstack::Maps* maps = unwinder.GetMaps();
   uint64_t sp_map_start = 0;
-  unwindstack::MapInfo* map_info = maps->Find(sp);
+  auto map_info = maps->Find(sp);
   if (map_info != nullptr) {
     stacks.emplace_back(std::make_pair(sp, map_info->end()));
     sp_map_start = map_info->start();
@@ -269,35 +300,34 @@
       sp_map_start = map_info->start();
     }
 
-    if (maps_by_start.count(frame.map_start) == 0) {
-      map_info = maps->Find(frame.map_start);
+    if (maps_by_start.count(frame.map_info->start()) == 0) {
       if (map_info == nullptr) {
         continue;
       }
 
-      auto info = FillInAndGetMapInfo(maps_by_start, map_info);
+      auto info = FillInAndGetMapInfo(maps_by_start, map_info.get());
       bool file_copied = false;
       SaveMapInformation(unwinder.GetProcessMemory(), info, &file_copied);
 
       // If you are using a a linker that creates two maps (one read-only, one
       // read-executable), it's necessary to capture the previous map
       // information if needed.
-      unwindstack::MapInfo* prev_map = map_info->prev_map();
+      auto prev_map = map_info->prev_map();
       if (prev_map != nullptr && map_info->offset() != 0 && prev_map->offset() == 0 &&
           prev_map->flags() == PROT_READ && map_info->name() == prev_map->name() &&
           maps_by_start.count(prev_map->start()) == 0) {
-        info = FillInAndGetMapInfo(maps_by_start, prev_map);
+        info = FillInAndGetMapInfo(maps_by_start, prev_map.get());
         SaveMapInformation(unwinder.GetProcessMemory(), info, &file_copied);
       }
     }
   }
 
   for (size_t i = 0; i < unwinder.NumFrames(); i++) {
-    printf("%s\n", unwinder.FormatFrame(i).c_str());
+    fprintf(output_fp, "%s\n", unwinder.FormatFrame(i).c_str());
   }
 
-  if (!SaveStack(pid, stacks)) {
-    return 1;
+  if (!SaveStack(tid, stacks, output_fp)) {
+    return false;
   }
 
   std::vector<std::pair<uint64_t, map_info_t>> sorted_maps(maps_by_start.begin(),
@@ -305,8 +335,8 @@
   std::sort(sorted_maps.begin(), sorted_maps.end(),
             [](auto& a, auto& b) { return a.first < b.first; });
 
-  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("maps.txt", "w+"), &fclose);
-  if (fp == nullptr) {
+  std::unique_ptr<FILE, decltype(&fclose)> map_fp(fopen("maps.txt", "w+"), &fclose);
+  if (map_fp == nullptr) {
     perror("Failed to create maps.txt");
     return false;
   }
@@ -323,32 +353,89 @@
     if (map.flags & PROT_EXEC) {
       perms[2] = 'x';
     }
-    fprintf(fp.get(), "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " 00:00 0", map.start, map.end, perms,
-            map.offset);
+    fprintf(map_fp.get(), "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " 00:00 0", map.start, map.end,
+            perms, map.offset);
     if (!map.name.empty()) {
-      fprintf(fp.get(), "   %s", map.name.c_str());
+      fprintf(map_fp.get(), "   %s", map.name.c_str());
     }
-    fprintf(fp.get(), "\n");
+    fprintf(map_fp.get(), "\n");
   }
 
-  return 0;
+  fprintf(output_fp, "------------------------------------------------------------------\n");
+  return true;
 }
+}  // namespace
 
 int main(int argc, char** argv) {
-  if (argc != 2) {
-    printf("Usage: unwind_for_offline <PID>\n");
-    return 1;
+  if (argc < 2) return usage(EXIT_FAILURE);
+
+  bool dump_threads = false;
+  std::string elf_name;
+  std::unique_ptr<FILE, decltype(&fclose)> output_fp(nullptr, &fclose);
+  int opt;
+  while ((opt = getopt(argc, argv, ":te:f::")) != kAllCmdOptionsParsed) {
+    switch (opt) {
+      case 't': {
+        dump_threads = true;
+        break;
+      }
+      case 'e': {
+        elf_name = optarg;
+        if (elf_name == "-f") {
+          fprintf(stderr, "Missing argument for option e.\n");
+          return usage(EXIT_FAILURE);
+        }
+        break;
+      }
+      case 'f': {
+        const std::string& output_filename = optarg != nullptr ? optarg : "output.txt";
+        if (optind == argc - 2) {
+          fprintf(stderr, "Ensure there is no space between '-f' and the filename provided.\n");
+          return usage(EXIT_FAILURE);
+        }
+        output_fp.reset(fopen(output_filename.c_str(), "a"));
+        break;
+      }
+      case '?': {
+        if (isprint(optopt))
+          fprintf(stderr, "Unknown option `-%c'.\n", optopt);
+        else
+          fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
+        return usage(EXIT_FAILURE);
+      }
+      case ':': {
+        fprintf(stderr, "Missing arg for option %c.\n", optopt);
+        return usage(EXIT_FAILURE);
+      }
+      default: {
+        return usage(EXIT_FAILURE);
+      }
+    }
+  }
+  if (optind != argc - 1) return usage(EXIT_FAILURE);
+
+  pid_t pid;
+  if (!android::base::ParseInt(argv[optind], &pid, kMinPid, std::numeric_limits<pid_t>::max()))
+    return usage(EXIT_FAILURE);
+
+  unwindstack::ProcessTracer proc(pid, dump_threads);
+  if (!proc.Stop()) return EXIT_FAILURE;
+  if (!elf_name.empty()) {
+    if (!EnsureProcInDesiredElf(elf_name, proc)) return EXIT_FAILURE;
+  }
+  if (!output_fp) output_fp.reset(stdout);
+  std::filesystem::path cwd = std::filesystem::current_path();
+
+  if (!proc.Attach(proc.pid())) return EXIT_FAILURE;
+  if (!SaveData(proc.pid(), cwd, /*is_main_thread=*/proc.IsTracingThreads(), output_fp.get()))
+    return EXIT_FAILURE;
+  if (!proc.Detach(proc.pid())) return EXIT_FAILURE;
+  for (const pid_t& tid : proc.tids()) {
+    if (!proc.Attach(tid)) return EXIT_FAILURE;
+    if (!SaveData(tid, cwd, /*is_main_thread=*/false, output_fp.get())) return EXIT_FAILURE;
+    if (!proc.Detach(tid)) return EXIT_FAILURE;
   }
 
-  pid_t pid = atoi(argv[1]);
-  if (!Attach(pid)) {
-    printf("Failed to attach to pid %d: %s\n", pid, strerror(errno));
-    return 1;
-  }
-
-  int return_code = SaveData(pid);
-
-  ptrace(PTRACE_DETACH, pid, 0, 0);
-
-  return return_code;
+  printf("\nSuccess!\n");
+  return EXIT_SUCCESS;
 }
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 65b65aa..adc93b4 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -103,9 +103,6 @@
 }
 
 int GetElfInfo(const char* file, uint64_t offset) {
-  // Send all log messages to stdout.
-  log_to_stdout(true);
-
   Elf elf(Memory::CreateFileMemory(file, offset).release());
   if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", file);
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 3675f96..da34dc8 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -154,7 +154,6 @@
 
   ArmExidx arm(nullptr, interface->memory(), nullptr);
 
-  log_to_stdout(true);
   arm.set_log(ARM_LOG_BY_REG);
   arm.set_log_skip_execution(true);
   arm.set_log_indent(1);
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index 2506fc1..a56ed5f 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -56,9 +56,6 @@
     }
   }
 
-  // Send all log messages to stdout.
-  unwindstack::log_to_stdout(true);
-
   unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(argv[1], 0).release());
   if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", argv[1]);
diff --git a/libunwindstack/utils/DwarfSectionImplFake.h b/libunwindstack/utils/DwarfSectionImplFake.h
new file mode 100644
index 0000000..54ed995
--- /dev/null
+++ b/libunwindstack/utils/DwarfSectionImplFake.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class DwarfSectionImplFake : public DwarfSectionImpl<TypeParam> {
+ public:
+  DwarfSectionImplFake(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
+  virtual ~DwarfSectionImplFake() = default;
+
+  bool Init(uint64_t, uint64_t, int64_t) override { return false; }
+
+  void GetFdes(std::vector<const DwarfFde*>*) override {}
+
+  const DwarfFde* GetFdeFromPc(uint64_t) override { return nullptr; }
+
+  uint64_t GetCieOffsetFromFde32(uint32_t) { return 0; }
+
+  uint64_t GetCieOffsetFromFde64(uint64_t) { return 0; }
+
+  uint64_t AdjustPcFromFde(uint64_t) override { return 0; }
+
+  void FakeSetCachedCieLocRegs(uint64_t offset, const DwarfLocations& loc_regs) {
+    this->cie_loc_regs_[offset] = loc_regs;
+  }
+  void FakeClearError() { this->last_error_.code = DWARF_ERROR_NONE; }
+};
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/utils/MemoryFake.cpp
similarity index 97%
rename from libunwindstack/tests/MemoryFake.cpp
rename to libunwindstack/utils/MemoryFake.cpp
index 5695dfc..573285b 100644
--- a/libunwindstack/tests/MemoryFake.cpp
+++ b/libunwindstack/utils/MemoryFake.cpp
@@ -41,7 +41,7 @@
     if (value != data_.end()) {
       value->second = src[i];
     } else {
-      data_.insert({ addr, src[i] });
+      data_.insert({addr, src[i]});
     }
   }
 }
@@ -58,4 +58,4 @@
   return size;
 }
 
-}  // namespace unwindstack
+}  // namespace unwindstack
\ No newline at end of file
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/utils/MemoryFake.h
similarity index 76%
rename from libunwindstack/tests/MemoryFake.h
rename to libunwindstack/utils/MemoryFake.h
index 20610a5..b7adfd9 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/utils/MemoryFake.h
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
-#define _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
+#pragma once
 
 #include <stdint.h>
 
 #include <string>
-#include <vector>
 #include <unordered_map>
+#include <vector>
 
 #include <unwindstack/Memory.h>
 
@@ -38,21 +37,13 @@
 
   void SetMemoryBlock(uint64_t addr, size_t length, uint8_t value);
 
-  void SetData8(uint64_t addr, uint8_t value) {
-    SetMemory(addr, &value, sizeof(value));
-  }
+  void SetData8(uint64_t addr, uint8_t value) { SetMemory(addr, &value, sizeof(value)); }
 
-  void SetData16(uint64_t addr, uint16_t value) {
-    SetMemory(addr, &value, sizeof(value));
-  }
+  void SetData16(uint64_t addr, uint16_t value) { SetMemory(addr, &value, sizeof(value)); }
 
-  void SetData32(uint64_t addr, uint32_t value) {
-    SetMemory(addr, &value, sizeof(value));
-  }
+  void SetData32(uint64_t addr, uint32_t value) { SetMemory(addr, &value, sizeof(value)); }
 
-  void SetData64(uint64_t addr, uint64_t value) {
-    SetMemory(addr, &value, sizeof(value));
-  }
+  void SetData64(uint64_t addr, uint64_t value) { SetMemory(addr, &value, sizeof(value)); }
 
   void SetMemory(uint64_t addr, std::vector<uint8_t> values) {
     SetMemory(addr, values.data(), values.size());
@@ -80,5 +71,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/utils/OfflineUnwindUtils.cpp b/libunwindstack/utils/OfflineUnwindUtils.cpp
new file mode 100644
index 0000000..8cc544d
--- /dev/null
+++ b/libunwindstack/utils/OfflineUnwindUtils.cpp
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2021 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 <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <cstddef>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <regex>
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <zlib.h>
+
+#include <unwindstack/Arch.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MachineX86.h>
+#include <unwindstack/MachineX86_64.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/Unwinder.h>
+
+#include "Check.h"
+#include "MemoryOffline.h"
+#include "utils/MemoryFake.h"
+
+#include "OfflineUnwindUtils.h"
+
+namespace unwindstack {
+
+void DecompressFiles(const std::string& directory) {
+  namespace fs = std::filesystem;
+  for (const auto& file : fs::recursive_directory_iterator(directory)) {
+    fs::path src_path = file.path();
+    if (src_path.extension() == ".gz") {
+      fs::path dst_path = fs::path(src_path).replace_extension();  // Remove .gz extension.
+      if (!fs::exists(dst_path) || fs::last_write_time(src_path) > fs::last_write_time(dst_path)) {
+        gzFile src = gzopen(src_path.c_str(), "rb");
+        CHECK(src != nullptr);
+        fs::path tmp_path = fs::path(src_path).replace_extension("." + std::to_string(getpid()));
+        std::ofstream tmp(tmp_path);  // Temporary file to avoid races between unit tests.
+        char buffer[1024];
+        int size;
+        while ((size = gzread(src, buffer, sizeof(buffer))) > 0) {
+          tmp.write(buffer, size);
+        }
+        tmp.close();
+        gzclose(src);
+        fs::rename(tmp_path, dst_path);
+      }
+    }
+  }
+}
+
+void CreateLinks(const std::string& directory) {
+  namespace fs = std::filesystem;
+  for (const auto& file : fs::recursive_directory_iterator(directory)) {
+    fs::path src_path = file.path();
+    if (fs::is_regular_file(src_path) && src_path.filename() == "links.txt") {
+      std::string contents;
+      if (!android::base::ReadFileToString(src_path.c_str(), &contents)) {
+        errx(1, "Unable to read file: %s", src_path.c_str());
+      }
+      fs::path parent_path = src_path.parent_path();
+      std::vector<std::string> lines(android::base::Split(contents, "\n"));
+      for (auto line : lines) {
+        std::string trimmed_line(android::base::Trim(line));
+        if (trimmed_line.empty()) {
+          continue;
+        }
+
+        std::vector<std::string> values(android::base::Split(trimmed_line, " "));
+        if (values.size() != 2) {
+          errx(1, "Invalid line in %s: line %s", src_path.c_str(), line.c_str());
+        }
+
+        // Create the symlink if it doesn't already exist.
+        fs::path target(parent_path);
+        target /= fs::path(values[0]);
+        fs::path source(parent_path);
+        source /= fs::path(values[1]);
+        if (!fs::exists(source)) {
+          // Ignore any errors, if this is running at the same time
+          // in multiple processes, then this might fail.
+          std::error_code ec;
+          fs::create_symlink(target, source, ec);
+        }
+      }
+    }
+  }
+}
+
+std::string GetOfflineFilesDirectory() {
+  std::string path = android::base::GetExecutableDirectory() + "/offline_files/";
+  DecompressFiles(path);
+  CreateLinks(path);
+  return path;
+}
+
+std::string DumpFrames(const Unwinder& unwinder) {
+  std::string str;
+  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+    str += unwinder.FormatFrame(i) + "\n";
+  }
+  return str;
+}
+
+bool AddMemory(std::string file_name, MemoryOfflineParts* parts, std::string* error_msg) {
+  MemoryOffline* memory = new MemoryOffline;
+  if (!memory->Init(file_name.c_str(), 0)) {
+    std::stringstream err_stream;
+    err_stream << "Failed to add stack '" << file_name << "' to stack memory.";
+    *error_msg = err_stream.str();
+    return false;
+  }
+  parts->Add(memory);
+
+  return true;
+}
+
+Regs* OfflineUnwindUtils::GetRegs(const std::string& initial_sample_name) const {
+  const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+  std::string error_msg;
+  if (!IsValidUnwindSample(sample_name, &error_msg)) {
+    std::cerr << error_msg;
+    return nullptr;
+  }
+  return samples_.at(sample_name).regs.get();
+}
+
+Maps* OfflineUnwindUtils::GetMaps(const std::string& initial_sample_name) const {
+  const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+  std::string error_msg;
+  if (!IsValidUnwindSample(sample_name, &error_msg)) {
+    std::cerr << error_msg;
+    return nullptr;
+  }
+  return samples_.at(sample_name).maps.get();
+}
+
+std::shared_ptr<Memory> OfflineUnwindUtils::GetProcessMemory(
+    const std::string& initial_sample_name) const {
+  const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+  std::string error_msg;
+  if (!IsValidUnwindSample(sample_name, &error_msg)) {
+    std::cerr << error_msg;
+    return nullptr;
+  }
+  return samples_.at(sample_name).process_memory;
+}
+
+JitDebug* OfflineUnwindUtils::GetJitDebug(const std::string& initial_sample_name) const {
+  const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+  std::string error_msg;
+  if (!IsValidUnwindSample(sample_name, &error_msg)) {
+    std::cerr << error_msg;
+    return nullptr;
+  }
+  return samples_.at(sample_name).jit_debug.get();
+}
+
+const std::string* OfflineUnwindUtils::GetOfflineFilesPath(
+    const std::string& initial_sample_name) const {
+  const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+  std::string error_msg;
+  if (!IsValidUnwindSample(sample_name, &error_msg)) {
+    std::cerr << error_msg;
+    return nullptr;
+  }
+  return &samples_.at(sample_name).offline_files_path;
+}
+
+const std::string* OfflineUnwindUtils::GetFrameInfoFilepath(
+    const std::string& initial_sample_name) const {
+  const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+  std::string error_msg;
+  if (!IsValidUnwindSample(sample_name, &error_msg)) {
+    std::cerr << error_msg;
+    return nullptr;
+  }
+  return &samples_.at(sample_name).frame_info_filepath;
+}
+
+bool OfflineUnwindUtils::Init(const std::vector<UnwindSampleInfo>& sample_infos,
+                              std::string* error_msg) {
+  // Save the current path so the caller can switch back to it later.
+  cwd_ = std::filesystem::current_path();
+
+  // Fill in the unwind samples.
+  std::stringstream err_stream;
+  for (const auto& sample_info : sample_infos) {
+    std::string offline_files_full_path =
+        GetOfflineFilesDirectory() + sample_info.offline_files_dir;
+    if (!std::filesystem::exists(offline_files_full_path)) {
+      err_stream << "Offline files directory '" << offline_files_full_path << "' does not exist.";
+      *error_msg = err_stream.str();
+      return false;
+    }
+    std::string frame_info_filepath = offline_files_full_path + sample_info.frame_info_filename;
+
+    std::string map_buffer;
+    if (!android::base::ReadFileToString((offline_files_full_path + "maps.txt"), &map_buffer)) {
+      err_stream << "Failed to read from '" << offline_files_full_path << "maps.txt' into memory.";
+      *error_msg = err_stream.str();
+      return false;
+    }
+
+    // CreateMaps, CreatRegs, and Create*Memory may need to be called later by the client. So we
+    // need to create the sample now in case the flags are set to call these methods in Init.
+    const std::string& sample_name = sample_info.offline_files_dir;
+    samples_.emplace(sample_name, (UnwindSample){
+                                      std::move(offline_files_full_path),
+                                      std::move(frame_info_filepath), std::move(map_buffer),
+                                      nullptr,                         // regs
+                                      nullptr,                         // maps
+                                      std::make_shared<MemoryFake>(),  // process_memory
+                                      nullptr,                         // jit_debug
+                                  });
+    UnwindSample& sample = samples_.at(sample_name);
+
+    if (sample_info.create_maps) {
+      if (!CreateMaps(error_msg, sample_name)) return false;
+    }
+    if (!CreateRegs(sample_info.arch, error_msg, sample_name)) return false;
+
+    switch (sample_info.memory_flag) {
+      case ProcessMemoryFlag::kNone: {
+        if (!CreateProcessMemory(error_msg, sample_name)) return false;
+        break;
+      }
+      case ProcessMemoryFlag::kIncludeJitMemory: {
+        if (!CreateProcessMemory(error_msg, sample_name)) return false;
+        sample.jit_debug = CreateJitDebug(sample.regs->Arch(), sample.process_memory);
+        break;
+      }
+      case ProcessMemoryFlag::kNoMemory: {
+        break;
+      }
+      default: {
+        std::stringstream err_stream;
+        err_stream << "Unknown memory type for sample '" << sample_name << "'.";
+        *error_msg = err_stream.str();
+        return false;
+      }
+    }
+  }
+  initted_ = true;
+  return true;
+}
+
+bool OfflineUnwindUtils::Init(const UnwindSampleInfo& sample_info, std::string* error_msg) {
+  if (Init(std::vector<UnwindSampleInfo>{sample_info}, error_msg)) {
+    if (!ChangeToSampleDirectory(error_msg)) return false;
+    return true;
+  }
+  return false;
+}
+
+bool OfflineUnwindUtils::ChangeToSampleDirectory(std::string* error_msg,
+                                                 const std::string& initial_sample_name) const {
+  if (!initted_) {
+    *error_msg =
+        "Cannot change to sample directory because OfflineUnwindUtils::Init has not been called.";
+    return false;
+  }
+  const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+  if (!IsValidUnwindSample(sample_name, error_msg)) return false;
+
+  std::filesystem::current_path(std::filesystem::path(samples_.at(sample_name).offline_files_path));
+  return true;
+}
+
+bool OfflineUnwindUtils::GetExpectedNumFrames(size_t* expected_num_frames, std::string* error_msg,
+                                              const std::string& initial_sample_name) const {
+  if (!initted_) {
+    *error_msg =
+        "Cannot get expected number of frames of a sample because OfflineUnwindUtils::Init has not "
+        "been called.";
+    return false;
+  }
+  const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+  if (!IsValidUnwindSample(sample_name, error_msg)) return false;
+
+  const std::string& sample_frames_path = samples_.at(sample_name).frame_info_filepath;
+  if (!std::filesystem::exists(sample_frames_path)) {
+    std::stringstream err_stream;
+    err_stream << "Offline files directory '" << sample_frames_path << "' does not exist.";
+    *error_msg = err_stream.str();
+    return false;
+  }
+
+  std::ifstream in(sample_frames_path);
+  in.unsetf(std::ios_base::skipws);  // Ensure that we do not skip newlines.
+  *expected_num_frames =
+      std::count(std::istream_iterator<char>(in), std::istream_iterator<char>(), '\n');
+
+  return true;
+}
+
+bool OfflineUnwindUtils::CreateMaps(std::string* error_msg,
+                                    const std::string& initial_sample_name) {
+  const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+  if (!IsValidUnwindSample(sample_name, error_msg)) return false;
+  UnwindSample& sample = samples_.at(sample_name);
+
+  sample.maps.reset(new BufferMaps(sample.map_buffer.c_str()));
+  if (!sample.maps->Parse()) {
+    *error_msg = "Failed to parse offline maps.";
+    return false;
+  }
+  return true;
+}
+
+bool OfflineUnwindUtils::CreateProcessMemory(std::string* error_msg,
+                                             const std::string& initial_sample_name) {
+  const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+  if (!IsValidUnwindSample(sample_name, error_msg)) return false;
+  UnwindSample& sample = samples_.at(sample_name);
+
+  // Construct process memory from all descriptor, stack, entry, and jit files
+  auto memory = std::make_unique<MemoryOfflineParts>();
+  bool data_files_found = false;
+  for (const auto& file : std::filesystem::directory_iterator(sample.offline_files_path)) {
+    std::string filename = file.path().string();
+    if (std::regex_match(filename,
+                         std::regex("^(.+)\\/(descriptor|stack|entry|jit)(\\d*)\\.data$"))) {
+      data_files_found = true;
+      if (!AddMemory(filename, memory.get(), error_msg)) return false;
+    }
+  }
+  if (!data_files_found) {
+    *error_msg = "No memory (stack, JIT, etc.) data files found.";
+    return false;
+  }
+
+  sample.process_memory.reset(memory.release());
+  return true;
+}
+
+namespace {
+template <typename AddressType>
+bool ReadRegs(RegsImpl<AddressType>* regs,
+              const std::unordered_map<std::string, uint32_t>& name_to_reg, std::string* error_msg,
+              const std::string& offline_files_path) {
+  std::stringstream err_stream;
+  FILE* fp = fopen((offline_files_path + "regs.txt").c_str(), "r");
+  if (fp == nullptr) {
+    err_stream << "Error opening file '" << offline_files_path << "regs.txt': " << strerror(errno);
+    *error_msg = err_stream.str();
+    return false;
+  }
+
+  while (!feof(fp)) {
+    uint64_t value;
+    char reg_name[100];
+    if (fscanf(fp, "%s %" SCNx64 "\n", reg_name, &value) != 2) {
+      err_stream << "Failed to read in register name/values from '" << offline_files_path
+                 << "regs.txt'.";
+      *error_msg = err_stream.str();
+      return false;
+    }
+    std::string name(reg_name);
+    if (!name.empty()) {
+      // Remove the : from the end.
+      name.resize(name.size() - 1);
+    }
+    auto entry = name_to_reg.find(name);
+    if (entry == name_to_reg.end()) {
+      err_stream << "Unknown register named " << reg_name;
+      *error_msg = err_stream.str();
+      return false;
+    }
+    (*regs)[entry->second] = value;
+  }
+  fclose(fp);
+  return true;
+}
+}  // namespace
+
+bool OfflineUnwindUtils::CreateRegs(ArchEnum arch, std::string* error_msg,
+                                    const std::string& initial_sample_name) {
+  const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+  if (!IsValidUnwindSample(sample_name, error_msg)) return false;
+  auto& regs = samples_.at(sample_name).regs;
+  const auto& offline_files_path = samples_.at(sample_name).offline_files_path;
+
+  switch (arch) {
+    case ARCH_ARM: {
+      RegsArm* regs_impl = new RegsArm;
+      regs.reset(regs_impl);
+      if (!ReadRegs<uint32_t>(regs_impl, arm_regs_, error_msg, offline_files_path)) return false;
+      break;
+    }
+    case ARCH_ARM64: {
+      RegsArm64* regs_impl = new RegsArm64;
+      regs.reset(regs_impl);
+      if (!ReadRegs<uint64_t>(regs_impl, arm64_regs_, error_msg, offline_files_path)) return false;
+      break;
+    }
+    case ARCH_X86: {
+      RegsX86* regs_impl = new RegsX86;
+      regs.reset(regs_impl);
+      if (!ReadRegs<uint32_t>(regs_impl, x86_regs_, error_msg, offline_files_path)) return false;
+      break;
+    }
+    case ARCH_X86_64: {
+      RegsX86_64* regs_impl = new RegsX86_64;
+      regs.reset(regs_impl);
+      if (!ReadRegs<uint64_t>(regs_impl, x86_64_regs_, error_msg, offline_files_path)) return false;
+      break;
+    }
+    default:
+      *error_msg = "Unknown architechture " + std::to_string(arch);
+      return false;
+  }
+
+  return true;
+}
+
+const std::string& OfflineUnwindUtils::GetAdjustedSampleName(
+    const std::string& initial_sample_name) const {
+  // Only return the first entry in the sample map if this is the single unwind use case.
+  // Otherwise return the inputted sample name so we can check if that is a valid sample name.
+  if (initial_sample_name == kSingleSample && samples_.size() == 1) {
+    return samples_.begin()->first;
+  }
+  return initial_sample_name;
+}
+
+bool OfflineUnwindUtils::IsValidUnwindSample(const std::string& sample_name,
+                                             std::string* error_msg) const {
+  if (samples_.find(sample_name) == samples_.end()) {
+    std::stringstream err_stream;
+    err_stream << "Invalid sample name (offline file directory) '" << sample_name << "'.";
+    if (sample_name == kSingleSample) {
+      err_stream << " An explicit sample name must be provided for the multiple unwind use case "
+                    "of OfflineUnwindUtils (i.e. should not use the default sample name).";
+    }
+    *error_msg = err_stream.str();
+    return false;
+  }
+  return true;
+}
+
+std::unordered_map<std::string, uint32_t> OfflineUnwindUtils::arm_regs_ = {
+    {"r0", ARM_REG_R0},  {"r1", ARM_REG_R1}, {"r2", ARM_REG_R2},   {"r3", ARM_REG_R3},
+    {"r4", ARM_REG_R4},  {"r5", ARM_REG_R5}, {"r6", ARM_REG_R6},   {"r7", ARM_REG_R7},
+    {"r8", ARM_REG_R8},  {"r9", ARM_REG_R9}, {"r10", ARM_REG_R10}, {"r11", ARM_REG_R11},
+    {"ip", ARM_REG_R12}, {"sp", ARM_REG_SP}, {"lr", ARM_REG_LR},   {"pc", ARM_REG_PC},
+};
+
+std::unordered_map<std::string, uint32_t> OfflineUnwindUtils::arm64_regs_ = {
+    {"x0", ARM64_REG_R0},      {"x1", ARM64_REG_R1},   {"x2", ARM64_REG_R2},
+    {"x3", ARM64_REG_R3},      {"x4", ARM64_REG_R4},   {"x5", ARM64_REG_R5},
+    {"x6", ARM64_REG_R6},      {"x7", ARM64_REG_R7},   {"x8", ARM64_REG_R8},
+    {"x9", ARM64_REG_R9},      {"x10", ARM64_REG_R10}, {"x11", ARM64_REG_R11},
+    {"x12", ARM64_REG_R12},    {"x13", ARM64_REG_R13}, {"x14", ARM64_REG_R14},
+    {"x15", ARM64_REG_R15},    {"x16", ARM64_REG_R16}, {"x17", ARM64_REG_R17},
+    {"x18", ARM64_REG_R18},    {"x19", ARM64_REG_R19}, {"x20", ARM64_REG_R20},
+    {"x21", ARM64_REG_R21},    {"x22", ARM64_REG_R22}, {"x23", ARM64_REG_R23},
+    {"x24", ARM64_REG_R24},    {"x25", ARM64_REG_R25}, {"x26", ARM64_REG_R26},
+    {"x27", ARM64_REG_R27},    {"x28", ARM64_REG_R28}, {"x29", ARM64_REG_R29},
+    {"sp", ARM64_REG_SP},      {"lr", ARM64_REG_LR},   {"pc", ARM64_REG_PC},
+    {"pst", ARM64_REG_PSTATE},
+};
+
+std::unordered_map<std::string, uint32_t> OfflineUnwindUtils::x86_regs_ = {
+    {"eax", X86_REG_EAX}, {"ebx", X86_REG_EBX}, {"ecx", X86_REG_ECX},
+    {"edx", X86_REG_EDX}, {"ebp", X86_REG_EBP}, {"edi", X86_REG_EDI},
+    {"esi", X86_REG_ESI}, {"esp", X86_REG_ESP}, {"eip", X86_REG_EIP},
+};
+
+std::unordered_map<std::string, uint32_t> OfflineUnwindUtils::x86_64_regs_ = {
+    {"rax", X86_64_REG_RAX}, {"rbx", X86_64_REG_RBX}, {"rcx", X86_64_REG_RCX},
+    {"rdx", X86_64_REG_RDX}, {"r8", X86_64_REG_R8},   {"r9", X86_64_REG_R9},
+    {"r10", X86_64_REG_R10}, {"r11", X86_64_REG_R11}, {"r12", X86_64_REG_R12},
+    {"r13", X86_64_REG_R13}, {"r14", X86_64_REG_R14}, {"r15", X86_64_REG_R15},
+    {"rdi", X86_64_REG_RDI}, {"rsi", X86_64_REG_RSI}, {"rbp", X86_64_REG_RBP},
+    {"rsp", X86_64_REG_RSP}, {"rip", X86_64_REG_RIP},
+};
+
+}  // namespace unwindstack
diff --git a/libunwindstack/utils/OfflineUnwindUtils.h b/libunwindstack/utils/OfflineUnwindUtils.h
new file mode 100644
index 0000000..df89c68
--- /dev/null
+++ b/libunwindstack/utils/OfflineUnwindUtils.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <filesystem>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <unwindstack/Arch.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+#include "MemoryOffline.h"
+
+// These utils facilitate performing offline unwinds. Offline unwinds are similar to local
+// unwinds, however, instead of pausing the process to gather the current execution state
+// (stack, registers, Elf / maps), a snapshot of the process is taken. This snapshot data
+// is used at a later time (when the process is no longer running) to unwind the process
+// at the point the snapshot was taken.
+//
+// Offline unwinds simulate one of the most common use cases of the Unwinder. These types of
+// unwinds are performed by two of the largest clients of libunwindstack: Perfetto and Simpleperf.
+//
+// Offline unwind snapshots were obtained using the following approach:
+// 1. (Optional) Flash a virtual or physical device with the internal Android build rather than
+//    an AOSP build to have additional and more complex apps to unwind.
+// 2. Determine the pid of the app/process you want to unwind. View all of the running
+//    processes with `adb shell ps -A`  or `adb shell ps -A | grep name.of.process` if you know
+//    the (package) name of the process.
+// 3. (Optional) If you want to ensure that an application is compiled or that the compiled code is
+//    erased (e.g. want interpreter / JIT frames in the unwind), run `adb shell cmd package compile`
+//    based on the options provided here
+//    (https://source.android.com/devices/tech/dalvik/jit-compiler).
+// 4. Ensure the process is running and in a "desired state" when you execute
+//    `adb shell /bin/unwind_for_offline [options] pid`. For example:
+//   a. If you are unwinding the bluetooth process and want the unwind to contain the bluetooth
+//      ELF (libbluetooth.so), try to pair with a device over bluetooth. Make sure you use the
+//      `-t` and `-e` flags.
+//   b. You will likely see more variation in the thread snapshots (especially if you are trying
+//      to capture JIT/interpreter frames) if you ensure the app is not-idle when you run
+//      `unwind_for_offline`. E.g. immediately run unwind_for_offline after searching for a
+//      landmark in Google Maps.
+// 5. Grab the desired snapshot directories with `adb pull ...`
+// 6. (Optional) Reduce the size of copied ELFs:
+//   a. Use tools/share_common_elfs.sh to eliminate copies of the same ELF files that are already
+//      used by other 'libunwindstack/offline_files/' subdirectories.
+//   b. Strip ELFs of all sections that are not needed for unwinding and/or symbolization.
+//   c. Compress/Zip the entire snapshot directory.
+// 7. Use the path to the snapshot directory(ies) for the `offline_files_dirs` parameter to
+//    `OfflineUnwindUtils::Init`.
+//
+// See b/192012600 for additional information regarding Offline Unwind Benchmarks.
+namespace unwindstack {
+
+void DecompressFiles(const std::string& directory);
+
+std::string GetOfflineFilesDirectory();
+
+std::string DumpFrames(const Unwinder& unwinder);
+
+bool AddMemory(std::string file_name, MemoryOfflineParts* parts, std::string* error_msg);
+
+// Enum that indicates how `UnwindSample::process_memory` of `OfflineUnwindUtils::samples_`
+// should be initialized.
+enum class ProcessMemoryFlag {
+  kNone = 0,
+  kIncludeJitMemory,
+  kNoMemory,
+};
+
+// A `UnwindSampleInfo` object contains the information necessary for OfflineUnwindUtils
+// to initialize a single offline unwind sample.
+struct UnwindSampleInfo {
+  std::string offline_files_dir;
+  ArchEnum arch;
+  std::string frame_info_filename = "output.txt";
+  ProcessMemoryFlag memory_flag = ProcessMemoryFlag::kNone;
+  bool create_maps = true;
+};
+
+// The `OfflineUnwindUtils` class helps perform offline unwinds by handling the creation of the
+// `Regs`, `Maps`, and `Memory` objects needed for unwinding.
+//
+// `OfflineUnwindUtils` assists in two unwind use cases:
+// 1. Single unwinds: unwind from a single sample/snapshot (one set of offline unwind files).
+// 2. Consecutive/Multiple unwinds: unwind from a multiple samples/snapshots.
+//
+// `Init` contains two overloads for these two unwind cases. Other than `Init` and
+// `ReturnToCurrentWorkingDirectory`, the remainder of the public API includes a `sample_name`
+// parameter to indicate which sample/snapshot we are referencing. Specifying this value is
+// REQUIRED for the multiple unwind use case. However, in the single use case, the caller has
+// the choice of either providing the sample name or using the default value.
+class OfflineUnwindUtils {
+ public:
+  // If the sample name passed to Get* is an invalid sample, nullptr is returned.
+  Regs* GetRegs(const std::string& sample_name = kSingleSample) const;
+
+  Maps* GetMaps(const std::string& sample_name = kSingleSample) const;
+
+  std::shared_ptr<Memory> GetProcessMemory(const std::string& sample_name = kSingleSample) const;
+
+  JitDebug* GetJitDebug(const std::string& sample_name = kSingleSample) const;
+
+  const std::string* GetOfflineFilesPath(const std::string& sample_name = kSingleSample) const;
+
+  const std::string* GetFrameInfoFilepath(const std::string& sample_name = kSingleSample) const;
+
+  // Note: If the caller sets elements of `set_maps` to false or `memory_types` to
+  //  kNoMemory, they are responsible for calling `CreateMaps` or `CreateProcessMemory` before
+  //  expecting `GetMaps` or `GetProcessMemory` to return anything but nullptr.
+  bool Init(const std::vector<UnwindSampleInfo>& sample_infos, std::string* error_msg);
+
+  bool Init(const UnwindSampleInfo& sample_info, std::string* error_msg);
+
+  // This must be called explicitly for the multiple unwind use case sometime before
+  // Unwinder::Unwind is called. This is required because the Unwinder must init each
+  // ELF object with a MemoryFileAtOffset memory object. Because the maps.txt provides a relative
+  // path to the ELF files, we must be in the directory of the maps.txt when unwinding.
+  //
+  // Note: Init performs the check that this sample directory exists. If Init fails,
+  // `initted_` is not set to true and this function will return false.
+  bool ChangeToSampleDirectory(std::string* error_msg,
+                               const std::string& initial_sample_name = kSingleSample) const;
+
+  void ReturnToCurrentWorkingDirectory() {
+    if (!cwd_.empty()) std::filesystem::current_path(cwd_);
+  }
+
+  bool GetExpectedNumFrames(size_t* expected_num_frames, std::string* error_msg,
+                            const std::string& sample_name = kSingleSample) const;
+
+  bool CreateMaps(std::string* error_msg, const std::string& sample_name = kSingleSample);
+
+  bool CreateProcessMemory(std::string* error_msg, const std::string& sample_name = kSingleSample);
+
+  static constexpr char kSingleSample[] = "";
+
+ private:
+  // An `UnwindSample` encapsulates the information necessary to perform an offline unwind for a
+  // single offline sample/snapshot.
+  struct UnwindSample {
+    std::string offline_files_path;
+    std::string frame_info_filepath;
+    std::string map_buffer;
+    std::unique_ptr<Regs> regs;
+    std::unique_ptr<Maps> maps;
+    std::shared_ptr<Memory> process_memory;
+    std::unique_ptr<JitDebug> jit_debug;
+  };
+
+  bool CreateRegs(ArchEnum arch, std::string* error_msg,
+                  const std::string& sample_name = kSingleSample);
+
+  // Needed to support using the default value `kSingleSample` for the single unwind use case.
+  const std::string& GetAdjustedSampleName(const std::string& sample_name) const;
+
+  bool IsValidUnwindSample(const std::string& sample_name, std::string* error_msg) const;
+
+  static std::unordered_map<std::string, uint32_t> arm_regs_;
+  static std::unordered_map<std::string, uint32_t> arm64_regs_;
+  static std::unordered_map<std::string, uint32_t> x86_regs_;
+  static std::unordered_map<std::string, uint32_t> x86_64_regs_;
+
+  std::string cwd_;
+  std::unordered_map<std::string, UnwindSample> samples_;
+  bool initted_ = false;
+};
+
+}  // namespace unwindstack
diff --git a/libunwindstack/utils/PidUtils.cpp b/libunwindstack/utils/PidUtils.cpp
new file mode 100644
index 0000000..4268225
--- /dev/null
+++ b/libunwindstack/utils/PidUtils.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2022 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 <stdint.h>
+#include <stdio.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "PidUtils.h"
+
+namespace unwindstack {
+
+static bool Exited(pid_t pid) {
+  int status;
+  pid_t wait_pid = waitpid(pid, &status, WNOHANG);
+  if (wait_pid != pid) {
+    return false;
+  }
+
+  if (WIFEXITED(status)) {
+    fprintf(stderr, "%d died: Process exited with code %d\n", pid, WEXITSTATUS(status));
+  } else if (WIFSIGNALED(status)) {
+    fprintf(stderr, "%d died: Process exited due to signal %d\n", pid, WTERMSIG(status));
+  } else {
+    fprintf(stderr, "%d died: Process finished for unknown reason\n", pid);
+  }
+  return true;
+}
+
+bool Quiesce(pid_t pid) {
+  siginfo_t si;
+  // Wait for up to 10 seconds.
+  for (time_t start_time = time(nullptr); time(nullptr) - start_time < 10;) {
+    if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+      return true;
+    }
+    if (errno != ESRCH) {
+      if (errno == EINVAL) {
+        // The process is in group-stop state, so try and kick the
+        // process out of that state.
+        if (ptrace(PTRACE_LISTEN, pid, 0, 0) == -1) {
+          // Cannot recover from this, so just pretend it worked and see
+          // if we can unwind.
+          return true;
+        }
+      } else {
+        perror("ptrace getsiginfo failed");
+        return false;
+      }
+    }
+    usleep(5000);
+  }
+  fprintf(stderr, "Did not quiesce in 10 seconds\n");
+  return false;
+}
+
+bool Attach(pid_t pid) {
+  // Wait up to 45 seconds to attach.
+  for (time_t start_time = time(nullptr); time(nullptr) - start_time < 45;) {
+    if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
+      break;
+    }
+    if (errno != ESRCH) {
+      perror("Failed to attach");
+      return false;
+    }
+    usleep(5000);
+  }
+
+  if (Quiesce(pid)) {
+    return true;
+  }
+
+  if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) {
+    perror("Failed to detach");
+  }
+  return false;
+}
+
+bool Detach(pid_t pid) {
+  if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) {
+    perror("ptrace detach failed");
+    return false;
+  }
+  return true;
+}
+
+bool RunWhenQuiesced(pid_t pid, bool leave_attached, std::function<PidRunEnum()> fn) {
+  // Wait up to 120 seconds to run the fn.
+  PidRunEnum status = PID_RUN_KEEP_GOING;
+  for (time_t start_time = time(nullptr);
+       time(nullptr) - start_time < 120 && status == PID_RUN_KEEP_GOING;) {
+    if (Attach(pid)) {
+      status = fn();
+      if (status == PID_RUN_PASS && leave_attached) {
+        return true;
+      }
+
+      if (!Detach(pid)) {
+        return false;
+      }
+    } else if (Exited(pid)) {
+      return false;
+    }
+    usleep(5000);
+  }
+  if (status == PID_RUN_KEEP_GOING) {
+    fprintf(stderr, "Timed out waiting for pid %d to be ready\n", pid);
+  }
+  return status == PID_RUN_PASS;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/utils/PidUtils.h b/libunwindstack/utils/PidUtils.h
new file mode 100644
index 0000000..5f67204
--- /dev/null
+++ b/libunwindstack/utils/PidUtils.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <functional>
+
+namespace unwindstack {
+
+enum PidRunEnum : uint8_t {
+  PID_RUN_KEEP_GOING,
+  PID_RUN_PASS,
+  PID_RUN_FAIL,
+};
+
+bool Quiesce(pid_t pid);
+
+bool Attach(pid_t pid);
+
+bool Detach(pid_t pid);
+
+bool RunWhenQuiesced(pid_t pid, bool leave_attached, std::function<PidRunEnum()> fn);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/utils/ProcessTracer.cpp b/libunwindstack/utils/ProcessTracer.cpp
new file mode 100644
index 0000000..7f60ca0
--- /dev/null
+++ b/libunwindstack/utils/ProcessTracer.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2021 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 <fcntl.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <array>
+#include <atomic>
+#include <csignal>
+#include <cstddef>
+#include <cstdio>
+#include <cstring>
+#include <memory>
+#include <regex>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <procinfo/process.h>
+
+#include "ProcessTracer.h"
+
+namespace unwindstack {
+
+ProcessTracer::ProcessTracer(pid_t pid, bool is_tracing_threads)
+    : pid_(pid), is_tracing_threads_(is_tracing_threads) {
+  if (is_tracing_threads_) is_tracing_threads_ = InitProcessTids();
+}
+
+bool ProcessTracer::InitProcessTids() {
+  std::string error_msg;
+  if (!android::procinfo::GetProcessTids(pid_, &tids_, &error_msg)) {
+    fprintf(stderr,
+            "Failed to get process tids: %s. Reverting to tracing the "
+            "main thread only.\n",
+            error_msg.c_str());
+    return false;
+  }
+  if (tids_.erase(pid_) != 1) {
+    fprintf(stderr,
+            "Failed to erase the main thread from the thread id set. "
+            "Reverting to tracing the main thread only.\n");
+    return false;
+  }
+  return true;
+}
+
+ProcessTracer::~ProcessTracer() {
+  if (cur_attached_tid_ != kNoThreadAttached) Detach(cur_attached_tid_);
+  if (!is_running_) Resume();
+}
+
+bool ProcessTracer::Stop() {
+  if (kill(pid_, SIGSTOP) == kKillFailed) {
+    fprintf(stderr, "Failed to send stop signal to pid %d: %s\n", pid_, strerror(errno));
+    return false;
+  }
+  usleep(1000);  // 1 ms. Without this sleep, any attempt to resume right away may fail.
+
+  is_running_ = false;
+  return true;
+}
+
+bool ProcessTracer::Resume() {
+  if (kill(pid_, SIGCONT) == kKillFailed) {
+    fprintf(stderr, "Failed to send continue signal to pid %d: %s\n", pid_, strerror(errno));
+    return false;
+  }
+  usleep(1000);  // 1 ms. Without this sleep, any attempt to stop right away may fail.
+
+  is_running_ = true;
+  return true;
+}
+
+bool ProcessTracer::Detach(pid_t tid) {
+  if (tid != pid_ && tids_.find(tid) == tids_.end()) {
+    fprintf(stderr, "Tid %d does not belong to proc %d.\n", tid, pid_);
+    return false;
+  }
+
+  if (cur_attached_tid_ == kNoThreadAttached) {
+    fprintf(stderr, "Cannot detach because no thread is currently attached.\n");
+    return false;
+  }
+  if (is_running_ && !Stop()) return false;
+
+  if (ptrace(PTRACE_DETACH, tid, nullptr, nullptr) == kPtraceFailed) {
+    fprintf(stderr, "Failed to detach from tid %d: %s\n", tid, strerror(errno));
+    return false;
+  }
+
+  cur_attached_tid_ = kNoThreadAttached;
+  return true;
+}
+
+bool ProcessTracer::Attach(pid_t tid) {
+  if (tid != pid_ && tids_.find(tid) == tids_.end()) {
+    fprintf(stderr, "Tid %d does not belong to proc %d.\n", tid, pid_);
+    return false;
+  }
+
+  if (is_running_) Stop();
+  if (cur_attached_tid_ != kNoThreadAttached) {
+    fprintf(stderr, "Cannot attatch to tid %d. Already attached to tid %d.\n", tid,
+            cur_attached_tid_);
+    return false;
+  }
+
+  if (ptrace(PTRACE_ATTACH, tid, nullptr, nullptr) == kPtraceFailed) {
+    fprintf(stderr, "Failed to attached to tid %d: %s\n", tid, strerror(errno));
+    return false;
+  }
+  int status;
+  if (waitpid(tid, &status, 0) == kWaitpidFailed) {
+    fprintf(stderr, "Failed to stop tid %d: %s\n", tid, strerror(errno));
+    return false;
+  }
+
+  cur_attached_tid_ = tid;
+  return true;
+}
+
+bool ProcessTracer::StopInDesiredElf(const std::string& elf_name) {
+  signal(SIGINT, [](int) { keepWaitingForPcInElf = false; });
+  bool pc_in_desired_elf = true;
+  do {
+    if (!Attach(pid_)) return false;
+    pc_in_desired_elf = ProcIsInDesiredElf(pid_, elf_name);
+    if (!Detach(pid_)) return false;
+
+    if (!pc_in_desired_elf) {
+      for (pid_t tid : tids_) {
+        if (!Attach(tid)) return false;
+        pc_in_desired_elf = ProcIsInDesiredElf(tid, elf_name);
+        if (!Detach(tid)) return false;
+        if (pc_in_desired_elf) break;
+      }
+    }
+
+    // If the process is not in the desired ELF, resume it for a short time, then check again.
+    if (!pc_in_desired_elf) {
+      Resume();
+      usleep(1000);  // 1 ms
+      Stop();
+    }
+  } while (!pc_in_desired_elf && keepWaitingForPcInElf);
+
+  if (!pc_in_desired_elf) {
+    fprintf(stderr, "\nExited while waiting for pid %d to enter %s.\n", pid_, elf_name.c_str());
+    return false;
+  }
+  return true;
+}
+
+bool ProcessTracer::UsesSharedLibrary(pid_t pid, const std::string& desired_elf_name) {
+  std::unique_ptr<Maps> maps = std::make_unique<RemoteMaps>(pid);
+  if (!maps->Parse()) {
+    fprintf(stderr, "Could not parse maps for pid %d.\n", pid);
+    return false;
+  }
+  for (const auto& map : *maps) {
+    if (android::base::Basename(map->name()).c_str() == desired_elf_name) return true;
+  }
+  return false;
+}
+
+bool ProcessTracer::ProcIsInDesiredElf(pid_t pid, const std::string& desired_elf_name) {
+  std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
+  if (regs == nullptr) {
+    fprintf(stderr, "Unable to get remote reg data.\n");
+    return false;
+  }
+  UnwinderFromPid unwinder(1024, pid);
+  unwinder.SetRegs(regs.get());
+  if (!unwinder.Init()) {
+    fprintf(stderr, "Unable to intitialize unwinder.\n");
+    return false;
+  }
+  Maps* maps = unwinder.GetMaps();
+  auto map_info = maps->Find(regs->pc());
+  if (map_info == nullptr) {
+    regs->fallback_pc();
+    map_info = maps->Find(regs->pc());
+    if (map_info == nullptr) {
+      return false;
+    }
+  }
+
+  const std::string& current_elf_name = android::base::Basename(map_info->name()).c_str();
+  bool in_desired_elf = current_elf_name == desired_elf_name;
+  if (in_desired_elf) printf("pid %d is in %s! Unwinding...\n\n", pid, desired_elf_name.c_str());
+  return in_desired_elf;
+}
+}  // namespace unwindstack
diff --git a/libunwindstack/utils/ProcessTracer.h b/libunwindstack/utils/ProcessTracer.h
new file mode 100644
index 0000000..1809cb2
--- /dev/null
+++ b/libunwindstack/utils/ProcessTracer.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <set>
+#include <string>
+#include <unordered_set>
+
+namespace unwindstack {
+
+// ProcessTracer objects abstract operations for tracing a process and its threads with ptrace(2).
+class ProcessTracer final {
+ public:
+  ProcessTracer(pid_t pid, bool is_tracing_threads);
+
+  ~ProcessTracer();
+
+  // ProcessTracer instances are moveable but not copyable because they manage the
+  // state of a process.
+  ProcessTracer(const ProcessTracer&) = delete;
+  ProcessTracer& operator=(const ProcessTracer&) = delete;
+  ProcessTracer(ProcessTracer&&) = default;
+  ProcessTracer& operator=(ProcessTracer&&) = default;
+
+  pid_t pid() const { return pid_; }
+
+  const std::set<pid_t>& tids() const { return tids_; }
+
+  bool IsTracingThreads() const { return is_tracing_threads_; }
+
+  bool Stop();
+
+  bool Resume();
+
+  // Like ptrace, it is required to call ProcessTracer::Detach before calling ProcessTracer::Attach
+  // on a different thread of the same process.
+  bool Detach(pid_t tid);
+
+  bool Attach(pid_t tid);
+
+  // This method for determining whether a thread is currently executing instructions from a
+  // desired ELF is not the most time efficient solution. In the interest of simplicity and
+  // limiting memory usage, the UnwinderFromPid, Regs, and Maps instances constructed for
+  // in each check (loop iteration) are thrown away.
+  //
+  // A SIGINT signal handler is set up to allow the user to gracefully exit with CTRL-C if they
+  // decide that they no longer want to wait for the process to enter the desired ELF.
+  bool StopInDesiredElf(const std::string& elf_name);
+
+  // `desired_elf_name` should match the filename of the path (the component following the final
+  // '/') corresponding to the shared library as indicated in /proc/pid/maps.
+  static bool UsesSharedLibrary(pid_t pid, const std::string& desired_elf_name);
+
+ private:
+  static bool ProcIsInDesiredElf(pid_t tid, const std::string& desired_elf_name);
+
+  // Initialize tids_ such that the main thread is the first element and
+  // the remaining tids are in order from least to greatest.
+  bool InitProcessTids();
+
+  static constexpr pid_t kNoThreadAttached = -2;
+  static constexpr pid_t kKillFailed = -1;
+  static constexpr pid_t kPtraceFailed = -1;
+  static constexpr pid_t kWaitpidFailed = -1;
+  static inline std::atomic_bool keepWaitingForPcInElf = true;
+  const pid_t pid_;
+  bool is_tracing_threads_ = false;
+  std::set<pid_t> tids_;
+  bool is_running_ = true;
+  pid_t cur_attached_tid_ = kNoThreadAttached;
+};
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/utils/RegsFake.h
similarity index 96%
rename from libunwindstack/tests/RegsFake.h
rename to libunwindstack/utils/RegsFake.h
index f67d7dc..4bb4181 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/utils/RegsFake.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
-#define _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
+#pragma once
 
 #include <stdint.h>
 
@@ -113,5 +112,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
diff --git a/libunwindstack/utils/tests/ProcessTracerTest.cpp b/libunwindstack/utils/tests/ProcessTracerTest.cpp
new file mode 100644
index 0000000..eae55bb
--- /dev/null
+++ b/libunwindstack/utils/tests/ProcessTracerTest.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2021 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 <dlfcn.h>
+#include <gtest/gtest.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <array>
+#include <atomic>
+#include <cerrno>
+#include <csignal>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <filesystem>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <thread>
+
+#include <android-base/file.h>
+#include <procinfo/process.h>
+
+#include "OfflineUnwindUtils.h"
+#include "ProcessTracer.h"
+#include "tests/TestUtils.h"
+
+namespace unwindstack {
+namespace {
+
+class ProcessTracerTest : public ::testing::TestWithParam<bool> {
+ protected:
+  enum class BoolOrTimeout {
+    kSuccess = 0,
+    kFail,
+    kTimeout,
+  };
+
+  // Setup a child process that has a few threads that simply busy wait.
+  void SetUp() override {
+    // Setup signal handlers for child to let parent know that it is ready and for parent
+    // to kill the child.
+    child_is_ready_ = false;
+    ASSERT_NE(SIG_ERR, signal(kChildIsReadySignal, [](int) { child_is_ready_ = true; }))
+        << "Failed to set up signal handler for kChildIsReadySignal: " << strerror(errno);
+    child_keep_running_ = true;
+    ASSERT_NE(SIG_ERR, signal(kStopChildSignal, [](int) { child_keep_running_ = false; }))
+        << "Failed to set up signal handler for kStopChildSignal: " << strerror(errno);
+
+    pid_t parent_pid = getpid();
+    child_pid_ = fork();
+    if (child_pid_ == static_cast<pid_t>(-1)) FAIL() << "SetUp: fork() failed: " << strerror(errno);
+    if (child_pid_ == 0) {
+      ASSERT_NO_FATAL_FAILURE(ChildProcSpin(parent_pid));
+    }
+
+    // Make sure the child process has set up its threads before running the test.
+    sigset_t signal_mask, old_mask;
+    sigemptyset(&signal_mask);
+    sigaddset(&signal_mask, kChildIsReadySignal);
+    sigprocmask(SIG_BLOCK, &signal_mask, &old_mask);
+    while (!child_is_ready_) sigsuspend(&old_mask);
+    sigprocmask(SIG_UNBLOCK, &signal_mask, NULL);
+  }
+
+  void TearDown() override {
+    // Send signal to join threads and exit.
+    if (-1 == kill(child_pid_, kStopChildSignal)) {
+      std::cerr << "TearDown: kill sending kStopChildSignal failed: " << strerror(errno) << ".\n";
+      kill(child_pid_, SIGKILL);
+    }
+  }
+
+  void ChildProcSpin(pid_t parent_pid) {
+    // Busy wait in a dlopened local library so we can reliably test (across different
+    // architecture) if a process is within a desired ELF.
+    std::unique_ptr<void, decltype(&dlclose)> test_lib_handle(GetTestLibHandle(), &dlclose);
+    ASSERT_TRUE(test_lib_handle);
+    int (*busy_wait_func)() = reinterpret_cast<int (*)()>(dlsym(test_lib_handle.get(), "BusyWait"));
+    ASSERT_NE(nullptr, busy_wait_func);
+
+    std::array<std::thread, kNumThreads> threads;
+    std::array<std::atomic_bool, kNumThreads> threads_are_ready{false, false, false, false, false};
+    for (size_t i = 0; i < kNumThreads; ++i) {
+      threads.at(i) = std::thread([&threads_are_ready, i, &busy_wait_func]() {
+        while (child_keep_running_) {
+          DoNotOptimize(busy_wait_func());
+          threads_are_ready.at(i) = true;
+        }
+      });
+    }
+    // Wait until all threads have entered the loop before informing parent child is
+    // ready to avoid a race.
+    while (!std::all_of(threads_are_ready.begin(), threads_are_ready.end(),
+                        [&](const std::atomic_bool& el) { return el == true; })) {
+      usleep(100);
+    }
+    ASSERT_NE(-1, kill(parent_pid, kChildIsReadySignal) == -1)
+        << "TearDown: kill sending kChildIsReady failed: " << strerror(errno) << ".\n";
+    for (size_t i = 0; i < kNumThreads; ++i) {
+      threads.at(i).join();
+    }
+    exit(EXIT_SUCCESS);
+  }
+
+  BoolOrTimeout StopInDesiredElfTimeout(ProcessTracer& proc, const std::string& elf_name,
+                                        size_t timeout_sec = 2) {
+    static BoolOrTimeout result = BoolOrTimeout::kSuccess;
+    if (SIG_ERR == signal(SIGALRM, [](int) {
+          result = BoolOrTimeout::kTimeout;
+          // StopInDesiredElf contains signal handler for SIGINT mainly so that we could stop the
+          // search easily when running unwind_for_offline and we can use it here too.
+          kill(getpid(), SIGINT);
+        })) {
+      std::cerr << "Failed to set up signal handler for SIGALRM: " << strerror(errno) << ".\n";
+      exit(EXIT_FAILURE);
+    }
+    alarm(timeout_sec);
+    if (proc.StopInDesiredElf(elf_name)) {
+      result = BoolOrTimeout::kSuccess;
+    } else if (result != BoolOrTimeout::kTimeout) {
+      result = BoolOrTimeout::kFail;
+    }
+    alarm(0);
+    return result;
+  }
+
+  static constexpr size_t kNumThreads = 5;
+  static constexpr int kChildIsReadySignal = SIGUSR1;
+  static constexpr int kStopChildSignal = SIGUSR2;
+  static inline std::atomic_bool child_is_ready_ = false;
+  static inline std::atomic_bool child_keep_running_ = true;
+  pid_t child_pid_;
+};
+
+static void VerifyState(pid_t tid, bool running) {
+  while (true) {
+    android::procinfo::ProcessInfo proc_info;
+    ASSERT_TRUE(GetProcessInfo(tid, &proc_info));
+    if (running) {
+      if (proc_info.state == android::procinfo::kProcessStateRunning ||
+          proc_info.state == android::procinfo::kProcessStateSleeping) {
+        break;
+      }
+    } else if (proc_info.state == android::procinfo::kProcessStateStopped) {
+      break;
+    }
+    usleep(1000);
+  }
+}
+
+static void VerifyState(ProcessTracer& proc, bool running) {
+  // Verify that the main thread and all threads are in the expected state.
+  VerifyState(proc.pid(), running);
+  if (::testing::Test::HasFatalFailure()) return;
+  for (const pid_t& tid : proc.tids()) {
+    VerifyState(tid, running);
+    if (::testing::Test::HasFatalFailure()) return;
+  }
+}
+
+TEST_P(ProcessTracerTest, stop_and_resume) {
+  ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+
+  ASSERT_TRUE(proc.Stop());
+  VerifyState(proc, /*running*/ false);
+  if (::testing::Test::HasFatalFailure()) return;
+
+  ASSERT_TRUE(proc.Resume());
+  VerifyState(proc, /*running*/ true);
+  if (::testing::Test::HasFatalFailure()) return;
+}
+
+TEST_P(ProcessTracerTest, attach_and_detach) {
+  ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+
+  ASSERT_TRUE(proc.Attach(child_pid_));
+  // Attaching to the same pid should result in failure and errno indicating that we cannot trace
+  // the priocess because it is already being traced after the call to Attach().
+  ASSERT_EQ(-1, ptrace(PTRACE_ATTACH, child_pid_, nullptr, nullptr));
+  ASSERT_EQ(EPERM, errno);
+  ASSERT_TRUE(proc.Detach(child_pid_));
+  for (const pid_t& tid : proc.tids()) {
+    ASSERT_TRUE(proc.Attach(tid));
+    ASSERT_EQ(-1, ptrace(PTRACE_ATTACH, tid, nullptr, nullptr));
+    ASSERT_EQ(EPERM, errno);
+    ASSERT_TRUE(proc.Detach(tid));
+  }
+}
+
+TEST_P(ProcessTracerTest, consecutive_attach_fail) {
+  if (!GetParam()) GTEST_SKIP();
+  ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+
+  bool is_first_thread = true;
+  for (const pid_t& tid : proc.tids()) {
+    if (is_first_thread) {
+      ASSERT_TRUE(proc.Attach(tid));
+      is_first_thread = false;
+    } else {
+      ASSERT_FALSE(proc.Attach(tid));
+    }
+  }
+}
+
+TEST_P(ProcessTracerTest, trace_invalid_tid) {
+  if (GetParam()) GTEST_SKIP();
+  ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+  ASSERT_FALSE(proc.Attach(getpid()));
+  ASSERT_FALSE(proc.Detach(getpid()));
+}
+
+TEST_P(ProcessTracerTest, detach_with_no_attached) {
+  if (GetParam()) GTEST_SKIP();
+  ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+  ASSERT_FALSE(proc.Detach(child_pid_));
+}
+
+TEST_P(ProcessTracerTest, uses_shared_library) {
+  ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+
+  std::string elf_name = "libunwindstack_local.so";
+  ASSERT_TRUE(proc.UsesSharedLibrary(child_pid_, elf_name));
+  for (const pid_t& tid : proc.tids()) {
+    ASSERT_TRUE(proc.UsesSharedLibrary(tid, elf_name));
+  }
+}
+
+TEST_P(ProcessTracerTest, does_not_use_shared_library) {
+  ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+
+  std::string elf_name = "libfake.so";
+  ASSERT_FALSE(proc.UsesSharedLibrary(child_pid_, elf_name));
+  for (const pid_t& tid : proc.tids()) {
+    ASSERT_FALSE(proc.UsesSharedLibrary(tid, elf_name));
+  }
+}
+
+TEST_P(ProcessTracerTest, stop_in_elf_we_use) {
+  // Skip the run with is_tracing_threads=false because main thread only uses
+  // the threading library.
+  if (!GetParam()) GTEST_SKIP();
+  ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+  std::string elf_name = "libunwindstack_local.so";
+
+  EXPECT_EQ(BoolOrTimeout::kSuccess, StopInDesiredElfTimeout(proc, elf_name));
+}
+
+TEST_P(ProcessTracerTest, timeout_when_try_to_stop_in_elf_we_do_not_use) {
+  ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+  std::string elf_name = "libfake.so";
+
+  EXPECT_EQ(BoolOrTimeout::kTimeout, StopInDesiredElfTimeout(proc, elf_name));
+}
+
+INSTANTIATE_TEST_CASE_P(IsTracingThreads, ProcessTracerTest, testing::Values(false, true));
+
+}  // namespace
+}  // namespace unwindstack