Merge "Add a graphics owner for graphics.h."
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index fb8a205..5323524 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -114,6 +114,7 @@
         "libbacktrace",
         "libunwind",
         "libunwindstack",
+        "libdexfile",
         "liblzma",
         "libcutils",
     ],
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index d9eed76..94b1935 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -50,6 +50,7 @@
     "BacktracePtrace.cpp",
     "thread_utils.c",
     "ThreadEntry.cpp",
+    "UnwindDexFile.cpp",
     "UnwindStack.cpp",
     "UnwindStackMap.cpp",
 ]
@@ -92,13 +93,27 @@
                 "liblog",
                 "libunwind",
                 "libunwindstack",
+                "libdexfile",
             ],
 
             static_libs: ["libcutils"],
+
+            // libdexfile will eventually properly export headers, for now
+            // include these directly.
+            include_dirs: [
+                "art/runtime",
+            ],
+
+            header_libs: [ "jni_headers", ],
         },
         android: {
             static_libs: ["libasync_safe"],
         },
+        vendor: {
+            cflags: ["-DNO_LIBDEXFILE"],
+            exclude_srcs: ["UnwindDexFile.cpp"],
+            exclude_shared_libs: ["libdexfile"],
+        },
     },
     whole_static_libs: ["libdemangle"],
 }
@@ -161,6 +176,8 @@
         "backtrace_test.cpp",
         "GetPss.cpp",
         "thread_utils.c",
+
+        "unwind_dex_test.cpp",
     ],
 
     cflags: [
@@ -172,6 +189,7 @@
     shared_libs: [
         "libbacktrace_test",
         "libbacktrace",
+        "libdexfile",
         "libbase",
         "libcutils",
         "liblog",
@@ -212,6 +230,12 @@
         },
     },
 
