Merge "libsnapshot: Partially implement OpenSnapshotWriter."
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index c52c6f7..5280121 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -597,8 +597,8 @@
   }
 
   // TODO: Use seccomp to lock ourselves down.
-  unwindstack::UnwinderFromPid unwinder(256, vm_pid);
-  if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
+  unwindstack::UnwinderFromPid unwinder(256, vm_pid, unwindstack::Regs::CurrentArch());
+  if (!unwinder.Init()) {
     LOG(FATAL) << "Failed to init unwinder object.";
   }
 
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 9bcbdb3..abcb2c4 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -82,16 +82,12 @@
     thread.pid = getpid();
     thread.tid = gettid();
     thread.thread_name = get_thread_name(gettid());
-    unwindstack::ArchEnum arch = unwindstack::Regs::CurrentArch();
-    thread.registers.reset(unwindstack::Regs::CreateFromUcontext(arch, ucontext));
+    thread.registers.reset(
+        unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
 
     // TODO: Create this once and store it in a global?
     unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid());
-    if (unwinder.Init(arch)) {
-      dump_backtrace_thread(output_fd, &unwinder, thread);
-    } else {
-      async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Unable to init unwinder.");
-    }
+    dump_backtrace_thread(output_fd, &unwinder, thread);
   }
   __linker_disable_fallback_allocator();
 }
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index f5a873c..c543a83 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -18,8 +18,9 @@
 
 #include "libdebuggerd/backtrace.h"
 
-#include <errno.h>
 #include <dirent.h>
+#include <errno.h>
+#include <inttypes.h>
 #include <limits.h>
 #include <stddef.h>
 #include <stdio.h>
@@ -65,7 +66,11 @@
   unwinder->SetRegs(thread.registers.get());
   unwinder->Unwind();
   if (unwinder->NumFrames() == 0) {
-    _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d", thread.tid);
+    _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d\n", thread.tid);
+    if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
+      _LOG(&log, logtype::THREAD, "  Error code: %s\n", unwinder->LastErrorCodeString());
+      _LOG(&log, logtype::THREAD, "  Error address: 0x%" PRIx64 "\n", unwinder->LastErrorAddress());
+    }
     return;
   }
 
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index e1fe82b..d88c5a9 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -407,7 +407,11 @@
   unwinder->SetRegs(regs_copy.get());
   unwinder->Unwind();
   if (unwinder->NumFrames() == 0) {
-    _LOG(log, logtype::THREAD, "Failed to unwind");
+    _LOG(log, logtype::THREAD, "Failed to unwind\n");
+    if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
+      _LOG(log, logtype::THREAD, "  Error code: %s\n", unwinder->LastErrorCodeString());
+      _LOG(log, logtype::THREAD, "  Error address: 0x%" PRIx64 "\n", unwinder->LastErrorAddress());
+    }
   } else {
     _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
     log_backtrace(log, unwinder, "    ");
@@ -578,8 +582,8 @@
       .siginfo = siginfo,
   };
 
-  unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid);
-  if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
+  unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch());
+  if (!unwinder.Init()) {
     LOG(FATAL) << "Failed to init unwinder object.";
   }
 
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index 038b59e..a506575 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -37,6 +37,12 @@
 #include "ThreadEntry.h"
 
 bool BacktraceCurrent::ReadWord(uint64_t ptr, word_t* out_value) {
+#if defined(__aarch64__)
+  // Tagged pointer after Android R would lead top byte to have random values
+  // https://source.android.com/devices/tech/debug/tagged-pointers
+  ptr &= (1ULL << 56) - 1;
+#endif
+
   if (!VerifyReadWordArgs(ptr, out_value)) {
     return false;
   }
@@ -54,6 +60,12 @@
 }
 
 size_t BacktraceCurrent::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
+#if defined(__aarch64__)
+  // Tagged pointer after Android R would lead top byte to have random values
+  // https://source.android.com/devices/tech/debug/tagged-pointers
+  addr &= (1ULL << 56) - 1;
+#endif
+
   backtrace_map_t map;
   FillInMap(addr, &map);
   if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 624711f..82ff21c 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -52,11 +52,11 @@
   unwinder.SetResolveNames(stack_map->ResolveNames());
   stack_map->SetArch(regs->Arch());
   if (stack_map->GetJitDebug() != nullptr) {
-    unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
+    unwinder.SetJitDebug(stack_map->GetJitDebug());
   }
 #if !defined(NO_LIBDEXFILE_SUPPORT)
   if (stack_map->GetDexFiles() != nullptr) {
-    unwinder.SetDexFiles(stack_map->GetDexFiles(), regs->Arch());
+    unwinder.SetDexFiles(stack_map->GetDexFiles());
   }
 #endif
   unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore());
