Merge "Load dex files from ART-specific data structure." am: 7f5615e8d4 am: 82aecc3b52
am: c3ceb9af84

Change-Id: Ib242ff3e27549dbfb0cf41f570c6eab468c1b2bf
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 1622e30..6dcc621 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -46,7 +46,7 @@
   std::vector<std::string> search_libs_{"libart.so", "libartd.so"};
   jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_));
 #if !defined(NO_LIBDEXFILE_SUPPORT)
-  dex_files_.reset(new unwindstack::DexFiles(process_memory_));
+  dex_files_.reset(new unwindstack::DexFiles(process_memory_, search_libs_));
 #endif
 
   if (!stack_maps_->Parse()) {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 74dfaa5..3354c90 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -128,6 +128,12 @@
         "DexFile.cpp",
         "DexFiles.cpp",
     ],
+    target: {
+        // Always disable optimizations for host to make it easier to debug.
+        host: {
+            cflags: ["-O0", "-g"],
+        },
+    },
 
     shared_libs: [
         "libbase",
@@ -151,8 +157,13 @@
     vendor_available: false,
     defaults: ["libunwindstack_flags"],
 
+    shared: {
+        enabled: false,
+    },
+
     srcs: [
         "tests/DexFileTest.cpp",
+        "tests/DexFilesTest.cpp",
     ],
     local_include_dirs: ["include"],
     allow_undefined_symbols: true,
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index be6c2f7..b4a992a 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -52,10 +52,20 @@
   return nullptr;
 }
 
-void DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
+DexFileFromFile::~DexFileFromFile() {
+  if (size_ != 0) {
+    munmap(mapped_memory_, size_);
+  }
+}
+
+bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
                                    uint64_t* method_offset) {
   if (dex_file_ == nullptr) {
-    return;
+    return false;
+  }
+
+  if (!dex_file_->IsInDataSection(dex_file_->Begin() + dex_offset)) {
+    return false;  // The DEX offset is not within the bytecode of this dex file.
   }
 
   for (uint32_t i = 0; i < dex_file_->NumClassDefs(); ++i) {
@@ -82,16 +92,11 @@
       if (offset <= dex_offset && dex_offset < offset + size) {
         *method_name = dex_file_->PrettyMethod(it.GetMemberIndex(), false);
         *method_offset = dex_offset - offset;
-        return;
+        return true;
       }
     }
   }
-}
-
-DexFileFromFile::~DexFileFromFile() {
-  if (size_ != 0) {
-    munmap(mapped_memory_, size_);
-  }
+  return false;
 }
 
 bool DexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) {
@@ -139,25 +144,41 @@
 }
 
 bool DexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, Memory* memory) {
-  art::DexFile::Header header;
-  if (!memory->ReadFully(dex_file_offset_in_memory, &header, sizeof(header))) {
+  memory_.resize(sizeof(art::DexFile::Header));
+  if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
     return false;
   }
 
-  if (!art::StandardDexFile::IsMagicValid(header.magic_) &&
-      !art::CompactDexFile::IsMagicValid(header.magic_)) {
+  art::DexFile::Header* header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
+  bool modify_data_off = false;
+  uint32_t file_size = header->file_size_;
+  if (art::CompactDexFile::IsMagicValid(header->magic_)) {
+    uint32_t computed_file_size;
+    if (__builtin_add_overflow(header->data_off_, header->data_size_, &computed_file_size)) {
+      return false;
+    }
+    if (computed_file_size > file_size) {
+      file_size = computed_file_size;
+      modify_data_off = true;
+    }
+  } else if (!art::StandardDexFile::IsMagicValid(header->magic_)) {
     return false;
   }
 
-  memory_.resize(header.file_size_);
-  if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), header.file_size_)) {
+  memory_.resize(file_size);
+  if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
     return false;
   }
 
+  header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
+  if (modify_data_off) {
+    header->data_off_ = header->file_size_;
+  }
+
   art::DexFileLoader loader;
   std::string error_msg;
   auto dex =