+    // libdexfile will eventually properly export headers, for now
+    // include these directly.
+    include_dirs: [
+        "art/runtime",
+    ],
+
     data: [
         "testdata/arm/*",
         "testdata/arm64/*",
diff --git a/libbacktrace/UnwindDexFile.cpp b/libbacktrace/UnwindDexFile.cpp
new file mode 100644
index 0000000..5780fbb
--- /dev/null
+++ b/libbacktrace/UnwindDexFile.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <android-base/unique_fd.h>
+
+#include <dex/code_item_accessors-no_art-inl.h>
+#include <dex/compact_dex_file.h>
+#include <dex/dex_file-inl.h>
+#include <dex/dex_file_loader.h>
+#include <dex/standard_dex_file.h>
+
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+
+#include "UnwindDexFile.h"
+
+UnwindDexFile* UnwindDexFile::Create(uint64_t dex_file_offset_in_memory,
+                                     unwindstack::Memory* memory, unwindstack::MapInfo* info) {
+  if (!info->name.empty()) {
+    std::unique_ptr<UnwindDexFileFromFile> dex_file(new UnwindDexFileFromFile);
+    if (dex_file->Open(dex_file_offset_in_memory - info->start + info->offset, info->name)) {
+      return dex_file.release();
+    }
+  }
+
+  std::unique_ptr<UnwindDexFileFromMemory> dex_file(new UnwindDexFileFromMemory);
+  if (dex_file->Open(dex_file_offset_in_memory, memory)) {
+    return dex_file.release();
+  }
+  return nullptr;
+}
+
+void UnwindDexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
+                                         uint64_t* method_offset) {
+  if (dex_file_ == nullptr) {
+    return;
+  }
+
+  for (uint32_t i = 0; i < dex_file_->NumClassDefs(); ++i) {
+    const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(i);
+    const uint8_t* class_data = dex_file_->GetClassData(class_def);
+    if (class_data == nullptr) {
+      continue;
+    }
+    for (art::ClassDataItemIterator it(*dex_file_.get(), class_data); it.HasNext(); it.Next()) {
+      if (!it.IsAtMethod()) {
+        continue;
+      }
+      const art::DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+      if (code_item == nullptr) {
+        continue;
+      }
+      art::CodeItemInstructionAccessor code(*dex_file_.get(), code_item);
+      if (!code.HasCodeItem()) {
+        continue;
+      }
+
+      uint64_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
+      size_t size = code.InsnsSizeInCodeUnits() * sizeof(uint16_t);
+      if (offset <= dex_offset && dex_offset < offset + size) {
+        *method_name = dex_file_->PrettyMethod(it.GetMemberIndex(), false);
+        *method_offset = dex_offset - offset;
+        return;
+      }
+    }
+  }
+}
+
+UnwindDexFileFromFile::~UnwindDexFileFromFile() {
+  if (size_ != 0) {
+    munmap(mapped_memory_, size_);
+  }
+}
+
+bool UnwindDexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) {
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
+  if (fd == -1) {
+    return false;
+  }
+  struct stat buf;
+  if (fstat(fd, &buf) == -1) {
+    return false;
+  }
+  uint64_t length;
+  if (buf.st_size < 0 ||
+      __builtin_add_overflow(dex_file_offset_in_file, sizeof(art::DexFile::Header), &length) ||
+      static_cast<uint64_t>(buf.st_size) < length) {
+    return false;
+  }
+
+  mapped_memory_ = mmap(nullptr, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+  if (mapped_memory_ == MAP_FAILED) {
+    return false;
+  }
+  size_ = buf.st_size;
+
+  uint8_t* memory = reinterpret_cast<uint8_t*>(mapped_memory_);
+
+  art::DexFile::Header* header =
+      reinterpret_cast<art::DexFile::Header*>(&memory[dex_file_offset_in_file]);
+  if (!art::StandardDexFile::IsMagicValid(header->magic_) &&
+      !art::CompactDexFile::IsMagicValid(header->magic_)) {
+    return false;
+  }
+
+  if (__builtin_add_overflow(dex_file_offset_in_file, header->file_size_, &length) ||
+      static_cast<uint64_t>(buf.st_size) < length) {
+    return false;
+  }
+
+  art::DexFileLoader loader;
+  std::string error_msg;
+  auto dex = loader.Open(&memory[dex_file_offset_in_file], header->file_size_, "", 0, nullptr,
+                         false, false, &error_msg);
+  dex_file_.reset(dex.release());
+  return dex_file_ != nullptr;
+}
+
+bool UnwindDexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, unwindstack::Memory* memory) {
+  art::DexFile::Header header;
+  if (!memory->ReadFully(dex_file_offset_in_memory, &header, sizeof(header))) {
+    return false;
+  }
+
+  if (!art::StandardDexFile::IsMagicValid(header.magic_) &&
+      !art::CompactDexFile::IsMagicValid(header.magic_)) {
+    return false;
+  }
+
+  memory_.resize(header.file_size_);
+  if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), header.file_size_)) {
+    return false;
+  }
+
+  art::DexFileLoader loader;
+  std::string error_msg;
+  auto dex =
+      loader.Open(memory_.data(), header.file_size_, "", 0, nullptr, false, false, &error_msg);
+  dex_file_.reset(dex.release());
+  return dex_file_ != nullptr;
+}
diff --git a/libbacktrace/UnwindDexFile.h b/libbacktrace/UnwindDexFile.h
new file mode 100644
index 0000000..dd70aba
--- /dev/null
+++ b/libbacktrace/UnwindDexFile.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_UNWIND_DEX_FILE_H
+#define _LIBBACKTRACE_UNWIND_DEX_FILE_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <dex/dex_file-inl.h>
+
+namespace unwindstack {
+class Memory;
+struct MapInfo;
+}  // namespace unwindstack
+
+class UnwindDexFile {
+ public:
+  UnwindDexFile() = default;
+  virtual ~UnwindDexFile() = default;
+
+  void GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
+
+  static UnwindDexFile* Create(uint64_t dex_file_offset_in_memory, unwindstack::Memory* memory,
+                               unwindstack::MapInfo* info);
+
+ protected:
+  std::unique_ptr<const art::DexFile> dex_file_;
+};
+
+class UnwindDexFileFromFile : public UnwindDexFile {
+ public:
+  UnwindDexFileFromFile() = default;
+  virtual ~UnwindDexFileFromFile();
+
+  bool Open(uint64_t dex_file_offset_in_file, const std::string& name);
+
+ private:
+  void* mapped_memory_ = nullptr;
+  size_t size_ = 0;
+};
+
+class UnwindDexFileFromMemory : public UnwindDexFile {
+ public:
+  UnwindDexFileFromMemory() = default;
+  virtual ~UnwindDexFileFromMemory() = default;
+
+  bool Open(uint64_t dex_file_offset_in_memory, unwindstack::Memory* memory);
+
+ private:
+  std::vector<uint8_t> memory_;
+};
+
+#endif  // _LIBBACKTRACE_UNWIND_DEX_FILE_H
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index a748d1b..91bcc66 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -40,9 +40,67 @@
 #include <unwindstack/Unwinder.h>
 
 #include "BacktraceLog.h"
+#ifndef NO_LIBDEXFILE
+#include "UnwindDexFile.h"
+#endif
 #include "UnwindStack.h"
 #include "UnwindStackMap.h"
 
+static void FillInDexFrame(UnwindStackMap* stack_map, uint64_t dex_pc,
+                           backtrace_frame_data_t* frame) {
+  // The DEX PC points into the .dex section within an ELF file.
+  // However, this is a BBS section manually mmaped to a .vdex file,
+  // so we need to get the following map to find the ELF data.
+  unwindstack::Maps* maps = stack_map->stack_maps();
+  auto it = maps->begin();
+  uint64_t rel_dex_pc;
+  unwindstack::MapInfo* info;
+  for (; it != maps->end(); ++it) {
+    auto entry = *it;
+    if (dex_pc >= entry->start && dex_pc < entry->end) {
+      info = entry;
+      rel_dex_pc = dex_pc - entry->start;
+      frame->map.start = entry->start;
+      frame->map.end = entry->end;
+      frame->map.offset = entry->offset;
+      frame->map.load_bias = entry->load_bias;
+      frame->map.flags = entry->flags;
+      frame->map.name = entry->name;
+      frame->rel_pc = rel_dex_pc;
+      break;
+    }
+  }
+  if (it == maps->end() || ++it == maps->end()) {
+    return;
+  }
+
+  auto entry = *it;
+  auto process_memory = stack_map->process_memory();
+  unwindstack::Elf* elf = entry->GetElf(process_memory, true);
+  if (!elf->valid()) {
+    return;
+  }
+
+  // Adjust the relative dex by the offset.
+  rel_dex_pc += entry->elf_offset;
+
+  uint64_t dex_offset;
+  if (!elf->GetFunctionName(rel_dex_pc, &frame->func_name, &dex_offset)) {
+    return;
+  }
+  frame->func_offset = dex_offset;
+  if (frame->func_name != "$dexfile") {
+    return;
+  }
+
+#ifndef NO_LIBDEXFILE
+  UnwindDexFile* dex_file = stack_map->GetDexFile(dex_pc - dex_offset, info);
+  if (dex_file != nullptr) {
+    dex_file->GetMethodInformation(dex_offset, &frame->func_name, &frame->func_offset);
+  }
+#endif
+}
+
 bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
                        std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
                        std::vector<std::string>* skip_names) {
@@ -60,19 +118,25 @@
     return true;
   }
 