@@ -180,5 +180,10 @@
 }
 
 size_t UnwindStackPtrace::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
+#if defined(__aarch64__)
+  // Tagged pointer after Android R would lead top byte to have random values
+  // https://source.android.com/devices/tech/debug/tagged-pointers
+  addr &= (1ULL << 56) - 1;
+#endif
   return memory_->Read(addr, buffer, bytes);
 }
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 8cc780a..75a419c 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -131,7 +131,6 @@
         support_system_process: true,
     },
     defaults: ["libunwindstack_defaults"],
-
     srcs: ["DexFile.cpp"],
     cflags: ["-DDEXFILE_SUPPORT"],
     shared_libs: ["libdexfile_support"],
@@ -168,6 +167,7 @@
     defaults: ["libunwindstack_defaults"],
 
     visibility: [
+        "//external/gwp_asan",
         "//system/core/debuggerd",
         "//system/core/init",
         "//system/core/libbacktrace",
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 57806c1..bcdbde8 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -27,6 +27,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
+#include <unwindstack/DexFiles.h>
 #include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/MapInfo.h>
@@ -34,7 +35,7 @@
 #include <unwindstack/Memory.h>
 #include <unwindstack/Unwinder.h>
 
-#include <unwindstack/DexFiles.h>
+#include "Check.h"
 
 // Use the demangler from libc++.
 extern "C" char* __cxa_demangle(const char*, char*, size_t*, int* status);
@@ -142,13 +143,11 @@
 
 void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
                       const std::vector<std::string>* map_suffixes_to_ignore) {
-  frames_.clear();
-  warnings_ = WARNING_NONE;
-  last_error_.code = ERROR_NONE;
-  last_error_.address = 0;
-  elf_from_memory_not_file_ = false;
+  CHECK(arch_ != ARCH_UNKNOWN);
+  ClearErrors();
 
-  ArchEnum arch = regs_->Arch();
+  frames_.clear();
+  elf_from_memory_not_file_ = false;
 
   bool return_address_attempt = false;
   bool adjust_pc = false;
@@ -169,7 +168,7 @@
       if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
         break;
       }
-      elf = map_info->GetElf(process_memory_, arch);
+      elf = map_info->GetElf(process_memory_, arch_);
       // If this elf is memory backed, and there is a valid file, then set
       // an indicator that we couldn't open the file.
       if (!elf_from_memory_not_file_ && map_info->memory_backed_elf && !map_info->name.empty() &&
@@ -183,7 +182,7 @@
         step_pc = rel_pc;
       }
       if (adjust_pc) {
-        pc_adjustment = GetPcAdjustment(rel_pc, elf, arch);
+        pc_adjustment = GetPcAdjustment(rel_pc, elf, arch_);
       } else {
         pc_adjustment = 0;
       }
@@ -311,7 +310,7 @@
 
 std::string Unwinder::FormatFrame(const FrameData& frame) const {
   std::string data;
-  if (regs_->Is32Bit()) {
+  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);
@@ -362,23 +361,33 @@
   return FormatFrame(frames_[frame_num]);
 }
 