-      loader.Open(memory_.data(), header.file_size_, "", 0, nullptr, false, false, &error_msg);
+      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/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
index 22e98df..3ce2f1e 100644
--- a/libunwindstack/DexFile.h
+++ b/libunwindstack/DexFile.h
@@ -32,7 +32,7 @@
   DexFile() = default;
   virtual ~DexFile() = default;
 
-  void GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
+  bool GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
 
   static DexFile* Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info);
 
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index fe6d3c6..c5f8138 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -24,23 +24,138 @@
 
 #include <unwindstack/DexFiles.h>
 #include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 
 #include "DexFile.h"
 
 namespace unwindstack {
 
+struct DEXFileEntry32 {
+  uint32_t next;
+  uint32_t prev;
+  uint32_t dex_file;
+};
+
+struct DEXFileEntry64 {
+  uint64_t next;
+  uint64_t prev;
+  uint64_t dex_file;
+};
+
 DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : memory_(memory) {}
 
+DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+    : memory_(memory), search_libs_(search_libs) {}
+
 DexFiles::~DexFiles() {
   for (auto& entry : files_) {
     delete entry.second;
   }
 }
 
+void DexFiles::SetArch(ArchEnum arch) {
+  switch (arch) {
+    case ARCH_ARM:
+    case ARCH_MIPS:
+    case ARCH_X86:
+      read_entry_ptr_func_ = &DexFiles::ReadEntryPtr32;
+      read_entry_func_ = &DexFiles::ReadEntry32;
+      break;
+
+    case ARCH_ARM64:
+    case ARCH_MIPS64:
+    case ARCH_X86_64:
+      read_entry_ptr_func_ = &DexFiles::ReadEntryPtr64;
+      read_entry_func_ = &DexFiles::ReadEntry64;
+      break;
+
+    case ARCH_UNKNOWN:
+      abort();
+  }
+}
+
+uint64_t DexFiles::ReadEntryPtr32(uint64_t addr) {
+  uint32_t entry;
+  if (!memory_->ReadFully(addr, &entry, sizeof(entry))) {
+    return 0;
+  }
+  return entry;
+}
+
+uint64_t DexFiles::ReadEntryPtr64(uint64_t addr) {
+  uint64_t entry;
+  if (!memory_->ReadFully(addr, &entry, sizeof(entry))) {
+    return 0;
+  }
+  return entry;
+}
+
+bool DexFiles::ReadEntry32() {
+  DEXFileEntry32 entry;
+  if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
+    entry_addr_ = 0;
+    return false;
+  }
+
+  addrs_.push_back(entry.dex_file);
+  entry_addr_ = entry.next;
+  return true;
+}
+
+bool DexFiles::ReadEntry64() {
+  DEXFileEntry64 entry;
+  if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
+    entry_addr_ = 0;
+    return false;
+  }
+
+  addrs_.push_back(entry.dex_file);
+  entry_addr_ = entry.next;
+  return true;
+}
+
+void DexFiles::Init(Maps* maps) {
+  if (initialized_) {
+    return;
+  }
+  initialized_ = true;
+  entry_addr_ = 0;
+
+  const std::string dex_debug_name("__art_debug_dexfiles");
+  for (MapInfo* info : *maps) {
+    if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) {
+      continue;
+    }
+
+    if (!search_libs_.empty()) {
+      bool found = false;
+      const char* lib = basename(info->name.c_str());
+      for (const std::string& name : search_libs_) {
+        if (name == lib) {
+          found = true;
+          break;
+        }
+      }
+      if (!found) {
+        continue;
+      }
+    }
+
+    Elf* elf = info->GetElf(memory_, true);
+    uint64_t ptr;
+    // Find first non-empty list (libart might be loaded multiple times).
+    if (elf->GetGlobalVariable(dex_debug_name, &ptr) && ptr != 0) {
+      entry_addr_ = (this->*read_entry_ptr_func_)(ptr + info->start);
+      if (entry_addr_ != 0) {
+        break;
+      }
+    }
+  }
+}
+
 DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
   // Lock while processing the data.
-  std::lock_guard<std::mutex> guard(files_lock_);
   DexFile* dex_file;
   auto entry = files_.find(dex_file_offset);
   if (entry == files_.end()) {
@@ -52,14 +167,38 @@
   return dex_file;
 }
 