-  frames->resize(unwinder.NumFrames() - num_ignore_frames);
   auto unwinder_frames = unwinder.frames();
+  // Get the real number of frames we'll need.
+  size_t total_frames = 0;
+  for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++, total_frames++) {
+    if (unwinder_frames[i].dex_pc != 0) {
+      total_frames++;
+    }
+  }
+  frames->resize(total_frames);
   size_t cur_frame = 0;
   for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++, cur_frame++) {
     auto frame = &unwinder_frames[i];
     backtrace_frame_data_t* back_frame = &frames->at(cur_frame);
 
-    back_frame->num = frame->num - num_ignore_frames;
+    back_frame->num = cur_frame;
 
     back_frame->rel_pc = frame->rel_pc;
     back_frame->pc = frame->pc;
     back_frame->sp = frame->sp;
-    back_frame->dex_pc = frame->dex_pc;
 
     back_frame->func_name = demangle(frame->function_name.c_str());
     back_frame->func_offset = frame->function_offset;
@@ -83,6 +147,19 @@
     back_frame->map.offset = frame->map_offset;
     back_frame->map.load_bias = frame->map_load_bias;
     back_frame->map.flags = frame->map_flags;
+
+    // Inject a frame that represents the dex pc data.
+    if (frame->dex_pc != 0) {
+      cur_frame++;
+      backtrace_frame_data_t* dex_frame = &frames->at(cur_frame);
+      dex_frame->num = cur_frame;
+      dex_frame->pc = frame->dex_pc;
+      dex_frame->rel_pc = frame->dex_pc;
+      dex_frame->sp = back_frame->sp;
+      dex_frame->stack_size = 0;
+      dex_frame->func_offset = 0;
+      FillInDexFrame(stack_map, frame->dex_pc, dex_frame);
+    }
   }
 
   return true;
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 60c7952..11ff84a 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -26,11 +26,20 @@
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
 
+#include "UnwindDexFile.h"
 #include "UnwindStackMap.h"
 
 //-------------------------------------------------------------------------
 UnwindStackMap::UnwindStackMap(pid_t pid) : BacktraceMap(pid) {}
 