-void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) {
-  jit_debug->SetArch(arch);
+void Unwinder::SetJitDebug(JitDebug* jit_debug) {
+  CHECK(arch_ != ARCH_UNKNOWN);
+  jit_debug->SetArch(arch_);
   jit_debug_ = jit_debug;
 }
 
-void Unwinder::SetDexFiles(DexFiles* dex_files, ArchEnum arch) {
-  dex_files->SetArch(arch);
+void Unwinder::SetDexFiles(DexFiles* dex_files) {
+  CHECK(arch_ != ARCH_UNKNOWN);
+  dex_files->SetArch(arch_);
   dex_files_ = dex_files;
 }
 
-bool UnwinderFromPid::Init(ArchEnum arch) {
+bool UnwinderFromPid::Init() {
+  CHECK(arch_ != ARCH_UNKNOWN);
+  if (initted_) {
+    return true;
+  }
+  initted_ = true;
+
   if (pid_ == getpid()) {
     maps_ptr_.reset(new LocalMaps());
   } else {
     maps_ptr_.reset(new RemoteMaps(pid_));
   }
   if (!maps_ptr_->Parse()) {
+    ClearErrors();
+    last_error_.code = ERROR_INVALID_MAP;
     return false;
   }
   maps_ = maps_ptr_.get();
@@ -387,16 +396,24 @@
 
   jit_debug_ptr_.reset(new JitDebug(process_memory_));
   jit_debug_ = jit_debug_ptr_.get();
-  SetJitDebug(jit_debug_, arch);
+  SetJitDebug(jit_debug_);
 #if defined(DEXFILE_SUPPORT)
   dex_files_ptr_.reset(new DexFiles(process_memory_));
   dex_files_ = dex_files_ptr_.get();
-  SetDexFiles(dex_files_, arch);
+  SetDexFiles(dex_files_);
 #endif
 
   return true;
 }
 
+void UnwinderFromPid::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
+                             const std::vector<std::string>* map_suffixes_to_ignore) {
+  if (!Init()) {
+    return;
+  }
+  Unwinder::Unwind(initial_map_names_to_skip, map_suffixes_to_ignore);
+}
+
 FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc, ArchEnum arch, Maps* maps,
                                          JitDebug* jit_debug,
                                          std::shared_ptr<Memory> process_memory,
@@ -449,8 +466,7 @@
 }
 
 FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc) {
-  return BuildFrameFromPcOnly(pc, regs_ ? regs_->Arch() : ARCH_UNKNOWN, maps_, jit_debug_,
-                              process_memory_, resolve_names_);
+  return BuildFrameFromPcOnly(pc, arch_, maps_, jit_debug_, process_memory_, resolve_names_);
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Arch.h b/libunwindstack/include/unwindstack/Arch.h
new file mode 100644
index 0000000..7060004
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Arch.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_ARCH_H
+#define _LIBUNWINDSTACK_ARCH_H
+
+#include <stddef.h>
+
+namespace unwindstack {
+
+enum ArchEnum : uint8_t {
+  ARCH_UNKNOWN = 0,
+  ARCH_ARM,
+  ARCH_ARM64,
+  ARCH_X86,
+  ARCH_X86_64,
+  ARCH_MIPS,
+  ARCH_MIPS64,
+};
+
+static inline bool ArchIs32Bit(ArchEnum arch) {
+  switch (arch) {
+    case ARCH_ARM:
+    case ARCH_X86:
+    case ARCH_MIPS:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_ARCH_H
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 472ed92..97614b1 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -25,6 +25,7 @@
 #include <unordered_map>
 #include <utility>
 
+#include <unwindstack/Arch.h>
 #include <unwindstack/ElfInterface.h>
 #include <unwindstack/Memory.h>
 
@@ -38,16 +39,6 @@
 struct MapInfo;
 class Regs;
 
-enum ArchEnum : uint8_t {
-  ARCH_UNKNOWN = 0,
-  ARCH_ARM,
-  ARCH_ARM64,
-  ARCH_X86,
-  ARCH_X86_64,
-  ARCH_MIPS,
-  ARCH_MIPS64,
-};
-
 class Elf {
  public:
   Elf(Memory* memory) : memory_(memory) {}
diff --git a/libunwindstack/include/unwindstack/Error.h b/libunwindstack/include/unwindstack/Error.h
index 66fefe7..0be4572 100644
--- a/libunwindstack/include/unwindstack/Error.h
+++ b/libunwindstack/include/unwindstack/Error.h
@@ -39,6 +39,27 @@
   ERROR_INVALID_ELF,          // Unwind in an invalid elf.
 };
 
+static inline const char* GetErrorCodeString(ErrorCode error) {
+  switch (error) {
+    case ERROR_NONE:
+      return "None";
+    case ERROR_MEMORY_INVALID:
+      return "Memory Invalid";
+    case ERROR_UNWIND_INFO:
+      return "Unwind Info";
+    case ERROR_UNSUPPORTED:
+      return "Unsupported";
+    case ERROR_INVALID_MAP:
+      return "Invalid Map";
+    case ERROR_MAX_FRAMES_EXCEEDED:
+      return "Maximum Frames Exceeded";
+    case ERROR_REPEATED_FRAME:
+      return "Repeated Frame";
+    case ERROR_INVALID_ELF:
+      return "Invalid Elf";
+  }
+}
+
 struct ErrorData {
   ErrorCode code;
   uint64_t address;  // Only valid when code is ERROR_MEMORY_INVALID.
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 5f42565..1a2a704 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -24,11 +24,12 @@
 #include <string>
 #include <vector>
 
+#include <unwindstack/Arch.h>
+
 namespace unwindstack {
 
 // Forward declarations.
 class Elf;
-enum ArchEnum : uint8_t;
 class Memory;
 
 class Regs {
@@ -52,7 +53,7 @@
 
   virtual ArchEnum Arch() = 0;
 
-  virtual bool Is32Bit() = 0;
+  bool Is32Bit() { return ArchIs32Bit(Arch()); }
 
   virtual void* RawData() = 0;
   virtual uint64_t pc() = 0;
@@ -96,8 +97,6 @@
       : Regs(total_regs, return_loc), regs_(total_regs) {}
   virtual ~RegsImpl() = default;
 
-  bool Is32Bit() override { return sizeof(AddressType) == sizeof(uint32_t); }
-
   inline AddressType& operator[](size_t reg) { return regs_[reg]; }
 
   void* RawData() override { return regs_.data(); }
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 3df8aad..b274c4c 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -24,6 +24,7 @@
 #include <string>
 #include <vector>
 
+#include <unwindstack/Arch.h>
 #include <unwindstack/DexFiles.h>
 #include <unwindstack/Error.h>
 #include <unwindstack/JitDebug.h>
@@ -35,7 +36,6 @@
 
 // Forward declarations.
 class Elf;
-enum ArchEnum : uint8_t;
 
 struct FrameData {
   size_t num;
@@ -64,7 +64,11 @@
 class Unwinder {
  public:
   Unwinder(size_t max_frames, Maps* maps, Regs* regs, std::shared_ptr<Memory> process_memory)
-      : max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) {
+      : max_frames_(max_frames),
+        maps_(maps),
+        regs_(regs),
+        process_memory_(process_memory),
+        arch_(regs->Arch()) {
     frames_.reserve(max_frames);
   }
   Unwinder(size_t max_frames, Maps* maps, std::shared_ptr<Memory> process_memory)
@@ -74,8 +78,8 @@
 
   virtual ~Unwinder() = default;
 
-  void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
-              const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
+  virtual void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
+                      const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
 
   size_t NumFrames() const { return frames_.size(); }
 
@@ -90,9 +94,14 @@
   std::string FormatFrame(size_t frame_num) const;
   std::string FormatFrame(const FrameData& frame) const;
 
-  void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
+  void SetArch(ArchEnum arch) { arch_ = arch; };
 
-  void SetRegs(Regs* regs) { regs_ = regs; }
+  void SetJitDebug(JitDebug* jit_debug);
+
+  void SetRegs(Regs* regs) {
+    regs_ = regs;
+    arch_ = regs_ != nullptr ? regs->Arch() : ARCH_UNKNOWN;
+  }
   Maps* GetMaps() { return maps_; }
   std::shared_ptr<Memory>& GetProcessMemory() { return process_memory_; }
 
@@ -107,11 +116,12 @@
 
   void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; }
 
-  void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
+  void SetDexFiles(DexFiles* dex_files);
 
   bool elf_from_memory_not_file() { return elf_from_memory_not_file_; }
 
   ErrorCode LastErrorCode() { return last_error_.code; }
+  const char* LastErrorCodeString() { return GetErrorCodeString(last_error_.code); }
   uint64_t LastErrorAddress() { return last_error_.address; }
   uint64_t warnings() { return warnings_; }
 
@@ -126,6 +136,15 @@
 
  protected:
   Unwinder(size_t max_frames) : max_frames_(max_frames) { frames_.reserve(max_frames); }
+  Unwinder(size_t max_frames, ArchEnum arch) : max_frames_(max_frames), arch_(arch) {
+    frames_.reserve(max_frames);
+  }
+
+  void ClearErrors() {
+    warnings_ = WARNING_NONE;
+    last_error_.code = ERROR_NONE;
+    last_error_.address = 0;
+  }
 
   void FillInDexFrame();
   FrameData* FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t pc_adjustment);
@@ -145,20 +164,27 @@
   bool elf_from_memory_not_file_ = false;
   ErrorData last_error_;
   uint64_t warnings_;
+  ArchEnum arch_ = ARCH_UNKNOWN;
 };
 
 class UnwinderFromPid : public Unwinder {
  public:
   UnwinderFromPid(size_t max_frames, pid_t pid) : Unwinder(max_frames), pid_(pid) {}
+  UnwinderFromPid(size_t max_frames, pid_t pid, ArchEnum arch)
+      : Unwinder(max_frames, arch), pid_(pid) {}
   virtual ~UnwinderFromPid() = default;
 
-  bool Init(ArchEnum arch);
+  bool Init();
+
+  void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
+              const std::vector<std::string>* map_suffixes_to_ignore = nullptr) override;
 
  private:
   pid_t pid_;
   std::unique_ptr<Maps> maps_ptr_;
   std::unique_ptr<JitDebug> jit_debug_ptr_;
   std::unique_ptr<DexFiles> dex_files_ptr_;
+  bool initted_ = false;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index c2bd836..0c6f9f8 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -314,7 +314,7 @@
 
   JitDebug jit_debug(process_memory_);
   Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
-  unwinder.SetJitDebug(&jit_debug, regs_->Arch());
+  unwinder.SetJitDebug(&jit_debug);
   unwinder.Unwind();
 
   std::string frame_info(DumpFrames(unwinder));
@@ -616,7 +616,7 @@
 
   JitDebug jit_debug(process_memory_);
   Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
-  unwinder.SetJitDebug(&jit_debug, regs_->Arch());
+  unwinder.SetJitDebug(&jit_debug);
   unwinder.Unwind();
 
   std::string frame_info(DumpFrames(unwinder));
@@ -939,7 +939,7 @@
   std::unique_ptr<Regs> regs_copy(leak_data->regs->Clone());
   JitDebug jit_debug(leak_data->process_memory);
   Unwinder unwinder(128, leak_data->maps, regs_copy.get(), leak_data->process_memory);
-  unwinder.SetJitDebug(&jit_debug, regs_copy->Arch());
+  unwinder.SetJitDebug(&jit_debug);
   unwinder.Unwind();
   ASSERT_EQ(76U, unwinder.NumFrames());
 }
@@ -1062,7 +1062,7 @@
 
   JitDebug jit_debug(process_memory_);
   Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
-  unwinder.SetJitDebug(&jit_debug, regs_->Arch());
+  unwinder.SetJitDebug(&jit_debug);
   unwinder.Unwind();
 
   std::string frame_info(DumpFrames(unwinder));
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index f76a101..b11d213 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -170,7 +170,6 @@
     unwinder.reset(new Unwinder(512, maps.get(), regs.get(), process_memory));
   } else {
     UnwinderFromPid* unwinder_from_pid = new UnwinderFromPid(512, getpid());
-    ASSERT_TRUE(unwinder_from_pid->Init(regs->Arch()));
     unwinder_from_pid->SetRegs(regs.get());
     unwinder.reset(unwinder_from_pid);
   }
@@ -283,7 +282,6 @@
   ASSERT_TRUE(regs.get() != nullptr);
 
   UnwinderFromPid unwinder(512, pid);
-  ASSERT_TRUE(unwinder.Init(regs->Arch()));
   unwinder.SetRegs(regs.get());
 
   VerifyUnwind(&unwinder, kFunctionOrder);
@@ -335,7 +333,6 @@
   ASSERT_TRUE(regs.get() != nullptr);
 
   UnwinderFromPid unwinder(512, *pid);
-  ASSERT_TRUE(unwinder.Init(regs->Arch()));
   unwinder.SetRegs(regs.get());
 
   VerifyUnwind(&unwinder, kFunctionOrder);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 915f248..8bae242 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -1182,7 +1182,7 @@
 
   DexFiles dex_files(process_memory_);
   Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
-  unwinder.SetDexFiles(&dex_files, ARCH_ARM);
+  unwinder.SetDexFiles(&dex_files);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
   EXPECT_EQ(WARNING_DEX_PC_NOT_IN_MAP, unwinder.warnings());
@@ -1735,7 +1735,7 @@
   regs.FakeSetArch(ARCH_ARM);
   JitDebug jit_debug(process_memory_);
   Unwinder unwinder(10, maps_.get(), &regs, process_memory_);
-  unwinder.SetJitDebug(&jit_debug, ARCH_ARM);
+  unwinder.SetJitDebug(&jit_debug);
 
   FrameData frame = unwinder.BuildFrameFromPcOnly(0x100310);
   EXPECT_EQ(0x10030eU, frame.pc);
@@ -1751,4 +1751,21 @@
   EXPECT_EQ(0xeU, frame.function_offset);
 }
 
+TEST_F(UnwinderTest, unwinder_from_pid_init_error) {
+  UnwinderFromPid unwinder(10, getpid());
+  ASSERT_DEATH(unwinder.Init(), "");
+}
+
+TEST_F(UnwinderTest, set_jit_debug_error) {
+  Unwinder unwinder(10, maps_.get(), process_memory_);
+  JitDebug jit_debug(process_memory_);
+  ASSERT_DEATH(unwinder.SetJitDebug(&jit_debug), "");
+}
+
+TEST_F(UnwinderTest, set_dex_files_error) {
+  Unwinder unwinder(10, maps_.get(), process_memory_);
+  DexFiles dex_files(process_memory_);
+  ASSERT_DEATH(unwinder.SetDexFiles(&dex_files), "");
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/VerifyBionicTerminationTest.cpp b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
index eb2b01d..3e67dc9 100644
--- a/libunwindstack/tests/VerifyBionicTerminationTest.cpp
+++ b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
@@ -94,7 +94,6 @@
   std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
 
   UnwinderFromPid unwinder(512, getpid());
-  ASSERT_TRUE(unwinder.Init(regs->Arch()));
   unwinder.SetRegs(regs.get());
 
   RegsGetLocal(regs.get());
diff --git a/libunwindstack/tests/fuzz/UnwinderFuzz.cpp b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
index 2f4986a..1600547 100644
--- a/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
+++ b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
@@ -85,7 +85,7 @@
 
   // Create instance
   Unwinder unwinder(max_frames, maps.get(), regs.get(), memory);
-  unwinder.SetJitDebug(jit_debug_ptr.get(), arch);
+  unwinder.SetJitDebug(jit_debug_ptr.get());
   unwinder.SetResolveNames(data_provider.ConsumeBool());
   // Call unwind
   PerformUnwind(&data_provider, &unwinder);
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index 1812e50..ae45f06 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -90,11 +90,6 @@
   printf("\n");
 
   unwindstack::UnwinderFromPid unwinder(1024, pid);
-  if (!unwinder.Init(regs->Arch())) {
-    printf("Failed to init unwinder object.\n");
-    return;
-  }
-
   unwinder.SetRegs(regs);
   unwinder.Unwind();
 
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 64b58a8..c44a121 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -248,10 +248,6 @@
   // Do an unwind so we know how much of the stack to save, and what
   // elf files are involved.
   unwindstack::UnwinderFromPid unwinder(1024, pid);
-  if (!unwinder.Init(regs->Arch())) {
-    printf("Unable to init unwinder object.\n");
-    return 1;
-  }
   unwinder.SetRegs(regs);
   uint64_t sp = regs->sp();
   unwinder.Unwind();
diff --git a/libutils/FuzzFormatTypes.h b/libutils/FuzzFormatTypes.h
new file mode 100644
index 0000000..5d58a1a
--- /dev/null
+++ b/libutils/FuzzFormatTypes.h
@@ -0,0 +1,45 @@
+#pragma once
+#include <string>
+
+static const std::string kFormatChars = std::string("duoxXfFeEgGaAcsp");
+static constexpr int32_t kMaxFormatFlagValue = INT16_MAX;
+enum FormatChar : uint8_t {
+    SIGNED_DECIMAL = 0,
+    UNSIGNED_DECIMAL = 1,
+    UNSIGNED_OCTAL = 2,
+    UNSIGNED_HEX_LOWER = 3,
+    UNSIGNED_HEX_UPPER = 4,
+    // Uppercase/lowercase floating point impacts 'inf', 'infinity', and 'nan'
+    FLOAT_LOWER = 5,
+    FLOAT_UPPER = 6,
+    // Upper/lower impacts the "e" in exponents.
+    EXPONENT_LOWER = 7,
+    EXPONENT_UPPER = 8,
+    // %g will use %e or %f, whichever is shortest
+    SHORT_EXP_LOWER = 9,
+    // %G will use %E or %F, whichever is shortest
+    SHORT_EXP_UPPER = 10,
+    HEX_FLOAT_LOWER = 11,
+    HEX_FLOAT_UPPER = 12,
+    CHAR = 13,
+    STRING = 14,
+    POINTER = 15,
+    // Used by libfuzzer
+    kMaxValue = POINTER
+};
+
+bool canApplyFlag(FormatChar formatChar, char modifier) {
+    if (modifier == '#') {
+        return formatChar == UNSIGNED_OCTAL || formatChar == UNSIGNED_HEX_LOWER ||
+               formatChar == UNSIGNED_HEX_UPPER || formatChar == FLOAT_LOWER ||
+               formatChar == FLOAT_UPPER || formatChar == SHORT_EXP_LOWER ||
+               formatChar == SHORT_EXP_UPPER;
+    } else if (modifier == '.') {
+        return formatChar == SIGNED_DECIMAL || formatChar == UNSIGNED_DECIMAL ||
+               formatChar == UNSIGNED_OCTAL || formatChar == UNSIGNED_HEX_LOWER ||
+               formatChar == UNSIGNED_HEX_UPPER || formatChar == FLOAT_LOWER ||
+               formatChar == FLOAT_UPPER || formatChar == SHORT_EXP_LOWER ||
+               formatChar == SHORT_EXP_UPPER || formatChar == STRING;
+    }
+    return true;
+}
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
index 2adfe98..b02683c 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/String8_fuzz.cpp
@@ -15,97 +15,199 @@
  */
 #include <functional>
 #include <iostream>
+#include <memory>
 
+#include "FuzzFormatTypes.h"
 #include "fuzzer/FuzzedDataProvider.h"
 #include "utils/String8.h"
 
 static constexpr int MAX_STRING_BYTES = 256;
 static constexpr uint8_t MAX_OPERATIONS = 50;
+// Interestingly, 2147483614 (INT32_MAX - 33) seems to be the max value that is handled for format
+// flags. Unfortunately we need to use a smaller value so we avoid consuming too much memory.
 
-std::vector<std::function<void(FuzzedDataProvider&, android::String8, android::String8)>>
+void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend);
+std::vector<std::function<void(FuzzedDataProvider*, android::String8*, android::String8*)>>
         operations = {
-
                 // Bytes and size
-                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
-                    str1.bytes();
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    str1->bytes();
                 },
-                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
-                    str1.isEmpty();
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    str1->isEmpty();
                 },
-                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
-                    str1.length();
-                },
-                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
-                    str1.size();
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    str1->length();
                 },
 
                 // Casing
