Snap for 8505378 from 5458702d0787d5136cf59df51af6b1803b4600d8 to mainline-go-adservices-release

Change-Id: Ib88e68c8422119bc4a5600e01e35c1b6a418770e
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 93a6fd3..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,
         },
@@ -196,7 +193,7 @@
     ],
 
     target: {
-        linux_glibc: {
+        host_linux: {
             // This forces the creation of eh_frame with unwind information
             // for host.
             cflags: [
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 95022fb..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;
     }
   }
 
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index e842c11..bed17e4 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -52,6 +52,7 @@
 }
 
 libunwindstack_common_src_files = [
+    "AndroidUnwinder.cpp",
     "ArmExidx.cpp",
     "DexFiles.cpp",
     "DwarfCfa.cpp",
@@ -239,6 +240,7 @@
     srcs: [
         "utils/MemoryFake.cpp",
         "utils/OfflineUnwindUtils.cpp",
+        "utils/PidUtils.cpp",
         "utils/ProcessTracer.cpp",
     ],
 }
@@ -266,6 +268,7 @@
     defaults: ["libunwindstack_flags"],
 
     srcs: [
+        "tests/AndroidUnwinderTest.cpp",
         "tests/ArmExidxDecodeTest.cpp",
         "tests/ArmExidxExtractTest.cpp",
         "tests/DexFileTest.cpp",
@@ -313,6 +316,7 @@
         "tests/MemoryXzTest.cpp",
         "tests/RegsInfoTest.cpp",
         "tests/RegsIterateTest.cpp",
+        "tests/RegsRemoteTest.cpp",
         "tests/RegsStepIfSignalHandlerTest.cpp",
         "tests/RegsTest.cpp",
         "tests/SymbolsTest.cpp",
@@ -572,13 +576,13 @@
 
     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/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 493bc00..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>
 
@@ -30,5 +29,3 @@
   }
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_CHECK_H
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/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/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/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/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/MemoryOffline.h b/libunwindstack/MemoryOffline.h
index 8a8e10f..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>
 
@@ -58,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 ac0558b..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>
 
@@ -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.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/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 2bdb910..7b45261 100644
--- a/libunwindstack/ThreadUnwinder.cpp
+++ b/libunwindstack/ThreadUnwinder.cpp
@@ -72,6 +72,10 @@
 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_;
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index f6def23..facff86 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -145,6 +145,7 @@
     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;
@@ -155,7 +156,11 @@
       }
       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_);
@@ -186,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(),
-                  android::base::Basename(map_info->name())) == initial_map_names_to_skip->end()) {
+    if (!ignore_frame) {
       if (regs_->dex_pc() != 0) {
         // Add a frame to represent the dex file.
         FillInDexFrame();
@@ -296,8 +299,12 @@
 }
 
 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);
@@ -334,7 +341,7 @@
     data += ')';
   }
 
-  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 + ')';
@@ -347,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) {
diff --git a/libunwindstack/benchmarks/MemoryLocalUnsafe.h b/libunwindstack/benchmarks/MemoryLocalUnsafe.h
index d901e1d..8edf473 100644
--- a/libunwindstack/benchmarks/MemoryLocalUnsafe.h
+++ b/libunwindstack/benchmarks/MemoryLocalUnsafe.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_BENCHMARKS_MEMORY_LOCAL_UNSAFE_H
-#define _LIBUNWINDSTACK_BENCHMARKS_MEMORY_LOCAL_UNSAFE_H
+#pragma once
 
 #include <cstddef>
 #include <cstring>
@@ -39,5 +38,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_BENCHMARKS_MEMORY_LOCAL_UNSAFE_H
\ No newline at end of file
diff --git a/libunwindstack/benchmarks/Utils.h b/libunwindstack/benchmarks/Utils.h
index e916525..2d05af9 100644
--- a/libunwindstack/benchmarks/Utils.h
+++ b/libunwindstack/benchmarks/Utils.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_UTILS_H
-#define _LIBUNWINDSTACK_UTILS_H
+#pragma once
 
 #include <benchmark/benchmark.h>
 #include <stdint.h>
@@ -66,5 +65,3 @@
   // total number of iterations of the ranged for loop across all runs of a single benchmark.
   size_t total_iterations_ = 0;
 };
-
-#endif  // _LIBUNWINDSTACK_UTILS_h
diff --git a/libunwindstack/benchmarks/local_unwind_benchmarks.cpp b/libunwindstack/benchmarks/local_unwind_benchmarks.cpp
index dd12ab5..78c8c13 100644
--- a/libunwindstack/benchmarks/local_unwind_benchmarks.cpp
+++ b/libunwindstack/benchmarks/local_unwind_benchmarks.cpp
@@ -22,6 +22,7 @@
 
 #include <android-base/strings.h>
 