+UnwindStackMap::~UnwindStackMap() {
+#ifndef NO_LIBDEXFILE
+  for (auto& entry : dex_files_) {
+    delete entry.second;
+  }
+#endif
+}
+
 bool UnwindStackMap::Build() {
   if (pid_ == 0) {
     pid_ = getpid();
@@ -118,6 +127,26 @@
   return process_memory_;
 }
 
+#ifdef NO_LIBDEXFILE
+UnwindDexFile* UnwindStackMap::GetDexFile(uint64_t, unwindstack::MapInfo*) {
+  return nullptr;
+}
+#else
+UnwindDexFile* UnwindStackMap::GetDexFile(uint64_t dex_file_offset, unwindstack::MapInfo* info) {
+  // Lock while we get the data.
+  std::lock_guard<std::mutex> guard(dex_lock_);
+  UnwindDexFile* dex_file;
+  auto entry = dex_files_.find(dex_file_offset);
+  if (entry == dex_files_.end()) {
+    dex_file = UnwindDexFile::Create(dex_file_offset, process_memory_.get(), info);
+    dex_files_[dex_file_offset] = dex_file;
+  } else {
+    dex_file = entry->second;
+  }
+  return dex_file;
+}
+#endif
+
 //-------------------------------------------------------------------------
 // BacktraceMap create function.
 //-------------------------------------------------------------------------
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index 6b98809..a815aae 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -21,15 +21,20 @@
 #include <sys/types.h>
 
 #include <memory>
+#include <mutex>
+#include <unordered_map>
 
 #include <backtrace/BacktraceMap.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
 
+// Forward declarations.
+class UnwindDexFile;
+
 class UnwindStackMap : public BacktraceMap {
  public:
   explicit UnwindStackMap(pid_t pid);
-  ~UnwindStackMap() = default;
+  ~UnwindStackMap();
 
   bool Build() override;
 
@@ -44,12 +49,18 @@
 
   unwindstack::JitDebug* GetJitDebug() { return jit_debug_.get(); }
 
+  UnwindDexFile* GetDexFile(uint64_t dex_file_offset, unwindstack::MapInfo* info);
+
  protected:
   uint64_t GetLoadBias(size_t index) override;
 
   std::unique_ptr<unwindstack::Maps> stack_maps_;
   std::shared_ptr<unwindstack::Memory> process_memory_;
   std::unique_ptr<unwindstack::JitDebug> jit_debug_;
+#ifndef NO_LIBDEXFILE
+  std::mutex dex_lock_;
+  std::unordered_map<uint64_t, UnwindDexFile*> dex_files_;
+#endif
 };
 
 #endif  // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 1b8ad19..18e9f61 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -81,7 +81,6 @@
   uint64_t rel_pc;        // The relative pc.
   uint64_t sp;            // The top of the stack.
   size_t stack_size;      // The size of the stack, zero indicate an unknown stack size.
-  uint64_t dex_pc;        // If non-zero, the Dex PC for the ART interpreter.
   backtrace_map_t map;    // The map associated with the given pc.
   std::string func_name;  // The function name associated with this pc, NULL if not found.
   uint64_t func_offset;  // pc relative to the start of the function, only valid if func_name is not
diff --git a/libbacktrace/unwind_dex_test.cpp b/libbacktrace/unwind_dex_test.cpp
new file mode 100644
index 0000000..449e662
--- /dev/null
+++ b/libbacktrace/unwind_dex_test.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unordered_map>
+
+#include <android-base/test_utils.h>
+
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+
+#include <dex/code_item_accessors-no_art-inl.h>
+#include <dex/standard_dex_file.h>
+
+#include <gtest/gtest.h>
+
+#include "UnwindDexFile.h"
+
+class MemoryFake : public unwindstack::Memory {
+ public:
+  MemoryFake() = default;
+  virtual ~MemoryFake() = default;
+
+  size_t Read(uint64_t addr, void* buffer, size_t size) override;
+
+  void SetMemory(uint64_t addr, const void* memory, size_t length);
+
+  void Clear() { data_.clear(); }
+
+ private:
+  std::unordered_map<uint64_t, uint8_t> data_;
+};
+
+void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
+  const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
+  for (size_t i = 0; i < length; i++, addr++) {
+    auto value = data_.find(addr);
+    if (value != data_.end()) {
+      value->second = src[i];
+    } else {
+      data_.insert({addr, src[i]});
+    }
+  }
+}
+
+size_t MemoryFake::Read(uint64_t addr, void* memory, size_t size) {
+  uint8_t* dst = reinterpret_cast<uint8_t*>(memory);
+  for (size_t i = 0; i < size; i++, addr++) {
+    auto value = data_.find(addr);
+    if (value == data_.end()) {
+      return i;
+    }
+    dst[i] = value->second;
+  }
+  return size;
+}
+
+// Borrowed from art/dex/dex_file_test.cc.
+static constexpr uint32_t kDexData[] = {
+    0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
+    0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
+    0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
+    0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
+    0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
+    0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
+    0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
+    0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
+    0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
+    0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
+    0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
+    0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
+    0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
+    0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
+    0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
+    0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
+    0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
+};
+
+TEST(UnwindDexTest, from_file_open_non_exist) {
+  UnwindDexFileFromFile dex_file;
+  ASSERT_FALSE(dex_file.Open(0, "/file/does/not/exist"));
+}
+
+TEST(UnwindDexTest, from_file_open_too_small) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_EQ(sizeof(art::DexFile::Header) - 2,
+            static_cast<size_t>(
+                TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header)) - 2)));
+
+  // Header too small.
+  UnwindDexFileFromFile dex_file;
+  ASSERT_FALSE(dex_file.Open(0, tf.path));
+
+  // Header correct, file too small.
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
+  ASSERT_EQ(sizeof(art::DexFile::Header), static_cast<size_t>(TEMP_FAILURE_RETRY(write(
+                                              tf.fd, kDexData, sizeof(art::DexFile::Header)))));
+  ASSERT_FALSE(dex_file.Open(0, tf.path));
+}
+
+TEST(UnwindDexTest, from_file_open) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_EQ(sizeof(kDexData),
+            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+  UnwindDexFileFromFile dex_file;
+  ASSERT_TRUE(dex_file.Open(0, tf.path));
+}
+
+TEST(UnwindDexTest, from_file_open_non_zero_offset) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_EQ(0x100, lseek(tf.fd, 0x100, SEEK_SET));
+  ASSERT_EQ(sizeof(kDexData),
+            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+  UnwindDexFileFromFile dex_file;
+  ASSERT_TRUE(dex_file.Open(0x100, tf.path));
+}
+
+TEST(UnwindDexTest, from_memory_fail_too_small_for_header) {
+  MemoryFake memory;
+
+  memory.SetMemory(0x1000, kDexData, sizeof(art::DexFile::Header) - 1);
+  UnwindDexFileFromMemory dex_file;
+
+  ASSERT_FALSE(dex_file.Open(0x1000, &memory));
+}
+
+TEST(UnwindDexTest, from_memory_fail_too_small_for_data) {
+  MemoryFake memory;
+
+  memory.SetMemory(0x1000, kDexData, sizeof(kDexData) - 2);
+  UnwindDexFileFromMemory dex_file;
+
+  ASSERT_FALSE(dex_file.Open(0x1000, &memory));
+}
+
+TEST(UnwindDexTest, from_memory_open) {
+  MemoryFake memory;
+
+  memory.SetMemory(0x1000, kDexData, sizeof(kDexData));
+  UnwindDexFileFromMemory dex_file;
+
+  ASSERT_TRUE(dex_file.Open(0x1000, &memory));
+}
+
+TEST(UnwindDexTest, create_using_file) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
+  ASSERT_EQ(sizeof(kDexData),
+            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+  MemoryFake memory;
+  unwindstack::MapInfo info(0, 0x10000, 0, 0x5, tf.path);
+  std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x500, &memory, &info));
+  ASSERT_TRUE(dex_file != nullptr);
+}
+
+TEST(UnwindDexTest, create_using_file_non_zero_start) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
+  ASSERT_EQ(sizeof(kDexData),
+            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+  MemoryFake memory;
+  unwindstack::MapInfo info(0x100, 0x10000, 0, 0x5, tf.path);
+  std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x600, &memory, &info));
+  ASSERT_TRUE(dex_file != nullptr);
+}
+
+TEST(UnwindDexTest, create_using_file_non_zero_offset) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
+  ASSERT_EQ(sizeof(kDexData),
+            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+  MemoryFake memory;
+  unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, tf.path);
+  std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x400, &memory, &info));
+  ASSERT_TRUE(dex_file != nullptr);
+}
+
+TEST(UnwindDexTest, create_using_memory_empty_file) {
+  MemoryFake memory;
+  memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+  unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+  std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x4000, &memory, &info));
+  ASSERT_TRUE(dex_file != nullptr);
+}
+
+TEST(UnwindDexTest, create_using_memory_file_does_not_exist) {
+  MemoryFake memory;
+  memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+  unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
+  std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x4000, &memory, &info));
+  ASSERT_TRUE(dex_file != nullptr);
+}
+
+TEST(UnwindDexTest, create_using_memory_file_is_malformed) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_EQ(sizeof(kDexData) - 10,
+            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData) - 10))));
+
+  MemoryFake memory;
+  memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+  unwindstack::MapInfo info(0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
+  std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x4000, &memory, &info));
+  ASSERT_TRUE(dex_file != nullptr);
+
+  // Check it came from memory by clearing memory and verifying it fails.
+  memory.Clear();
+  dex_file.reset(UnwindDexFile::Create(0x4000, &memory, &info));
+  ASSERT_TRUE(dex_file == nullptr);
+}
+
+TEST(UnwindDexTest, get_method_not_opened) {
+  std::string method("something");
+  uint64_t method_offset = 100;
+  UnwindDexFile dex_file;
+  dex_file.GetMethodInformation(0x100, &method, &method_offset);
+  EXPECT_EQ("something", method);
+  EXPECT_EQ(100U, method_offset);
+}
+
+TEST(UnwindDexTest, get_method) {
+  MemoryFake memory;
+  memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+  unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+  std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x4000, &memory, &info));
+  ASSERT_TRUE(dex_file != nullptr);
+
+  std::string method;
+  uint64_t method_offset;
+  dex_file->GetMethodInformation(0x102, &method, &method_offset);
+  EXPECT_EQ("Main.<init>", method);
+  EXPECT_EQ(2U, method_offset);
+
+  method = "not_in_a_method";
+  method_offset = 0x123;
+  dex_file->GetMethodInformation(0x100000, &method, &method_offset);
+  EXPECT_EQ("not_in_a_method", method);
+  EXPECT_EQ(0x123U, method_offset);
+}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 9389b40..8dae956 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -170,6 +170,8 @@
     data: [
         "tests/files/elf32.xz",
         "tests/files/elf64.xz",
+        "tests/files/offline/bad_eh_frame_hdr_arm64/*",
+        "tests/files/offline/debug_frame_first_x86/*",
         "tests/files/offline/jit_debug_arm32/*",
         "tests/files/offline/jit_debug_x86_32/*",
         "tests/files/offline/gnu_debugdata_arm32/*",
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index 0337dba..e0f1eed 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -64,6 +64,11 @@
     return false;
   }
 