-                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
-                    str1.toUpper();
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    str1->toUpper();
                 },
-                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
-                    str1.toLower();
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    str1->toLower();
                 },
-
-                [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
-                    str1.removeAll(str2.c_str());
+                [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
+                    str1->removeAll(str2->c_str());
                 },
-                [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
-                    str1.compare(str2);
+                [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
+                    const android::String8& constRef(*str2);
+                    str1->compare(constRef);
                 },
 
                 // Append and format
-                [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
-                    str1.append(str2);
+                [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
+                    str1->append(str2->c_str());
                 },
-                [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
-                    str1.appendFormat(str1.c_str(), str2.c_str());
-                },
-                [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
-                    str1.format(str1.c_str(), str2.c_str());
-                },
+                [](FuzzedDataProvider* dataProvider, android::String8* str1, android::String8*)
+                        -> void { fuzzFormat(dataProvider, str1, dataProvider->ConsumeBool()); },
 
                 // Find operation
-                [](FuzzedDataProvider& dataProvider, android::String8 str1,
-                   android::String8) -> void {
+                [](FuzzedDataProvider* dataProvider, android::String8* str1,
+                   android::String8* str2) -> void {
                     // We need to get a value from our fuzzer here.
-                    int start_index = dataProvider.ConsumeIntegralInRange<int>(0, str1.size());
-                    str1.find(str1.c_str(), start_index);
+                    int start_index = dataProvider->ConsumeIntegralInRange<int>(0, str1->size());
+                    str1->find(str2->c_str(), start_index);
                 },
 
                 // Path handling
-                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
-                    str1.getBasePath();
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    str1->getBasePath();
                 },
-                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
-                    str1.getPathExtension();
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    str1->getPathExtension();
                 },
-                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
-                    str1.getPathLeaf();
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    str1->getPathLeaf();
                 },
-                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
-                    str1.getPathDir();
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    str1->getPathDir();
                 },