+#include <unwindstack/AndroidUnwinder.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
@@ -99,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;
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 73ffda9..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>
 
@@ -138,5 +137,3 @@
 };
 
 }  // 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/Log.h b/libunwindstack/include/unwindstack/Log.h
index 56de7e0..34eb218 100644
--- a/libunwindstack/include/unwindstack/Log.h
+++ b/libunwindstack/include/unwindstack/Log.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_LOG_H
-#define _LIBUNWINDSTACK_LOG_H
+#pragma once
 
 #include <stdarg.h>
 #include <stdint.h>
@@ -36,5 +35,3 @@
 }  // 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 ce4dbea..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>
 
@@ -232,5 +231,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index deaca6f..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>
@@ -138,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 de3438f..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>
@@ -83,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);
@@ -102,6 +104,7 @@
 
   void SetDexFiles(DexFiles* dex_files);
 
+  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; }
@@ -120,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;
@@ -149,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,
@@ -173,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;
 
@@ -189,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/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/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/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/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/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/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/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index 2b6e682..ed58eb9 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -32,6 +32,7 @@
 
 #include "MemoryRemote.h"
 
+#include "PidUtils.h"
 #include "TestUtils.h"
 #include "utils/MemoryFake.h"
 
@@ -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/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/TestLocal.cpp b/libunwindstack/tests/TestLocal.cpp
index f8684d7..25bd42f 100644
--- a/libunwindstack/tests/TestLocal.cpp
+++ b/libunwindstack/tests/TestLocal.cpp
@@ -17,6 +17,8 @@
 #include <stdint.h>
 #include <stdlib.h>
 
+#include "TestUtils.h"
+
 // 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.
@@ -25,7 +27,17 @@
 //  1. The loop iteration variable is volatile.
 //  2. A call to this function should be wrapped in TestUtils::DoNotOptimize().
 extern "C" int BusyWait() {
-  for (volatile size_t i = 0; i < 1000000; ++i)
-    ;
+  for (size_t i = 0; i < 1000000;) {
+    unwindstack::DoNotOptimize(i++);
+  }
   return 0;
 }
+
+// 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 f2046f8..1f748ba 100644
--- a/libunwindstack/tests/TestUtils.cpp
+++ b/libunwindstack/tests/TestUtils.cpp
@@ -18,8 +18,12 @@
 #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) {
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
index 44b4e75..19cd693 100644
--- a/libunwindstack/tests/TestUtils.h
+++ b/libunwindstack/tests/TestUtils.h
@@ -14,15 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
-#define _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+#pragma once
 
-#include <errno.h>
 #include <signal.h>
-#include <sys/ptrace.h>
+#include <stdint.h>
 #include <sys/types.h>
 #include <sys/wait.h>
-#include <unistd.h>
 
 namespace unwindstack {
 
@@ -38,62 +35,6 @@
   pid_t pid_;
 };
 
-inline bool TestQuiescePid(pid_t pid) {
-  siginfo_t si;
-  // Wait for up to 10 seconds.
-  for (size_t i = 0; i < 10000; i++) {
-    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) {
-          perror("ptrace listen failed.");
-          return false;
-        }
-      } else {
-        return false;
-      }
-    }
-    usleep(1000);
-  }
-  return false;
-}
-
-inline bool TestAttach(pid_t pid) {
-  // Wait up to 10 seconds to attach.
-  for (size_t j = 0; j < 10000; j++) {
-    if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
-      break;
-    }
-    if (errno == ESRCH) {
-      usleep(1000);
-      continue;
-    }
-    perror("Failed to attach.");
-    return false;
-  }
-
-  if (TestQuiescePid(pid)) {
-    return true;
-  }
-
-  if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) {
-    perror("Failed to detach.");
-  }
-  return false;
-}
-
-inline bool TestDetach(pid_t pid) {
-  if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) {
-    perror("ptrace detach failed");
-    return false;
-  }
-  return true;
-}
-
 void TestCheckForLeaks(void (*unwind_func)(void*), void* data);
 
 void* GetTestLibHandle();
@@ -106,5 +47,3 @@
 }
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 2ed1b9b..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 {
@@ -236,28 +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 < 4000; i++) {
-    ASSERT_TRUE(TestAttach(pid));
-
-    MemoryRemote memory(pid);
-    // Read the remote value to see if we are ready.
+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) {
-      *completed = true;
+      return PID_RUN_PASS;
     }