+  if (fde_count_ == 0) {
+    last_error_ = DWARF_ERROR_NO_FDES;
+    return false;
+  }
+
   entries_offset_ = memory_.cur_offset();
   entries_end_ = offset + size;
   entries_data_offset_ = offset;
diff --git a/libunwindstack/DwarfError.h b/libunwindstack/DwarfError.h
index 54199b8..d3cdd4d 100644
--- a/libunwindstack/DwarfError.h
+++ b/libunwindstack/DwarfError.h
@@ -31,6 +31,7 @@
   DWARF_ERROR_TOO_MANY_ITERATIONS,
   DWARF_ERROR_CFA_NOT_DEFINED,
   DWARF_ERROR_UNSUPPORTED_VERSION,
+  DWARF_ERROR_NO_FDES,
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 91d855b..04a1fae 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -835,9 +835,8 @@
     }
 
     if (next_entry_offset < memory_.cur_offset()) {
-      // This indicates some kind of corruption, or malformed section data.
-      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
+      // Simply consider the processing done in this case.
+      break;
     }
     memory_.set_cur_offset(next_entry_offset);
   }
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 0e3ab2c..17cc16a 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -126,22 +126,26 @@
   if (eh_frame_hdr_offset_ != 0) {
     eh_frame_.reset(new DwarfEhFrameWithHdr<AddressType>(memory_));
     if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_)) {
-      // Even if the eh_frame_offset_ is non-zero, do not bother
-      // trying to read that since something has gone wrong.
       eh_frame_.reset(nullptr);
-      eh_frame_hdr_offset_ = 0;
-      eh_frame_hdr_size_ = static_cast<uint64_t>(-1);
     }