-                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
-                    str1.convertToResPath();
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    str1->convertToResPath();
                 },
-                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
-                    android::String8 path_out_str = android::String8();
-                    str1.walkPath(&path_out_str);
-                    path_out_str.clear();
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    std::shared_ptr<android::String8> path_out_str =
+                            std::make_shared<android::String8>();
+                    str1->walkPath(path_out_str.get());
+                    path_out_str->clear();
                 },
-                [](FuzzedDataProvider& dataProvider, android::String8 str1,
-                   android::String8) -> void {
-                    str1.setPathName(dataProvider.ConsumeBytesWithTerminator<char>(5).data());
+                [](FuzzedDataProvider* dataProvider, android::String8* str1,
+                   android::String8*) -> void {
+                    str1->setPathName(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
                 },
-                [](FuzzedDataProvider& dataProvider, android::String8 str1,
-                   android::String8) -> void {
-                    str1.appendPath(dataProvider.ConsumeBytesWithTerminator<char>(5).data());
+                [](FuzzedDataProvider* dataProvider, android::String8* str1,
+                   android::String8*) -> void {
+                    str1->appendPath(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
                 },
 };
 
-void callFunc(uint8_t index, FuzzedDataProvider& dataProvider, android::String8 str1,
-              android::String8 str2) {
+void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend) {
+    FormatChar formatType = dataProvider->ConsumeEnum<FormatChar>();
+
+    std::string formatString("%");
+    // Width specifier
+    if (dataProvider->ConsumeBool()) {
+        // Left pad with zeroes
+        if (dataProvider->ConsumeBool()) {
+            formatString.push_back('0');
+        }
+        // Right justify (or left justify if negative)
+        int32_t justify = dataProvider->ConsumeIntegralInRange<int32_t>(-kMaxFormatFlagValue,
+                                                                        kMaxFormatFlagValue);
+        formatString += std::to_string(justify);
+    }
+
+    // The # specifier only works with o, x, X, a, A, e, E, f, F, g, and G
+    if (canApplyFlag(formatType, '#') && dataProvider->ConsumeBool()) {
+        formatString.push_back('#');
+    }
+
+    // Precision specifier
+    if (canApplyFlag(formatType, '.') && dataProvider->ConsumeBool()) {
+        formatString.push_back('.');
+        formatString +=
+                std::to_string(dataProvider->ConsumeIntegralInRange<int>(0, kMaxFormatFlagValue));
+    }
+
+    formatString.push_back(kFormatChars.at(static_cast<uint8_t>(formatType)));
+
+    switch (formatType) {
+        case SIGNED_DECIMAL: {
+            int val = dataProvider->ConsumeIntegral<int>();
+            if (shouldAppend) {
+                str1->appendFormat(formatString.c_str(), val);
+            } else {
+                str1->format(formatString.c_str(), dataProvider->ConsumeIntegral<int>());
+            }
+            break;
+        }
+
+        case UNSIGNED_DECIMAL:
+        case UNSIGNED_OCTAL:
+        case UNSIGNED_HEX_LOWER:
+        case UNSIGNED_HEX_UPPER: {
+            // Unsigned integers for u, o, x, and X
+            uint val = dataProvider->ConsumeIntegral<uint>();
+            if (shouldAppend) {
+                str1->appendFormat(formatString.c_str(), val);
+            } else {
+                str1->format(formatString.c_str(), val);
+            }
+            break;
+        }
+
+        case FLOAT_LOWER:
+        case FLOAT_UPPER:
+        case EXPONENT_LOWER:
+        case EXPONENT_UPPER:
+        case SHORT_EXP_LOWER:
+        case SHORT_EXP_UPPER:
+        case HEX_FLOAT_LOWER:
+        case HEX_FLOAT_UPPER: {
+            // Floating points for f, F, e, E, g, G, a, and A
+            float val = dataProvider->ConsumeFloatingPoint<float>();
+            if (shouldAppend) {
+                str1->appendFormat(formatString.c_str(), val);
+            } else {
+                str1->format(formatString.c_str(), val);
+            }
+            break;
+        }
+
+        case CHAR: {
+            char val = dataProvider->ConsumeIntegral<char>();
+            if (shouldAppend) {
+                str1->appendFormat(formatString.c_str(), val);
+            } else {
+                str1->format(formatString.c_str(), val);
+            }
+            break;
+        }
+
+        case STRING: {
+            std::string val = dataProvider->ConsumeRandomLengthString(MAX_STRING_BYTES);
+            if (shouldAppend) {
+                str1->appendFormat(formatString.c_str(), val.c_str());
+            } else {
+                str1->format(formatString.c_str(), val.c_str());
+            }
+            break;
+        }
+        case POINTER: {
+            uintptr_t val = dataProvider->ConsumeIntegral<uintptr_t>();
+            if (shouldAppend) {
+                str1->appendFormat(formatString.c_str(), val);
+            } else {
+                str1->format(formatString.c_str(), val);
+            }
+            break;
+        }
+    }
+}
+
+void callFunc(uint8_t index, FuzzedDataProvider* dataProvider, android::String8* str1,
+              android::String8* str2) {
     operations[index](dataProvider, str1, str2);
 }
 
@@ -120,14 +222,12 @@
     // Create UTF-8 pointers
     android::String8 str_one_utf8 = android::String8(vec.data());
     android::String8 str_two_utf8 = android::String8(vec_two.data());
-
     // Run operations against strings
     int opsRun = 0;
     while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
         uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
-        callFunc(op, dataProvider, str_one_utf8, str_two_utf8);
+        operations[op](&dataProvider, &str_one_utf8, &str_two_utf8);
     }
-
     // Just to be extra sure these can be freed, we're going to explicitly clear
     // them
     str_one_utf8.clear();