-void DexFiles::GetMethodInformation(uint64_t dex_file_offset, uint64_t dex_offset, MapInfo* info,
-                                    std::string* method_name, uint64_t* method_offset) {
-  DexFile* dex_file = GetDexFile(dex_file_offset, info);
-  if (dex_file != nullptr) {
-    dex_file->GetMethodInformation(dex_offset, method_name, method_offset);
+bool DexFiles::GetAddr(size_t index, uint64_t* addr) {
+  if (index < addrs_.size()) {
+    *addr = addrs_[index];
+    return true;
   }
+  if (entry_addr_ != 0 && (this->*read_entry_func_)()) {
+    *addr = addrs_.back();
+    return true;
+  }
+  return false;
 }
 
-void DexFiles::SetArch(ArchEnum) {}
+void DexFiles::GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc,
+                                    std::string* method_name, uint64_t* method_offset) {
+  std::lock_guard<std::mutex> guard(lock_);
+  if (!initialized_) {
+    Init(maps);
+  }
+
+  size_t index = 0;
+  uint64_t addr;
+  while (GetAddr(index++, &addr)) {
+    if (addr < info->start || addr >= info->end) {
+      continue;
+    }
+
+    DexFile* dex_file = GetDexFile(addr, info);
+    if (dex_file != nullptr &&
+        dex_file->GetMethodInformation(dex_pc - addr, method_name, method_offset)) {
+      break;
+    }
+  }
+}
 
 }  // namespace unwindstack
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
index d1dc0e6..0c319ec 100644
--- a/libunwindstack/JitDebug.cpp
+++ b/libunwindstack/JitDebug.cpp
@@ -172,7 +172,7 @@
   // Regardless of what happens below, consider the init finished.
   initialized_ = true;
 
-  std::string descriptor_name("__jit_debug_descriptor");
+  const std::string descriptor_name("__jit_debug_descriptor");
   for (MapInfo* info : *maps) {
     if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) {
       continue;
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index db8278e..6119ee0 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -53,49 +53,22 @@
   frame->pc = dex_pc;
   frame->sp = regs_->sp();
 
-  auto it = maps_->begin();
-  uint64_t rel_dex_pc;
-  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;
-  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->function_name, &dex_offset)) {
-    return;
-  }
-  frame->function_offset = dex_offset;
-  if (frame->function_name != "$dexfile") {
-    return;
-  }
+  MapInfo* info = maps_->Find(dex_pc);
+  frame->map_start = info->start;
+  frame->map_end = info->end;
+  frame->map_offset = info->offset;
+  frame->map_load_bias = info->load_bias;
+  frame->map_flags = info->flags;
+  frame->map_name = info->name;
+  frame->rel_pc = dex_pc - info->start;
 
 #if !defined(NO_LIBDEXFILE_SUPPORT)
-  dex_files_->GetMethodInformation(dex_pc - dex_offset, dex_offset, info, &frame->function_name,
+  if (dex_files_ == nullptr) {
+    return;
+  }
+
+  // dex_files_->GetMethodInformation(dex_pc - dex_offset, dex_offset, info, &frame->function_name,
+  dex_files_->GetMethodInformation(maps_, info, dex_pc, &frame->function_name,
                                    &frame->function_offset);
 #endif
 }
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
index 50c9c32..26f5d35 100644
--- a/libunwindstack/include/unwindstack/DexFiles.h
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -23,6 +23,7 @@
 #include <mutex>
 #include <string>
 #include <unordered_map>