-  } else if (eh_frame_offset_ != 0) {
-    // If there is a eh_frame section without a eh_frame_hdr section.
+  }
+
+  if (eh_frame_.get() == nullptr && eh_frame_offset_ != 0) {
+    // If there is an eh_frame section without an eh_frame_hdr section,
+    // or using the frame hdr object failed to init.
     eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
     if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_)) {
       eh_frame_.reset(nullptr);
-      eh_frame_offset_ = 0;
-      eh_frame_size_ = static_cast<uint64_t>(-1);
     }
   }
 
+  if (eh_frame_.get() == nullptr) {
+    eh_frame_hdr_offset_ = 0;
+    eh_frame_hdr_size_ = static_cast<uint64_t>(-1);
+    eh_frame_offset_ = 0;
+    eh_frame_size_ = static_cast<uint64_t>(-1);
+  }
+
   if (debug_frame_offset_ != 0) {
     debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
     if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_)) {
@@ -436,15 +440,16 @@
   }
   uint64_t adjusted_pc = pc - load_bias;
 
-  // Try the eh_frame first.
-  DwarfSection* eh_frame = eh_frame_.get();
-  if (eh_frame != nullptr && eh_frame->Step(adjusted_pc, regs, process_memory, finished)) {
+  // Try the debug_frame first since it contains the most specific unwind
+  // information.
+  DwarfSection* debug_frame = debug_frame_.get();
+  if (debug_frame != nullptr && debug_frame->Step(adjusted_pc, regs, process_memory, finished)) {
     return true;
   }
 
-  // Try the debug_frame next.
-  DwarfSection* debug_frame = debug_frame_.get();
-  if (debug_frame != nullptr && debug_frame->Step(adjusted_pc, regs, process_memory, finished)) {
+  // Try the eh_frame next.
+  DwarfSection* eh_frame = eh_frame_.get();
+  if (eh_frame != nullptr && eh_frame->Step(adjusted_pc, regs, process_memory, finished)) {
     return true;
   }
 
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index 07204bc..243198e 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -145,6 +145,45 @@
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
 }
 
+TYPED_TEST_P(DwarfDebugFrameTest, Init32_do_not_fail_on_bad_next_entry) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 1);
+  this->memory_.SetData8(0x5009, '\0');
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0);
+  this->memory_.SetData32(0x5108, 0x1500);
+  this->memory_.SetData32(0x510c, 0x200);
+
+  this->memory_.SetData32(0x5200, 0xfc);
+  this->memory_.SetData32(0x5204, 0);
+  this->memory_.SetData32(0x5208, 0x2500);
+  this->memory_.SetData32(0x520c, 0x300);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0x5300, 0);
+  this->memory_.SetData32(0x5304, 0xffffffff);
+  this->memory_.SetData8(0x5308, 1);
+  this->memory_.SetData8(0x5309, '\0');
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5400, 0xfc);
+  this->memory_.SetData32(0x5404, 0x300);
+  this->memory_.SetData32(0x5408, 0x3500);
+  this->memory_.SetData32(0x540c, 0x400);
+
+  this->memory_.SetData32(0x5500, 0xfc);
+  this->memory_.SetData32(0x5504, 0x300);
+  this->memory_.SetData32(0x5508, 0x4500);
+  this->memory_.SetData32(0x550c, 0x500);
+
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(2U, this->debug_frame_->TestGetFdeCount());
+}
+
 TYPED_TEST_P(DwarfDebugFrameTest, Init64) {
   // CIE 64 information.
   this->memory_.SetData32(0x5000, 0xffffffff);
@@ -231,6 +270,51 @@
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
 }
 