-    if (!*completed || !leave_attached) {
-      ASSERT_TRUE(TestDetach(pid));
-    }
-    if (*completed) {
-      break;
-    }
-    usleep(5000);
-  }
+    return PID_RUN_KEEP_GOING;
+  });
 }
 
 TEST_F(UnwindTest, remote) {
@@ -269,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());
@@ -280,7 +266,7 @@
 
   VerifyUnwind(pid, &maps, regs.get(), kFunctionOrder);
 
-  ASSERT_TRUE(TestDetach(pid));
+  ASSERT_TRUE(Detach(pid));
 }
 
 TEST_F(UnwindTest, unwind_from_pid_remote) {
@@ -292,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);
@@ -304,7 +288,7 @@
 
   VerifyUnwind(&unwinder, kFunctionOrder);
 
-  ASSERT_TRUE(TestDetach(pid));
+  ASSERT_TRUE(Detach(pid));
 }
 
 static void RemoteCheckForLeaks(void (*unwind_func)(void*)) {
@@ -316,13 +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_TRUE(TestDetach(pid));
+  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,7 +411,7 @@
 
   VerifyUnwind(pid, &maps, regs.get(), kFunctionSignalOrder);
 
-  ASSERT_TRUE(TestDetach(pid));
+  ASSERT_TRUE(Detach(pid));
 }
 
 TEST_F(UnwindTest, remote_through_signal) {
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 74bc516..2da3ee1 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -1761,8 +1761,7 @@
 
 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());
diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.h b/libunwindstack/tests/fuzz/UnwinderComponentCreator.h
index 392219f..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>
@@ -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/utils/DwarfSectionImplFake.h b/libunwindstack/utils/DwarfSectionImplFake.h
index 973e535..54ed995 100644
--- a/libunwindstack/utils/DwarfSectionImplFake.h
+++ b/libunwindstack/utils/DwarfSectionImplFake.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_UTILS_DWARF_SECTION_IMPL_FAKE_H
-#define _LIBUNWINDSTACK_UTILS_DWARF_SECTION_IMPL_FAKE_H
+#pragma once
 
 #include <unwindstack/DwarfSection.h>
 #include <unwindstack/Memory.h>
@@ -47,5 +46,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_UTILS_DWARF_SECTION_IMPL_FAKE_H
\ No newline at end of file
diff --git a/libunwindstack/utils/MemoryFake.h b/libunwindstack/utils/MemoryFake.h
index 012fe90..b7adfd9 100644
--- a/libunwindstack/utils/MemoryFake.h
+++ b/libunwindstack/utils/MemoryFake.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_UTILS_MEMORY_FAKE_H
-#define _LIBUNWINDSTACK_UTILS_MEMORY_FAKE_H
+#pragma once
 
 #include <stdint.h>
 
@@ -72,5 +71,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_UTILS_MEMORY_FAKE_H
diff --git a/libunwindstack/utils/OfflineUnwindUtils.h b/libunwindstack/utils/OfflineUnwindUtils.h
index fc55d0c..df89c68 100644
--- a/libunwindstack/utils/OfflineUnwindUtils.h
+++ b/libunwindstack/utils/OfflineUnwindUtils.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_UTILS_OFFLINE_UNWIND_UTILS_H
-#define _LIBUNWINDSTACK_UTILS_OFFLINE_UNWIND_UTILS_H
+#pragma once
 
 #include <cstddef>
 #include <filesystem>
@@ -187,5 +186,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_UTILS_OFFLINE_UNWIND_UTILS_H
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.h b/libunwindstack/utils/ProcessTracer.h
index cd5fe80..1809cb2 100644
--- a/libunwindstack/utils/ProcessTracer.h
+++ b/libunwindstack/utils/ProcessTracer.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_UTILS_PROCESS_TRACER_H
-#define _LIBUNWINDSTACK_UTILS_PROCESS_TRACER_H
+#pragma once
 
 #include <sys/types.h>
 
@@ -87,5 +86,3 @@
   pid_t cur_attached_tid_ = kNoThreadAttached;
 };
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_UTILS_PROCESS_TRACER_H
\ No newline at end of file
diff --git a/libunwindstack/utils/RegsFake.h b/libunwindstack/utils/RegsFake.h
index c0b35b7..4bb4181 100644
--- a/libunwindstack/utils/RegsFake.h
+++ b/libunwindstack/utils/RegsFake.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_UTILS_REGS_FAKE_H
-#define _LIBUNWINDSTACK_UTILS_REGS_FAKE_H
+#pragma once
 
 #include <stdint.h>
 
@@ -113,5 +112,3 @@
 };
 
 }  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_UTILS_REGS_FAKE_H