+#include <vector>
 
 namespace unwindstack {
 
@@ -36,19 +37,40 @@
 class DexFiles {
  public:
   explicit DexFiles(std::shared_ptr<Memory>& memory);
+  DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
   ~DexFiles();
 
   DexFile* GetDexFile(uint64_t dex_file_offset, MapInfo* info);
 
-  void GetMethodInformation(uint64_t dex_file_offset, uint64_t dex_offset, MapInfo* info,
-                            std::string* method_name, uint64_t* method_offset);
+  void GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc, std::string* method_name,
+                            uint64_t* method_offset);
 
   void SetArch(ArchEnum arch);
 
  private:
+  void Init(Maps* maps);
+
+  bool GetAddr(size_t index, uint64_t* addr);
+
+  uint64_t ReadEntryPtr32(uint64_t addr);
+
+  uint64_t ReadEntryPtr64(uint64_t addr);
+
+  bool ReadEntry32();
+
+  bool ReadEntry64();
+
   std::shared_ptr<Memory> memory_;
-  std::mutex files_lock_;
+  std::vector<std::string> search_libs_;
+
+  std::mutex lock_;
+  bool initialized_ = false;
   std::unordered_map<uint64_t, DexFile*> files_;
+
+  uint64_t entry_addr_ = 0;
+  uint64_t (DexFiles::*read_entry_ptr_func_)(uint64_t) = nullptr;
+  bool (DexFiles::*read_entry_func_)() = nullptr;
+  std::vector<uint64_t> addrs_;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DexFileData.h b/libunwindstack/tests/DexFileData.h
new file mode 100644
index 0000000..6975c68
--- /dev/null
+++ b/libunwindstack/tests/DexFileData.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DEXFILESDATA_H
+#define _LIBUNWINDSTACK_DEXFILESDATA_H
+
+namespace unwindstack {
+
+// 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,
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DEXFILESDATA_H
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index d1338cb..6e05c5e 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -32,31 +32,11 @@
 
 #include "DexFile.h"
 
+#include "DexFileData.h"
 #include "MemoryFake.h"
 
 namespace unwindstack {
 
-// 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(DexFileTest, from_file_open_non_exist) {
   DexFileFromFile dex_file;
   ASSERT_FALSE(dex_file.Open(0, "/file/does/not/exist"));
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
new file mode 100644
index 0000000..dca5605
--- /dev/null
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -0,0 +1,311 @@
+/*
+ * 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 <elf.h>
+#include <string.h>
+
+#include <memory>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "DexFileData.h"
+#include "ElfFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class DexFilesTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+
+    dex_files_.reset(new DexFiles(process_memory_));
+    dex_files_->SetArch(ARCH_ARM);
+
+    maps_.reset(
+        new BufferMaps("1000-4000 ---s 00000000 00:00 0\n"
+                       "4000-6000 r--s 00000000 00:00 0\n"
+                       "6000-8000 -w-s 00000000 00:00 0\n"
+                       "a000-c000 r-xp 00000000 00:00 0\n"
+                       "c000-f000 rwxp 00000000 00:00 0\n"
+                       "f000-11000 r-xp 00000000 00:00 0\n"
+                       "100000-110000 rw-p 0000000 00:00 0\n"
+                       "200000-210000 rw-p 0000000 00:00 0\n"
+                       "300000-400000 rw-p 0000000 00:00 0\n"));
+    ASSERT_TRUE(maps_->Parse());
+
+    // Global variable in a section that is not readable/executable.
+    MapInfo* map_info = maps_->Get(kMapGlobalNonReadableExectable);
+    ASSERT_TRUE(map_info != nullptr);
+    MemoryFake* memory = new MemoryFake;
+    ElfFake* elf = new ElfFake(memory);
+    elf->FakeSetValid(true);
+    ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
+    elf->FakeSetInterface(interface);
+    interface->FakeSetGlobalVariable("__art_debug_dexfiles", 0x800);
+    map_info->elf.reset(elf);
+
+    // Global variable not set by default.
+    map_info = maps_->Get(kMapGlobalSetToZero);
+    ASSERT_TRUE(map_info != nullptr);
+    memory = new MemoryFake;
+    elf = new ElfFake(memory);
+    elf->FakeSetValid(true);
+    interface = new ElfInterfaceFake(memory);
+    elf->FakeSetInterface(interface);
+    interface->FakeSetGlobalVariable("__art_debug_dexfiles", 0x800);
+    map_info->elf.reset(elf);
+
+    // Global variable set in this map.
+    map_info = maps_->Get(kMapGlobal);
+    ASSERT_TRUE(map_info != nullptr);
+    memory = new MemoryFake;
+    elf = new ElfFake(memory);
+    elf->FakeSetValid(true);
+    interface = new ElfInterfaceFake(memory);
+    elf->FakeSetInterface(interface);
+    interface->FakeSetGlobalVariable("__art_debug_dexfiles", 0x800);
+    map_info->elf.reset(elf);
+  }
+
+  void WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev, uint32_t dex_file);
+  void WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev, uint64_t dex_file);
+  void WriteDex(uint64_t dex_file);
+
+  static constexpr size_t kMapGlobalNonReadableExectable = 3;
+  static constexpr size_t kMapGlobalSetToZero = 4;
+  static constexpr size_t kMapGlobal = 5;
+  static constexpr size_t kMapDexFileEntries = 7;
+  static constexpr size_t kMapDexFiles = 8;
+
+  std::shared_ptr<Memory> process_memory_;
+  MemoryFake* memory_;
+  std::unique_ptr<DexFiles> dex_files_;
+  std::unique_ptr<BufferMaps> maps_;
+};
+
+void DexFilesTest::WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev,
+                                uint32_t dex_file) {
+  // Format of the 32 bit DEXFileEntry structure:
+  //   uint32_t next
+  memory_->SetData32(entry_addr, next);
+  //   uint32_t prev
+  memory_->SetData32(entry_addr + 4, prev);
+  //   uint32_t dex_file
+  memory_->SetData32(entry_addr + 8, dex_file);
+}
+
+void DexFilesTest::WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev,
+                                uint64_t dex_file) {
+  // Format of the 64 bit DEXFileEntry structure:
+  //   uint64_t next
+  memory_->SetData64(entry_addr, next);
+  //   uint64_t prev
+  memory_->SetData64(entry_addr + 8, prev);
+  //   uint64_t dex_file
+  memory_->SetData64(entry_addr + 16, dex_file);
+}
+
+void DexFilesTest::WriteDex(uint64_t dex_file) {
+  memory_->SetMemory(dex_file, kDexData, sizeof(kDexData) * sizeof(uint32_t));
+}
+
+TEST_F(DexFilesTest, get_method_information_invalid) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFileEntries);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0, &method_name, &method_offset);
+  EXPECT_EQ("nothing", method_name);
+  EXPECT_EQ(0x124U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_32) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  memory_->SetData32(0xf800, 0x200000);
+  WriteEntry32(0x200000, 0, 0, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_64) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  dex_files_->SetArch(ARCH_ARM64);
+  memory_->SetData64(0xf800, 0x200000);
+  WriteEntry64(0x200000, 0, 0, 0x301000);
+  WriteDex(0x301000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x301102, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(2U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_not_first_entry_32) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  memory_->SetData32(0xf800, 0x200000);
+  WriteEntry32(0x200000, 0x200100, 0, 0x100000);
+  WriteEntry32(0x200100, 0, 0x200000, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(4U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_not_first_entry_64) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  dex_files_->SetArch(ARCH_ARM64);
+  memory_->SetData64(0xf800, 0x200000);
+  WriteEntry64(0x200000, 0x200100, 0, 0x100000);
+  WriteEntry64(0x200100, 0, 0x200000, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300106, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(6U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_cached) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  memory_->SetData32(0xf800, 0x200000);
+  WriteEntry32(0x200000, 0, 0, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+
+  // Clear all memory and make sure that data is acquired from the cache.
+  memory_->Clear();
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_search_libs) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  memory_->SetData32(0xf800, 0x200000);
+  WriteEntry32(0x200000, 0x200100, 0, 0x100000);
+  WriteEntry32(0x200100, 0, 0x200000, 0x300000);
+  WriteDex(0x300000);
+
+  // Only search a given named list of libs.
+  std::vector<std::string> libs{"libart.so"};
+  dex_files_.reset(new DexFiles(process_memory_, libs));
+  dex_files_->SetArch(ARCH_ARM);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+  EXPECT_EQ("nothing", method_name);
+  EXPECT_EQ(0x124U, method_offset);
+
+  MapInfo* map_info = maps_->Get(kMapGlobal);
+  map_info->name = "/system/lib/libart.so";
+  dex_files_.reset(new DexFiles(process_memory_, libs));
+  dex_files_->SetArch(ARCH_ARM);
+  // Make sure that clearing out copy of the libs doesn't affect the
+  // DexFiles object.
+  libs.clear();
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(4U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_global_skip_zero_32) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  // First global variable found, but value is zero.
+  memory_->SetData32(0xc800, 0);
+
+  memory_->SetData32(0xf800, 0x200000);
+  WriteEntry32(0x200000, 0, 0, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+
+  // Verify that second is ignored when first is set to non-zero
+  dex_files_.reset(new DexFiles(process_memory_));
+  dex_files_->SetArch(ARCH_ARM);
+  method_name = "fail";
+  method_offset = 0x123;
+  memory_->SetData32(0xc800, 0x100000);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("fail", method_name);
+  EXPECT_EQ(0x123U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  // First global variable found, but value is zero.
+  memory_->SetData64(0xc800, 0);
+
+  memory_->SetData64(0xf800, 0x200000);
+  WriteEntry64(0x200000, 0, 0, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->SetArch(ARCH_ARM64);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+
+  // Verify that second is ignored when first is set to non-zero
+  dex_files_.reset(new DexFiles(process_memory_));
+  dex_files_->SetArch(ARCH_ARM64);
+  method_name = "fail";
+  method_offset = 0x123;
+  memory_->SetData32(0xc800, 0x100000);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("fail", method_name);
+  EXPECT_EQ(0x123U, method_offset);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
index 37628f8..c1c45f8 100644
--- a/libunwindstack/tests/JitDebugTest.cpp
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -56,30 +56,30 @@
 
     MapInfo* map_info = maps_->Get(3);
     ASSERT_TRUE(map_info != nullptr);
-    elf_memories_.push_back(new MemoryFake);
-    ElfFake* elf = new ElfFake(elf_memories_.back());
+    MemoryFake* memory = new MemoryFake;
+    ElfFake* elf = new ElfFake(memory);
     elf->FakeSetValid(true);
-    ElfInterfaceFake* interface = new ElfInterfaceFake(elf_memories_.back());
+    ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
     elf->FakeSetInterface(interface);
     interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
     map_info->elf.reset(elf);
 
     map_info = maps_->Get(5);
     ASSERT_TRUE(map_info != nullptr);
-    elf_memories_.push_back(new MemoryFake);
-    elf = new ElfFake(elf_memories_.back());
+    memory = new MemoryFake;
+    elf = new ElfFake(memory);
     elf->FakeSetValid(true);
-    interface = new ElfInterfaceFake(elf_memories_.back());
+    interface = new ElfInterfaceFake(memory);
     elf->FakeSetInterface(interface);
     interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
     map_info->elf.reset(elf);
 
     map_info = maps_->Get(6);
     ASSERT_TRUE(map_info != nullptr);
-    elf_memories_.push_back(new MemoryFake);
-    elf = new ElfFake(elf_memories_.back());
+    memory = new MemoryFake;
+    elf = new ElfFake(memory);
     elf->FakeSetValid(true);
-    interface = new ElfInterfaceFake(elf_memories_.back());
+    interface = new ElfInterfaceFake(memory);
     elf->FakeSetInterface(interface);
     interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
     map_info->elf.reset(elf);
@@ -171,7 +171,6 @@
 
   std::shared_ptr<Memory> process_memory_;
   MemoryFake* memory_;
-  std::vector<MemoryFake*> elf_memories_;
   std::unique_ptr<JitDebug> jit_debug_;
   std::unique_ptr<BufferMaps> maps_;
 };
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index 07e48af..22ca7bf 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -26,6 +26,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <unwindstack/DexFiles.h>
 #include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
@@ -91,8 +92,13 @@
 
   auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
   unwindstack::Unwinder unwinder(128, &remote_maps, regs, process_memory);
+
   unwindstack::JitDebug jit_debug(process_memory);
   unwinder.SetJitDebug(&jit_debug, regs->Arch());
+
+  unwindstack::DexFiles dex_files(process_memory);
+  unwinder.SetDexFiles(&dex_files, regs->Arch());
+
   unwinder.Unwind();
 
   // Print the frames.