+TYPED_TEST_P(DwarfDebugFrameTest, Init64_do_not_fail_on_bad_next_entry) {
+  // CIE 64 information.
+  this->memory_.SetData32(0x5000, 0xffffffff);
+  this->memory_.SetData64(0x5004, 0xf4);
+  this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
+  this->memory_.SetData8(0x5014, 1);
+  this->memory_.SetData8(0x5015, '\0');
+
+  // FDE 64 information.
+  this->memory_.SetData32(0x5100, 0xffffffff);
+  this->memory_.SetData64(0x5104, 0xf4);
+  this->memory_.SetData64(0x510c, 0);
+  this->memory_.SetData64(0x5114, 0x1500);
+  this->memory_.SetData64(0x511c, 0x200);
+
+  this->memory_.SetData32(0x5200, 0xffffffff);
+  this->memory_.SetData64(0x5204, 0xf4);
+  this->memory_.SetData64(0x520c, 0);
+  this->memory_.SetData64(0x5214, 0x2500);
+  this->memory_.SetData64(0x521c, 0x300);
+
+  // CIE 64 information.
+  this->memory_.SetData32(0x5300, 0xffffffff);
+  this->memory_.SetData64(0x5304, 0);
+  this->memory_.SetData64(0x530c, 0xffffffffffffffffULL);
+  this->memory_.SetData8(0x5314, 1);
+  this->memory_.SetData8(0x5315, '\0');
+
+  // FDE 64 information.
+  this->memory_.SetData32(0x5400, 0xffffffff);
+  this->memory_.SetData64(0x5404, 0xf4);
+  this->memory_.SetData64(0x540c, 0x300);
+  this->memory_.SetData64(0x5414, 0x3500);
+  this->memory_.SetData64(0x541c, 0x400);
+
+  this->memory_.SetData32(0x5500, 0xffffffff);
+  this->memory_.SetData64(0x5504, 0xf4);
+  this->memory_.SetData64(0x550c, 0x300);
+  this->memory_.SetData64(0x5514, 0x4500);
+  this->memory_.SetData64(0x551c, 0x500);
+
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(2U, this->debug_frame_->TestGetFdeCount());
+}
+
 TYPED_TEST_P(DwarfDebugFrameTest, Init_version1) {
   // CIE 32 information.
   this->memory_.SetData32(0x5000, 0xfc);
@@ -450,9 +534,11 @@
   EXPECT_EQ(0x20U, fde->cie->return_address_register);
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie, Init64,
-                           Init64_fde_not_following_cie, Init_version1, Init_version4,
-                           GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
+REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie,
+                           Init32_do_not_fail_on_bad_next_entry, Init64,
+                           Init64_do_not_fail_on_bad_next_entry, Init64_fde_not_following_cie,
+                           Init_version1, Init_version4, GetFdeOffsetFromPc, GetCieFde32,
+                           GetCieFde64);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 64b325b..ef2fb32 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -94,7 +94,13 @@
   EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
   EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
 
+  // Verify a zero fde count fails to init.
+  this->memory_.SetData32(0x1006, 0);
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+  ASSERT_EQ(DWARF_ERROR_NO_FDES, this->eh_frame_->last_error());
+
   // Verify an unexpected version will cause a fail.
+  this->memory_.SetData32(0x1006, 126);
   this->memory_.SetData8(0x1000, 0);
   ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
   ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 582ac18..09376ab 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -625,4 +625,127 @@
       frame_info);
 }
 
+// The eh_frame_hdr data is present but set to zero fdes. This should
+// fallback to iterating over the cies/fdes and ignore the eh_frame_hdr.
+// No .gnu_debugdata section in the elf file, so no symbols.
+TEST(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
+  std::string dir(TestGetFileDirectory() + "offline/bad_eh_frame_hdr_arm64/");
+
+  MemoryOffline* memory = new MemoryOffline;
+  ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
+
+  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
+  ASSERT_TRUE(fp != nullptr);
+  RegsArm64 regs;
+  uint64_t reg_value;
+  ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
+  regs[ARM64_REG_PC] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
+  regs[ARM64_REG_SP] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", &reg_value));
+  regs[ARM64_REG_LR] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "x29: %" SCNx64 "\n", &reg_value));
+  regs[ARM64_REG_R29] = reg_value;
+  regs.SetFromRaw();
+  fclose(fp);
+
+  fp = fopen((dir + "maps.txt").c_str(), "r");
+  ASSERT_TRUE(fp != nullptr);
+  // The file is guaranteed to be less than 4096 bytes.
+  std::vector<char> buffer(4096);
+  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
+  fclose(fp);
+
+  BufferMaps maps(buffer.data());
+  ASSERT_TRUE(maps.Parse());
+
+  ASSERT_EQ(ARCH_ARM64, regs.Arch());
+
+  std::shared_ptr<Memory> process_memory(memory);
+
+  char* cwd = getcwd(nullptr, 0);
+  ASSERT_EQ(0, chdir(dir.c_str()));
+  Unwinder unwinder(128, &maps, &regs, process_memory);
+  unwinder.Unwind();
+  ASSERT_EQ(0, chdir(cwd));
+  free(cwd);
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0000000000000550  waiter64\n"
+      "  #01 pc 0000000000000568  waiter64\n"
+      "  #02 pc 000000000000057c  waiter64\n"
+      "  #03 pc 0000000000000590  waiter64\n"
+      "  #04 pc 00000000000a8e98  libc.so (__libc_init+88)\n",
+      frame_info);
+}
+
+// The elf has bad eh_frame unwind information for the pcs. If eh_frame
+// is used first, the unwind will not match the expected output.
+TEST(UnwindOfflineTest, debug_frame_first_x86) {
+  std::string dir(TestGetFileDirectory() + "offline/debug_frame_first_x86/");
+
+  MemoryOffline* memory = new MemoryOffline;
+  ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
+
+  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
+  ASSERT_TRUE(fp != nullptr);
+  RegsX86 regs;
+  uint64_t reg_value;
+  ASSERT_EQ(1, fscanf(fp, "eax: %" SCNx64 "\n", &reg_value));
+  regs[X86_REG_EAX] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "ebx: %" SCNx64 "\n", &reg_value));
+  regs[X86_REG_EBX] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "ecx: %" SCNx64 "\n", &reg_value));
+  regs[X86_REG_ECX] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "edx: %" SCNx64 "\n", &reg_value));
+  regs[X86_REG_EDX] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "ebp: %" SCNx64 "\n", &reg_value));
+  regs[X86_REG_EBP] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "edi: %" SCNx64 "\n", &reg_value));
+  regs[X86_REG_EDI] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "esi: %" SCNx64 "\n", &reg_value));
+  regs[X86_REG_ESI] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "esp: %" SCNx64 "\n", &reg_value));
+  regs[X86_REG_ESP] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "eip: %" SCNx64 "\n", &reg_value));
+  regs[X86_REG_EIP] = reg_value;
+  regs.SetFromRaw();
+  fclose(fp);
+
+  fp = fopen((dir + "maps.txt").c_str(), "r");
+  ASSERT_TRUE(fp != nullptr);
+  // The file is guaranteed to be less than 4096 bytes.
+  std::vector<char> buffer(4096);
+  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
+  fclose(fp);
+
+  BufferMaps maps(buffer.data());
+  ASSERT_TRUE(maps.Parse());
+
+  ASSERT_EQ(ARCH_X86, regs.Arch());
+
+  std::shared_ptr<Memory> process_memory(memory);
+
+  char* cwd = getcwd(nullptr, 0);
+  ASSERT_EQ(0, chdir(dir.c_str()));
+  JitDebug jit_debug(process_memory);
+  Unwinder unwinder(128, &maps, &regs, process_memory);
+  unwinder.SetJitDebug(&jit_debug, regs.Arch());
+  unwinder.Unwind();
+  ASSERT_EQ(0, chdir(cwd));
+  free(cwd);
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 00000685  waiter (call_level3+53)\n"
+      "  #01 pc 000006b7  waiter (call_level2+23)\n"
+      "  #02 pc 000006d7  waiter (call_level1+23)\n"
+      "  #03 pc 000006f7  waiter (main+23)\n"
+      "  #04 pc 00018275  libc.so\n",
+      frame_info);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/libc.so b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/libc.so
new file mode 100644
index 0000000..78449bf
--- /dev/null
+++ b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/maps.txt b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/maps.txt
new file mode 100644
index 0000000..7cada15
--- /dev/null
+++ b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/maps.txt
@@ -0,0 +1,2 @@
+60a9fdf000-60a9fe0000 r-xp 0 00:00 0   waiter64
+7542cc0000-7542d8e000 r-xp 0 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/regs.txt b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/regs.txt
new file mode 100644
index 0000000..c24adbe
--- /dev/null
+++ b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/regs.txt
@@ -0,0 +1,4 @@
+pc: 60a9fdf550
+sp: 7fdd141990
+lr: 60a9fdf56c
+x29: 7fdd1419a0
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/stack.data b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/stack.data
new file mode 100644
index 0000000..b56d420
--- /dev/null
+++ b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64 b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64
new file mode 100644
index 0000000..81bda1d
--- /dev/null
+++ b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/libc.so b/libunwindstack/tests/files/offline/debug_frame_first_x86/libc.so
new file mode 100644
index 0000000..9c78790
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_first_x86/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/maps.txt b/libunwindstack/tests/files/offline/debug_frame_first_x86/maps.txt
new file mode 100644
index 0000000..74fc89f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_first_x86/maps.txt
@@ -0,0 +1,2 @@
+56598000-56599000 r-xp 0 00:00 0   waiter
+f7432000-f75e3000 r-xp 0 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/regs.txt b/libunwindstack/tests/files/offline/debug_frame_first_x86/regs.txt
new file mode 100644
index 0000000..48f4440
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_first_x86/regs.txt
@@ -0,0 +1,9 @@
+eax: 1d88ef8c
+ebx: 56599fe8
+ecx: 3
+edx: ffcf9ea4
+ebp: ffcf9e48
+edi: f75e5000
+esi: 1
+esp: ffcf9e38
+eip: 56598685
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/stack.data b/libunwindstack/tests/files/offline/debug_frame_first_x86/stack.data
new file mode 100644
index 0000000..0cf7d55
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_first_x86/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/waiter b/libunwindstack/tests/files/offline/debug_frame_first_x86/waiter
new file mode 100644
index 0000000..b1fc024
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_first_x86/waiter
Binary files differ
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index fd83ecc..b486a17 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -900,7 +900,16 @@
     downgrade_pressure = (int64_t)property_get_int32("ro.lmk.downgrade_pressure", 60);
     is_go_device = property_get_bool("ro.config.low_ram", false);
 
-    if (mlockall(MCL_CURRENT | MCL_FUTURE))
+    // MCL_ONFAULT pins pages as they fault instead of loading
+    // everything immediately all at once. (Which would be bad,
+    // because as of this writing, we have a lot of mapped pages we
+    // never use.) Old kernels will see MCL_ONFAULT and fail with
+    // EINVAL; we ignore this failure.
+    //
+    // N.B. read the man page for mlockall. MCL_CURRENT | MCL_ONFAULT
+    // pins ⊆ MCL_CURRENT, converging to just MCL_CURRENT as we fault
+    // in pages.
+    if (mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT) && errno != EINVAL)
         ALOGW("mlockall failed: errno=%d", errno);
 
     sched_setscheduler(0, SCHED_FIFO, &param);
diff --git a/usbd/usbd.rc b/usbd/usbd.rc
index c7838e8..809044a 100644
--- a/usbd/usbd.rc
+++ b/usbd/usbd.rc
@@ -2,4 +2,4 @@
     class late_start
     oneshot
     user root
-    group root usb
+    group root usb system