Merge "Remove FUSE logic; it's only a sdcardfs wrapper."
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 2a555af..b0345a1 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -50,6 +50,7 @@
   auto process_memory = stack_map->process_memory();
   unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
                                  regs, stack_map->process_memory());
+  unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
   unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore());
 
   if (num_ignore_frames >= unwinder.NumFrames()) {
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 836a774..93406dc 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -18,6 +18,9 @@
 #include <stdlib.h>
 #include <sys/types.h>
 
+#include <string>
+#include <vector>
+
 #include <backtrace/BacktraceMap.h>
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
@@ -39,6 +42,10 @@
   // Create the process memory object.
   process_memory_ = unwindstack::Memory::CreateProcessMemory(pid_);
 
+  // Create a JitDebug object for getting jit unwind information.
+  std::vector<std::string> search_libs_{"libart.so", "libartd.so"};
+  jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_));
+
   if (!stack_maps_->Parse()) {
     return false;
   }
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index 2f63655..12c5909 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -23,6 +23,7 @@
 #include <memory>
 
 #include <backtrace/BacktraceMap.h>
+#include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
 
 class UnwindStackMap : public BacktraceMap {
@@ -41,11 +42,14 @@
 
   const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; }
 
+  unwindstack::JitDebug* GetJitDebug() { return jit_debug_.get(); }
+
  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_;
 };
 
 #endif  // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index 339a06d..21fc7cc 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -17,6 +17,8 @@
 #ifndef _LIBS_LOG_LOG_MAIN_H
 #define _LIBS_LOG_LOG_MAIN_H
 
+#include <stdbool.h>
+
 #include <android/log.h>
 #include <sys/cdefs.h>
 
@@ -175,10 +177,10 @@
 #if LOG_NDEBUG
 #define ALOGV(...)          \
   do {                      \
-    if (0) {                \
+    if (false) {            \
       __ALOGV(__VA_ARGS__); \
     }                       \
-  } while (0)
+  } while (false)
 #else
 #define ALOGV(...) __ALOGV(__VA_ARGS__)
 #endif
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 133f3b9..484bc7d 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -55,6 +55,7 @@
         "Elf.cpp",
         "ElfInterface.cpp",
         "ElfInterfaceArm.cpp",
+        "JitDebug.cpp",
         "Log.cpp",
         "MapInfo.cpp",
         "Maps.cpp",
@@ -128,6 +129,7 @@
         "tests/ElfInterfaceTest.cpp",
         "tests/ElfTest.cpp",
         "tests/ElfTestUtils.cpp",
+        "tests/JitDebugTest.cpp",
         "tests/LogFake.cpp",
         "tests/MapInfoGetElfTest.cpp",
         "tests/MapInfoGetLoadBiasTest.cpp",
@@ -168,6 +170,7 @@
     data: [
         "tests/files/elf32.xz",
         "tests/files/elf64.xz",
+        "tests/files/offline/jit_debug_x86_32/*",
         "tests/files/offline/gnu_debugdata_arm32/*",
         "tests/files/offline/straddle_arm32/*",
         "tests/files/offline/straddle_arm64/*",
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 5ec4a3d..220e549 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -103,6 +103,37 @@
                                                      addr, load_bias_, name, func_offset)));
 }
 
+bool Elf::GetGlobalVariable(const std::string& name, uint64_t* memory_address) {
+  if (!valid_) {
+    return false;
+  }
+
+  if (!interface_->GetGlobalVariable(name, memory_address) &&
+      (gnu_debugdata_interface_ == nullptr ||
+       !gnu_debugdata_interface_->GetGlobalVariable(name, memory_address))) {
+    return false;
+  }
+
+  // Adjust by the load bias.
+  if (*memory_address < load_bias_) {
+    return false;
+  }
+
+  *memory_address -= load_bias_;
+
+  // If this winds up in the dynamic section, then we might need to adjust
+  // the address.
+  uint64_t dynamic_end = interface_->dynamic_vaddr() + interface_->dynamic_size();
+  if (*memory_address >= interface_->dynamic_vaddr() && *memory_address < dynamic_end) {
+    if (interface_->dynamic_vaddr() > interface_->dynamic_offset()) {
+      *memory_address -= interface_->dynamic_vaddr() - interface_->dynamic_offset();
+    } else {
+      *memory_address += interface_->dynamic_offset() - interface_->dynamic_vaddr();
+    }
+  }
+  return true;
+}
+
 // The relative pc is always relative to the start of the map from which it comes.
 bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
                Memory* process_memory, bool* finished) {
@@ -160,6 +191,23 @@
   }
 }
 
+bool Elf::IsValidPc(uint64_t pc) {
+  if (!valid_ || pc < load_bias_) {
+    return false;
+  }
+  pc -= load_bias_;
+
+  if (interface_->IsValidPc(pc)) {
+    return true;
+  }
+
+  if (gnu_debugdata_interface_ != nullptr && gnu_debugdata_interface_->IsValidPc(pc)) {
+    return true;
+  }
+
+  return false;
+}
+
 ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) {
   if (!IsValidElf(memory)) {
     return nullptr;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index df1642e..0e3ab2c 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -43,6 +43,30 @@
   }
 }
 
+bool ElfInterface::IsValidPc(uint64_t pc) {
+  if (!pt_loads_.empty()) {
+    for (auto& entry : pt_loads_) {
+      uint64_t start = entry.second.table_offset;
+      uint64_t end = start + entry.second.table_size;
+      if (pc >= start && pc < end) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  // No PT_LOAD data, look for a fde for this pc in the section data.
+  if (debug_frame_ != nullptr && debug_frame_->GetFdeFromPc(pc) != nullptr) {
+    return true;
+  }
+
+  if (eh_frame_ != nullptr && eh_frame_->GetFdeFromPc(pc) != nullptr) {
+    return true;
+  }
+
+  return false;
+}
+
 Memory* ElfInterface::CreateGnuDebugdataMemory() {
   if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) {
     return nullptr;
@@ -225,6 +249,10 @@
         return false;
       }
       dynamic_offset_ = phdr.p_offset;
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+        return false;
+      }
+      dynamic_vaddr_ = phdr.p_vaddr;
       if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
         return false;
       }
@@ -386,6 +414,20 @@
   return false;
 }
 
+template <typename SymType>
+bool ElfInterface::GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address) {
+  if (symbols_.empty()) {
+    return false;
+  }
+
+  for (const auto symbol : symbols_) {
+    if (symbol->GetGlobal<SymType>(memory_, name, memory_address)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 bool ElfInterface::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
                         bool* finished) {
   // Adjust the load bias to get the real relative pc.
@@ -451,6 +493,9 @@
 template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, uint64_t, std::string*,
                                                                    uint64_t*);
 
+template bool ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(const std::string&, uint64_t*);
+template bool ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(const std::string&, uint64_t*);
+
 template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
 template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);
 
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
new file mode 100644
index 0000000..1008439
--- /dev/null
+++ b/libunwindstack/JitDebug.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2017 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 <memory>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+// This implements the JIT Compilation Interface.
+// See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
+
+namespace unwindstack {
+
+struct JITCodeEntry32Pack {
+  uint32_t next;
+  uint32_t prev;
+  uint32_t symfile_addr;
+  uint64_t symfile_size;
+} __attribute__((packed));
+
+struct JITCodeEntry32Pad {
+  uint32_t next;
+  uint32_t prev;
+  uint32_t symfile_addr;
+  uint32_t pad;
+  uint64_t symfile_size;
+};
+
+struct JITCodeEntry64 {
+  uint64_t next;
+  uint64_t prev;
+  uint64_t symfile_addr;
+  uint64_t symfile_size;
+};
+
+struct JITDescriptorHeader {
+  uint32_t version;
+  uint32_t action_flag;
+};
+
+struct JITDescriptor32 {
+  JITDescriptorHeader header;
+  uint32_t relevant_entry;
+  uint32_t first_entry;
+};
+
+struct JITDescriptor64 {
+  JITDescriptorHeader header;
+  uint64_t relevant_entry;
+  uint64_t first_entry;
+};
+
+JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : memory_(memory) {}
+
+JitDebug::JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+    : memory_(memory), search_libs_(search_libs) {}
+
+JitDebug::~JitDebug() {
+  for (auto* elf : elf_list_) {
+    delete elf;
+  }
+}
+
+uint64_t JitDebug::ReadDescriptor32(uint64_t addr) {
+  JITDescriptor32 desc;
+  if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
+    return 0;
+  }
+
+  if (desc.header.version != 1 || desc.first_entry == 0) {
+    // Either unknown version, or no jit entries.
+    return 0;
+  }
+
+  return desc.first_entry;
+}
+
+uint64_t JitDebug::ReadDescriptor64(uint64_t addr) {
+  JITDescriptor64 desc;
+  if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
+    return 0;
+  }
+
+  if (desc.header.version != 1 || desc.first_entry == 0) {
+    // Either unknown version, or no jit entries.
+    return 0;
+  }
+
+  return desc.first_entry;
+}
+
+uint64_t JitDebug::ReadEntry32Pack(uint64_t* start, uint64_t* size) {
+  JITCodeEntry32Pack code;
+  if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
+    return 0;
+  }
+
+  *start = code.symfile_addr;
+  *size = code.symfile_size;
+  return code.next;
+}
+
+uint64_t JitDebug::ReadEntry32Pad(uint64_t* start, uint64_t* size) {
+  JITCodeEntry32Pad code;
+  if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
+    return 0;
+  }
+
+  *start = code.symfile_addr;
+  *size = code.symfile_size;
+  return code.next;
+}
+
+uint64_t JitDebug::ReadEntry64(uint64_t* start, uint64_t* size) {
+  JITCodeEntry64 code;
+  if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
+    return 0;
+  }
+
+  *start = code.symfile_addr;
+  *size = code.symfile_size;
+  return code.next;
+}
+
+void JitDebug::SetArch(ArchEnum arch) {
+  switch (arch) {
+    case ARCH_X86:
+      read_descriptor_func_ = &JitDebug::ReadDescriptor32;
+      read_entry_func_ = &JitDebug::ReadEntry32Pack;
+      break;
+
+    case ARCH_ARM:
+    case ARCH_MIPS:
+      read_descriptor_func_ = &JitDebug::ReadDescriptor32;
+      read_entry_func_ = &JitDebug::ReadEntry32Pad;
+      break;
+
+    case ARCH_ARM64:
+    case ARCH_X86_64:
+    case ARCH_MIPS64:
+      read_descriptor_func_ = &JitDebug::ReadDescriptor64;
+      read_entry_func_ = &JitDebug::ReadEntry64;
+      break;
+    case ARCH_UNKNOWN:
+      abort();
+  }
+}
+
+void JitDebug::Init(Maps* maps) {
+  if (initialized_) {
+    return;
+  }
+  // Regardless of what happens below, consider the init finished.
+  initialized_ = true;
+
+  std::string descriptor_name("__jit_debug_descriptor");
+  uint64_t descriptor_addr = 0;
+  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 (std::string& name : search_libs_) {
+        if (strcmp(name.c_str(), lib) == 0) {
+          found = true;
+          break;
+        }
+      }
+      if (!found) {
+        continue;
+      }
+    }
+
+    Elf* elf = info->GetElf(memory_, true);
+    if (elf->GetGlobalVariable(descriptor_name, &descriptor_addr)) {
+      descriptor_addr += info->start;
+      break;
+    }
+  }
+
+  if (descriptor_addr == 0) {
+    return;
+  }
+
+  entry_addr_ = (this->*read_descriptor_func_)(descriptor_addr);
+}
+
+Elf* JitDebug::GetElf(Maps* maps, uint64_t pc) {
+  // Use a single lock, this object should be used so infrequently that
+  // a fine grain lock is unnecessary.
+  std::lock_guard<std::mutex> guard(lock_);
+  if (!initialized_) {
+    Init(maps);
+  }
+
+  // Search the existing elf object first.
+  for (Elf* elf : elf_list_) {
+    if (elf->IsValidPc(pc)) {
+      return elf;
+    }
+  }
+
+  while (entry_addr_ != 0) {
+    uint64_t start;
+    uint64_t size;
+    entry_addr_ = (this->*read_entry_func_)(&start, &size);
+
+    Elf* elf = new Elf(new MemoryRange(memory_, start, size, 0));
+    elf->Init(true);
+    if (!elf->valid()) {
+      // The data is not formatted in a way we understand, do not attempt
+      // to process any other entries.
+      entry_addr_ = 0;
+      delete elf;
+      return nullptr;
+    }
+    elf_list_.push_back(elf);
+
+    if (elf->IsValidPc(pc)) {
+      return elf;
+    }
+  }
+  return nullptr;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 285f879..d4ba680 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -345,4 +345,26 @@
   return memory_->Read(addr, dst, size);
 }
 
+MemoryOfflineParts::~MemoryOfflineParts() {
+  for (auto memory : memories_) {
+    delete memory;
+  }
+}
+
+size_t MemoryOfflineParts::Read(uint64_t addr, void* dst, size_t size) {
+  if (memories_.empty()) {
+    return 0;
+  }
+
+  // Do a read on each memory object, no support for reading across the
+  // different memory objects.
+  for (MemoryOffline* memory : memories_) {
+    size_t bytes = memory->Read(addr, dst, size);
+    if (bytes != 0) {
+      return bytes;
+    }
+  }
+  return 0;
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
index 34f29bd..d05c3e2 100644
--- a/libunwindstack/RegsArm.cpp
+++ b/libunwindstack/RegsArm.cpp
@@ -37,10 +37,6 @@
 }
 
 uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid()) {
-    return rel_pc;
-  }
-
   uint64_t load_bias = elf->GetLoadBias();
   if (rel_pc < load_bias) {
     return rel_pc;
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index b4b92d6..25def40 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -108,8 +108,35 @@
   return return_value;
 }
 
+template <typename SymType>
+bool Symbols::GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address) {
+  uint64_t cur_offset = offset_;
+  while (cur_offset + entry_size_ <= end_) {
+    SymType entry;
+    if (!elf_memory->ReadFully(cur_offset, &entry, sizeof(entry))) {
+      return false;
+    }
+    cur_offset += entry_size_;
+
+    if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_OBJECT &&
+        ELF32_ST_BIND(entry.st_info) == STB_GLOBAL) {
+      uint64_t str_offset = str_offset_ + entry.st_name;
+      if (str_offset < str_end_) {
+        std::string symbol;
+        if (elf_memory->ReadString(str_offset, &symbol, str_end_ - str_offset) && symbol == name) {
+          *memory_address = entry.st_value;
+          return true;
+        }
+      }
+    }
+  }
+  return false;
+}
+
 // Instantiate all of the needed template functions.
 template bool Symbols::GetName<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
 template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
 
+template bool Symbols::GetGlobal<Elf32_Sym>(Memory*, const std::string&, uint64_t*);
+template bool Symbols::GetGlobal<Elf64_Sym>(Memory*, const std::string&, uint64_t*);
 }  // namespace unwindstack
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index 689144b..7d239c1 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -47,6 +47,9 @@
   bool GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
                uint64_t* func_offset);
 
+  template <typename SymType>
+  bool GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address);
+
   void ClearCache() {
     symbols_.clear();
     cur_offset_ = offset_;
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index a83f85b..b0a1c0c 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -27,12 +27,13 @@
 #include <android-base/stringprintf.h>
 
 #include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Unwinder.h>
 
 namespace unwindstack {
 
-void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc) {
+void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t func_pc) {
   size_t frame_num = frames_.size();
   frames_.resize(frame_num + 1);
   FrameData* frame = &frames_.at(frame_num);
@@ -53,7 +54,7 @@
   frame->map_flags = map_info->flags;
   frame->map_load_bias = elf->GetLoadBias();
 
-  if (!elf->GetFunctionName(frame->rel_pc, &frame->function_name, &frame->function_offset)) {
+  if (!elf->GetFunctionName(func_pc, &frame->function_name, &frame->function_offset)) {
     frame->function_name = "";
     frame->function_offset = 0;
   }
@@ -79,17 +80,20 @@
 
   bool return_address_attempt = false;
   bool adjust_pc = false;
+  std::unique_ptr<JitDebug> jit_debug;
   for (; frames_.size() < max_frames_;) {
     uint64_t cur_pc = regs_->pc();
     uint64_t cur_sp = regs_->sp();
 
     MapInfo* map_info = maps_->Find(regs_->pc());
     uint64_t rel_pc;
+    uint64_t adjusted_pc;
     uint64_t adjusted_rel_pc;
     Elf* elf;
     if (map_info == nullptr) {
       rel_pc = regs_->pc();
       adjusted_rel_pc = rel_pc;
+      adjusted_pc = rel_pc;
     } else {
       if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
         break;
@@ -97,16 +101,30 @@
       elf = map_info->GetElf(process_memory_, true);
       rel_pc = elf->GetRelPc(regs_->pc(), map_info);
       if (adjust_pc) {
-        adjusted_rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
+        adjusted_pc = regs_->GetAdjustedPc(rel_pc, elf);
       } else {
-        adjusted_rel_pc = rel_pc;
+        adjusted_pc = rel_pc;
+      }
+      adjusted_rel_pc = adjusted_pc;
+
+      // If the pc is in an invalid elf file, try and get an Elf object
+      // using the jit debug information.
+      if (!elf->valid() && jit_debug_ != nullptr) {
+        uint64_t adjusted_jit_pc = regs_->pc() - (rel_pc - adjusted_pc);
+        Elf* jit_elf = jit_debug_->GetElf(maps_, adjusted_jit_pc);
+        if (jit_elf != nullptr) {
+          // The jit debug information requires a non relative adjusted pc.
+          adjusted_pc = adjusted_jit_pc;
+          adjusted_rel_pc = adjusted_pc - map_info->start;
+          elf = jit_elf;
+        }
       }
     }
 
     if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
         std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
                   basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
-      FillInFrame(map_info, elf, adjusted_rel_pc);
+      FillInFrame(map_info, elf, adjusted_rel_pc, adjusted_pc);
 
       // Once a frame is added, stop skipping frames.
       initial_map_names_to_skip = nullptr;
@@ -134,7 +152,7 @@
           in_device_map = true;
         } else {
           bool finished;
-          stepped = elf->Step(rel_pc, adjusted_rel_pc, map_info->elf_offset, regs_,
+          stepped = elf->Step(rel_pc, adjusted_pc, map_info->elf_offset, regs_,
                               process_memory_.get(), &finished);
           if (stepped && finished) {
             break;
@@ -174,13 +192,13 @@
   if (frame_num >= frames_.size()) {
     return "";
   }
-  return FormatFrame(frames_[frame_num], regs_->Format32Bit());
+  return FormatFrame(frames_[frame_num], regs_->Is32Bit());
 }
 
-std::string Unwinder::FormatFrame(const FrameData& frame, bool bits32) {
+std::string Unwinder::FormatFrame(const FrameData& frame, bool is32bit) {
   std::string data;
 
-  if (bits32) {
+  if (is32bit) {
     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);
@@ -208,4 +226,9 @@
   return data;
 }
 
+void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) {
+  jit_debug->SetArch(arch);
+  jit_debug_ = jit_debug;
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index a85e5f4..5f34391 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -59,6 +59,8 @@
 
   bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
 
+  bool GetGlobalVariable(const std::string& name, uint64_t* memory_address);
+
   uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
 
   bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
@@ -68,6 +70,8 @@
 
   uint64_t GetLoadBias() { return load_bias_; }
 
+  bool IsValidPc(uint64_t pc);
+
   bool valid() { return valid_; }
 
   uint32_t machine_type() { return machine_type_; }
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 5d3cd5e..faa61ee 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -60,9 +60,13 @@
   virtual bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
                                uint64_t* offset) = 0;
 
+  virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0;
+
   virtual bool Step(uint64_t rel_pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
                     bool* finished);
 
+  virtual bool IsValidPc(uint64_t pc);
+
   Memory* CreateGnuDebugdataMemory();
 
   Memory* memory() { return memory_; }
@@ -72,6 +76,7 @@
   void SetGnuDebugdataInterface(ElfInterface* interface) { gnu_debugdata_interface_ = interface; }
 
   uint64_t dynamic_offset() { return dynamic_offset_; }
+  uint64_t dynamic_vaddr() { return dynamic_vaddr_; }
   uint64_t dynamic_size() { return dynamic_size_; }
   uint64_t eh_frame_hdr_offset() { return eh_frame_hdr_offset_; }
   uint64_t eh_frame_hdr_size() { return eh_frame_hdr_size_; }
@@ -108,6 +113,9 @@
   bool GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, std::string* name,
                                    uint64_t* func_offset);
 
+  template <typename SymType>
+  bool GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address);
+
   virtual bool HandleType(uint64_t, uint32_t, uint64_t) { return false; }
 
   template <typename EhdrType>
@@ -118,6 +126,7 @@
 
   // Stored elf data.
   uint64_t dynamic_offset_ = 0;
+  uint64_t dynamic_vaddr_ = 0;
   uint64_t dynamic_size_ = 0;
 
   uint64_t eh_frame_hdr_offset_ = 0;
@@ -163,6 +172,10 @@
     return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, load_bias, name, func_offset);
   }
 
+  bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
+    return ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(name, memory_address);
+  }
+
   static void GetMaxSize(Memory* memory, uint64_t* size) {
     GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
   }
@@ -188,6 +201,10 @@
     return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, load_bias, name, func_offset);
   }
 
+  bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
+    return ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(name, memory_address);
+  }
+
   static void GetMaxSize(Memory* memory, uint64_t* size) {
     GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
   }
diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h
new file mode 100644
index 0000000..0bcd0b0
--- /dev/null
+++ b/libunwindstack/include/unwindstack/JitDebug.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 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_JIT_DEBUG_H
+#define _LIBUNWINDSTACK_JIT_DEBUG_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+class Maps;
+class Memory;
+enum ArchEnum : uint8_t;
+
+class JitDebug {
+ public:
+  explicit JitDebug(std::shared_ptr<Memory>& memory);
+  JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
+  ~JitDebug();
+
+  Elf* GetElf(Maps* maps, uint64_t pc);
+
+  void SetArch(ArchEnum arch);
+
+ private:
+  void Init(Maps* maps);
+
+  std::shared_ptr<Memory> memory_;
+  uint64_t entry_addr_ = 0;
+  bool initialized_ = false;
+  std::vector<Elf*> elf_list_;
+  std::vector<std::string> search_libs_;
+
+  std::mutex lock_;
+
+  uint64_t (JitDebug::*read_descriptor_func_)(uint64_t) = nullptr;
+  uint64_t (JitDebug::*read_entry_func_)(uint64_t*, uint64_t*) = nullptr;
+
+  uint64_t ReadDescriptor32(uint64_t);
+  uint64_t ReadDescriptor64(uint64_t);
+
+  uint64_t ReadEntry32Pack(uint64_t* start, uint64_t* size);
+  uint64_t ReadEntry32Pad(uint64_t* start, uint64_t* size);
+  uint64_t ReadEntry64(uint64_t* start, uint64_t* size);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_JIT_DEBUG_H
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index 94ceaab..19bce04 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -151,6 +151,19 @@
   std::unique_ptr<MemoryRange> memory_;
 };
 
+class MemoryOfflineParts : public Memory {
+ public:
+  MemoryOfflineParts() = default;
+  virtual ~MemoryOfflineParts();
+
+  void Add(MemoryOffline* memory) { memories_.push_back(memory); }
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+  std::vector<MemoryOffline*> memories_;
+};
+
 }  // namespace unwindstack
 
 #endif  // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 613682f..1904d4d 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -51,7 +51,7 @@
 
   virtual ArchEnum Arch() = 0;
 
-  virtual bool Format32Bit() = 0;
+  virtual bool Is32Bit() = 0;
 
   virtual void* RawData() = 0;
   virtual uint64_t pc() = 0;
@@ -94,7 +94,7 @@
   void set_pc(AddressType pc) { pc_ = pc; }
   void set_sp(AddressType sp) { sp_ = sp; }
 
-  bool Format32Bit() override { return sizeof(AddressType) == sizeof(uint32_t); }
+  bool Is32Bit() override { return sizeof(AddressType) == sizeof(uint32_t); }
 
   inline AddressType& operator[](size_t reg) { return regs_[reg]; }
 
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index b64d460..5adec4f 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -32,6 +32,8 @@
 
 // Forward declarations.
 class Elf;
+class JitDebug;
+enum ArchEnum : uint8_t;
 
 struct FrameData {
   size_t num;
@@ -67,16 +69,19 @@
   const std::vector<FrameData>& frames() { return frames_; }
 
   std::string FormatFrame(size_t frame_num);
-  static std::string FormatFrame(const FrameData& frame, bool bits32);
+  static std::string FormatFrame(const FrameData& frame, bool is32bit);
+
+  void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
 
  private:
-  void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc);
+  void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t adjusted_pc);
 
   size_t max_frames_;
   Maps* maps_;
   Regs* regs_;
   std::vector<FrameData> frames_;
   std::shared_ptr<Memory> process_memory_;
+  JitDebug* jit_debug_ = nullptr;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index 68de797..ae9da5e 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -43,6 +43,15 @@
   return true;
 }
 
+bool ElfInterfaceFake::GetGlobalVariable(const std::string& global, uint64_t* offset) {
+  auto entry = globals_.find(global);
+  if (entry == globals_.end()) {
+    return false;
+  }
+  *offset = entry->second;
+  return true;
+}
+
 bool ElfInterfaceFake::Step(uint64_t, uint64_t, Regs* regs, Memory*, bool* finished) {
   if (steps_.empty()) {
     return false;
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index abf9927..099026c 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -21,6 +21,7 @@
 
 #include <deque>
 #include <string>
+#include <unordered_map>
 
 #include <unwindstack/Elf.h>
 #include <unwindstack/ElfInterface.h>
@@ -55,6 +56,9 @@
   void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; }
 
   void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); }
+  void FakeSetGnuDebugdataInterface(ElfInterface* interface) {
+    gnu_debugdata_interface_.reset(interface);
+  }
 };
 
 class ElfInterfaceFake : public ElfInterface {
@@ -67,9 +71,14 @@
   bool GetSoname(std::string*) override { return false; }
 
   bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override;
+  bool GetGlobalVariable(const std::string&, uint64_t*) override;
 
   bool Step(uint64_t, uint64_t, Regs*, Memory*, bool*) override;
 
+  void FakeSetGlobalVariable(const std::string& global, uint64_t offset) {
+    globals_[global] = offset;
+  }
+
   static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
   static void FakePushStepData(const StepData data) { steps_.push_back(data); }
 
@@ -79,6 +88,8 @@
   }
 
  private:
+  std::unordered_map<std::string, uint64_t> globals_;
+
   static std::deque<FunctionData> functions_;
   static std::deque<StepData> steps_;
 };
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index e138c3a..042c5fb 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -958,4 +958,189 @@
   InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
 }
 
+TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) {
+  std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+  Elf32_Ehdr ehdr;
+  memset(&ehdr, 0, sizeof(ehdr));
+  ehdr.e_phoff = 0x100;
+  ehdr.e_phnum = 1;
+  ehdr.e_phentsize = sizeof(Elf32_Phdr);
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  Elf32_Phdr phdr;
+  memset(&phdr, 0, sizeof(phdr));
+  phdr.p_type = PT_LOAD;
+  phdr.p_vaddr = 0;
+  phdr.p_memsz = 0x10000;
+  phdr.p_flags = PF_R | PF_X;
+  phdr.p_align = 0x1000;
+  memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
+  EXPECT_TRUE(elf->IsValidPc(0));
+  EXPECT_TRUE(elf->IsValidPc(0x5000));
+  EXPECT_TRUE(elf->IsValidPc(0xffff));
+  EXPECT_FALSE(elf->IsValidPc(0x10000));
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load_non_zero_load_bias) {
+  std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+  Elf32_Ehdr ehdr;
+  memset(&ehdr, 0, sizeof(ehdr));
+  ehdr.e_phoff = 0x100;
+  ehdr.e_phnum = 1;
+  ehdr.e_phentsize = sizeof(Elf32_Phdr);
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  Elf32_Phdr phdr;
+  memset(&phdr, 0, sizeof(phdr));
+  phdr.p_type = PT_LOAD;
+  phdr.p_vaddr = 0x2000;
+  phdr.p_memsz = 0x10000;
+  phdr.p_flags = PF_R | PF_X;
+  phdr.p_align = 0x1000;
+  memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0x2000U, load_bias);
+  EXPECT_FALSE(elf->IsValidPc(0));
+  EXPECT_FALSE(elf->IsValidPc(0x1000));
+  EXPECT_FALSE(elf->IsValidPc(0x1fff));
+  EXPECT_TRUE(elf->IsValidPc(0x2000));
+  EXPECT_TRUE(elf->IsValidPc(0x5000));
+  EXPECT_TRUE(elf->IsValidPc(0x11fff));
+  EXPECT_FALSE(elf->IsValidPc(0x12000));
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_debug_frame) {
+  std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+  uint64_t sh_offset = 0x100;
+
+  Elf32_Ehdr ehdr;
+  memset(&ehdr, 0, sizeof(ehdr));
+  ehdr.e_shstrndx = 1;
+  ehdr.e_shoff = sh_offset;
+  ehdr.e_shentsize = sizeof(Elf32_Shdr);
+  ehdr.e_shnum = 3;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  Elf32_Shdr shdr;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_NULL;
+  memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+  sh_offset += sizeof(shdr);
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 1;
+  shdr.sh_offset = 0x500;
+  shdr.sh_size = 0x100;
+  memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+  memory_.SetMemory(0x500, ".debug_frame");
+
+  sh_offset += sizeof(shdr);
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_name = 0;
+  shdr.sh_addr = 0x600;
+  shdr.sh_offset = 0x600;
+  shdr.sh_size = 0x200;
+  memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+  // CIE 32.
+  memory_.SetData32(0x600, 0xfc);
+  memory_.SetData32(0x604, 0xffffffff);
+  memory_.SetData8(0x608, 1);
+  memory_.SetData8(0x609, '\0');
+  memory_.SetData8(0x60a, 0x4);
+  memory_.SetData8(0x60b, 0x4);
+  memory_.SetData8(0x60c, 0x1);
+
+  // FDE 32.
+  memory_.SetData32(0x700, 0xfc);
+  memory_.SetData32(0x704, 0);
+  memory_.SetData32(0x708, 0x2100);
+  memory_.SetData32(0x70c, 0x200);
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  elf->InitHeaders();
+  EXPECT_EQ(0U, load_bias);
+  EXPECT_FALSE(elf->IsValidPc(0));
+  EXPECT_FALSE(elf->IsValidPc(0x20ff));
+  EXPECT_TRUE(elf->IsValidPc(0x2100));
+  EXPECT_TRUE(elf->IsValidPc(0x2200));
+  EXPECT_TRUE(elf->IsValidPc(0x22ff));
+  EXPECT_FALSE(elf->IsValidPc(0x2300));
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_eh_frame) {
+  std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+  uint64_t sh_offset = 0x100;
+
+  Elf32_Ehdr ehdr;
+  memset(&ehdr, 0, sizeof(ehdr));
+  ehdr.e_shstrndx = 1;
+  ehdr.e_shoff = sh_offset;
+  ehdr.e_shentsize = sizeof(Elf32_Shdr);
+  ehdr.e_shnum = 3;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  Elf32_Shdr shdr;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_NULL;
+  memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+  sh_offset += sizeof(shdr);
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 1;
+  shdr.sh_offset = 0x500;
+  shdr.sh_size = 0x100;
+  memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+  memory_.SetMemory(0x500, ".eh_frame");
+
+  sh_offset += sizeof(shdr);
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_name = 0;
+  shdr.sh_addr = 0x600;
+  shdr.sh_offset = 0x600;
+  shdr.sh_size = 0x200;
+  memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+  // CIE 32.
+  memory_.SetData32(0x600, 0xfc);
+  memory_.SetData32(0x604, 0);
+  memory_.SetData8(0x608, 1);
+  memory_.SetData8(0x609, '\0');
+  memory_.SetData8(0x60a, 0x4);
+  memory_.SetData8(0x60b, 0x4);
+  memory_.SetData8(0x60c, 0x1);
+
+  // FDE 32.
+  memory_.SetData32(0x700, 0xfc);
+  memory_.SetData32(0x704, 0x104);
+  memory_.SetData32(0x708, 0x20f8);
+  memory_.SetData32(0x70c, 0x200);
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  elf->InitHeaders();
+  EXPECT_EQ(0U, load_bias);
+  EXPECT_FALSE(elf->IsValidPc(0));
+  EXPECT_FALSE(elf->IsValidPc(0x27ff));
+  EXPECT_TRUE(elf->IsValidPc(0x2800));
+  EXPECT_TRUE(elf->IsValidPc(0x2900));
+  EXPECT_TRUE(elf->IsValidPc(0x29ff));
+  EXPECT_FALSE(elf->IsValidPc(0x2a00));
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 5e808ef..7e6a62a 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -346,7 +346,14 @@
   void InitHeaders() override {}
   bool GetSoname(std::string*) override { return false; }
   bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override { return false; }
+
   MOCK_METHOD5(Step, bool(uint64_t, uint64_t, Regs*, Memory*, bool*));
+  MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
+  MOCK_METHOD1(IsValidPc, bool(uint64_t));
+
+  void MockSetDynamicOffset(uint64_t offset) { dynamic_offset_ = offset; }
+  void MockSetDynamicVaddr(uint64_t vaddr) { dynamic_vaddr_ = vaddr; }
+  void MockSetDynamicSize(uint64_t size) { dynamic_size_ = size; }
 };
 
 TEST_F(ElfTest, step_in_interface) {
@@ -378,14 +385,200 @@
   elf.FakeSetInterface(interface);
   MemoryFake process_memory;
 
-  // Invalid relative pc given load_bias.
   bool finished;
-  ASSERT_FALSE(elf.Step(0x1004, 0x1000, 0x2000, &regs, &process_memory, &finished));
-
   EXPECT_CALL(*interface, Step(0x7300, 0x4000, &regs, &process_memory, &finished))
       .WillOnce(::testing::Return(true));
 
   ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, &regs, &process_memory, &finished));
 }
 
+TEST_F(ElfTest, get_global_invalid_elf) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(false);
+
+  std::string global("something");
+  uint64_t offset;
+  ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
+}
+
+TEST_F(ElfTest, get_global_valid_not_in_interface) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)).WillOnce(::testing::Return(false));
+
+  ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
+}
+
+TEST_F(ElfTest, get_global_valid_below_load_bias) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0x1000);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+  ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_zero) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+
+  ElfInterfaceMock* gnu_interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetGnuDebugdataInterface(gnu_interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)).WillOnce(::testing::Return(false));
+
+  EXPECT_CALL(*gnu_interface, GetGlobalVariable(global, &offset))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x500), ::testing::Return(true)));
+
+  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+  EXPECT_EQ(0x500U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_in_gnu_debugdata_dynamic_zero) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+  EXPECT_EQ(0x300U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_zero_non_zero_load_bias) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0x100);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+  EXPECT_EQ(0x200U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_adjust_negative) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  interface->MockSetDynamicOffset(0x400);
+  interface->MockSetDynamicVaddr(0x800);
+  interface->MockSetDynamicSize(0x100);
+  elf.FakeSetInterface(interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x850), ::testing::Return(true)));
+
+  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+  EXPECT_EQ(0x450U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_adjust_positive) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  interface->MockSetDynamicOffset(0x1000);
+  interface->MockSetDynamicVaddr(0x800);
+  interface->MockSetDynamicSize(0x100);
+  elf.FakeSetInterface(interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x850), ::testing::Return(true)));
+
+  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+  EXPECT_EQ(0x1050U, offset);
+}
+
+TEST_F(ElfTest, is_valid_pc_elf_invalid) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(false);
+  elf.FakeSetLoadBias(0);
+
+  EXPECT_FALSE(elf.IsValidPc(0x100));
+  EXPECT_FALSE(elf.IsValidPc(0x200));
+}
+
+TEST_F(ElfTest, is_valid_pc_interface) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+
+  EXPECT_CALL(*interface, IsValidPc(0x1500)).WillOnce(::testing::Return(true));
+
+  EXPECT_TRUE(elf.IsValidPc(0x1500));
+}
+
+TEST_F(ElfTest, is_valid_pc_non_zero_load_bias) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0x1000);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+
+  EXPECT_CALL(*interface, IsValidPc(0x500)).WillOnce(::testing::Return(true));
+
+  EXPECT_FALSE(elf.IsValidPc(0x100));
+  EXPECT_FALSE(elf.IsValidPc(0x200));
+  EXPECT_TRUE(elf.IsValidPc(0x1500));
+}
+
+TEST_F(ElfTest, is_valid_pc_from_gnu_debugdata) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+  ElfInterfaceMock* gnu_interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetGnuDebugdataInterface(gnu_interface);
+
+  EXPECT_CALL(*interface, IsValidPc(0x1500)).WillOnce(::testing::Return(false));
+  EXPECT_CALL(*gnu_interface, IsValidPc(0x1500)).WillOnce(::testing::Return(true));
+
+  EXPECT_TRUE(elf.IsValidPc(0x1500));
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
new file mode 100644
index 0000000..83d7a49
--- /dev/null
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -0,0 +1,377 @@
+/*
+ * 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/Elf.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class JitDebugTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+
+    jit_debug_.reset(new JitDebug(process_memory_));
+    jit_debug_->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 --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"));
+    ASSERT_TRUE(maps_->Parse());
+
+    MapInfo* map_info = maps_->Get(3);
+    ASSERT_TRUE(map_info != nullptr);
+    elf_memories_.push_back(new MemoryFake);
+    ElfFake* elf = new ElfFake(elf_memories_.back());
+    elf->FakeSetValid(true);
+    ElfInterfaceFake* interface = new ElfInterfaceFake(elf_memories_.back());
+    elf->FakeSetInterface(interface);
+    interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
+    map_info->elf = elf;
+
+    map_info = maps_->Get(5);
+    ASSERT_TRUE(map_info != nullptr);
+    elf_memories_.push_back(new MemoryFake);
+    elf = new ElfFake(elf_memories_.back());
+    elf->FakeSetValid(true);
+    interface = new ElfInterfaceFake(elf_memories_.back());
+    elf->FakeSetInterface(interface);
+    interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
+    map_info->elf = elf;
+  }
+
+  template <typename EhdrType, typename ShdrType>
+  void CreateElf(uint64_t offset, uint8_t class_type, uint8_t machine_type, uint32_t pc,
+                 uint32_t size) {
+    EhdrType ehdr;
+    memset(&ehdr, 0, sizeof(ehdr));
+    uint64_t sh_offset = sizeof(ehdr);
+    memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
+    ehdr.e_ident[EI_CLASS] = class_type;
+    ehdr.e_machine = machine_type;
+    ehdr.e_shstrndx = 1;
+    ehdr.e_shoff = sh_offset;
+    ehdr.e_shentsize = sizeof(ShdrType);
+    ehdr.e_shnum = 3;
+    memory_->SetMemory(offset, &ehdr, sizeof(ehdr));
+
+    ShdrType shdr;
+    memset(&shdr, 0, sizeof(shdr));
+    shdr.sh_type = SHT_NULL;
+    memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+    sh_offset += sizeof(shdr);
+    memset(&shdr, 0, sizeof(shdr));
+    shdr.sh_type = SHT_STRTAB;
+    shdr.sh_name = 1;
+    shdr.sh_offset = 0x500;
+    shdr.sh_size = 0x100;
+    memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+    memory_->SetMemory(offset + 0x500, ".debug_frame");
+
+    sh_offset += sizeof(shdr);
+    memset(&shdr, 0, sizeof(shdr));
+    shdr.sh_type = SHT_PROGBITS;
+    shdr.sh_name = 0;
+    shdr.sh_addr = 0x600;
+    shdr.sh_offset = 0x600;
+    shdr.sh_size = 0x200;
+    memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+    // Now add a single cie/fde.
+    uint64_t dwarf_offset = offset + 0x600;
+    if (class_type == ELFCLASS32) {
+      // CIE 32 information.
+      memory_->SetData32(dwarf_offset, 0xfc);
+      memory_->SetData32(dwarf_offset + 0x4, 0xffffffff);
+      memory_->SetData8(dwarf_offset + 0x8, 1);
+      memory_->SetData8(dwarf_offset + 0x9, '\0');
+      memory_->SetData8(dwarf_offset + 0xa, 0x4);
+      memory_->SetData8(dwarf_offset + 0xb, 0x4);
+      memory_->SetData8(dwarf_offset + 0xc, 0x1);
+
+      // FDE 32 information.
+      memory_->SetData32(dwarf_offset + 0x100, 0xfc);
+      memory_->SetData32(dwarf_offset + 0x104, 0);
+      memory_->SetData32(dwarf_offset + 0x108, pc);
+      memory_->SetData32(dwarf_offset + 0x10c, size);
+    } else {
+      // CIE 64 information.
+      memory_->SetData32(dwarf_offset, 0xffffffff);
+      memory_->SetData64(dwarf_offset + 4, 0xf4);
+      memory_->SetData64(dwarf_offset + 0xc, 0xffffffffffffffffULL);
+      memory_->SetData8(dwarf_offset + 0x14, 1);
+      memory_->SetData8(dwarf_offset + 0x15, '\0');
+      memory_->SetData8(dwarf_offset + 0x16, 0x4);
+      memory_->SetData8(dwarf_offset + 0x17, 0x4);
+      memory_->SetData8(dwarf_offset + 0x18, 0x1);
+
+      // FDE 64 information.
+      memory_->SetData32(dwarf_offset + 0x100, 0xffffffff);
+      memory_->SetData64(dwarf_offset + 0x104, 0xf4);
+      memory_->SetData64(dwarf_offset + 0x10c, 0);
+      memory_->SetData64(dwarf_offset + 0x114, pc);
+      memory_->SetData64(dwarf_offset + 0x11c, size);
+    }
+  }
+
+  void WriteDescriptor32(uint64_t addr, uint32_t entry);
+  void WriteDescriptor64(uint64_t addr, uint64_t entry);
+  void WriteEntry32Pack(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+                        uint64_t elf_size);
+  void WriteEntry32Pad(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+                       uint64_t elf_size);
+  void WriteEntry64(uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr,
+                    uint64_t elf_size);
+
+  std::shared_ptr<Memory> process_memory_;
+  MemoryFake* memory_;
+  std::vector<MemoryFake*> elf_memories_;
+  std::unique_ptr<JitDebug> jit_debug_;
+  std::unique_ptr<BufferMaps> maps_;
+};
+
+void JitDebugTest::WriteDescriptor32(uint64_t addr, uint32_t entry) {
+  // Format of the 32 bit JITDescriptor structure:
+  //   uint32_t version
+  memory_->SetData32(addr, 1);
+  //   uint32_t action_flag
+  memory_->SetData32(addr + 4, 0);
+  //   uint32_t relevant_entry
+  memory_->SetData32(addr + 8, 0);
+  //   uint32_t first_entry
+  memory_->SetData32(addr + 12, entry);
+}
+
+void JitDebugTest::WriteDescriptor64(uint64_t addr, uint64_t entry) {
+  // Format of the 64 bit JITDescriptor structure:
+  //   uint32_t version
+  memory_->SetData32(addr, 1);
+  //   uint32_t action_flag
+  memory_->SetData32(addr + 4, 0);
+  //   uint64_t relevant_entry
+  memory_->SetData64(addr + 8, 0);
+  //   uint64_t first_entry
+  memory_->SetData64(addr + 16, entry);
+}
+
+void JitDebugTest::WriteEntry32Pack(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+                                    uint64_t elf_size) {
+  // Format of the 32 bit JITCodeEntry structure:
+  //   uint32_t next
+  memory_->SetData32(addr, next);
+  //   uint32_t prev
+  memory_->SetData32(addr + 4, prev);
+  //   uint32_t symfile_addr
+  memory_->SetData32(addr + 8, elf_addr);
+  //   uint64_t symfile_size
+  memory_->SetData64(addr + 12, elf_size);
+}
+
+void JitDebugTest::WriteEntry32Pad(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+                                   uint64_t elf_size) {
+  // Format of the 32 bit JITCodeEntry structure:
+  //   uint32_t next
+  memory_->SetData32(addr, next);
+  //   uint32_t prev
+  memory_->SetData32(addr + 4, prev);
+  //   uint32_t symfile_addr
+  memory_->SetData32(addr + 8, elf_addr);
+  //   uint32_t pad
+  memory_->SetData32(addr + 12, 0);
+  //   uint64_t symfile_size
+  memory_->SetData64(addr + 16, elf_size);
+}
+
+void JitDebugTest::WriteEntry64(uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr,
+                                uint64_t elf_size) {
+  // Format of the 64 bit JITCodeEntry structure:
+  //   uint64_t next
+  memory_->SetData64(addr, next);
+  //   uint64_t prev
+  memory_->SetData64(addr + 8, prev);
+  //   uint64_t symfile_addr
+  memory_->SetData64(addr + 16, elf_addr);
+  //   uint64_t symfile_size
+  memory_->SetData64(addr + 24, elf_size);
+}
+
+TEST_F(JitDebugTest, get_elf_invalid) {
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_no_global_variable) {
+  maps_.reset(new BufferMaps(""));
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_no_valid_descriptor_in_memory) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_no_valid_code_entry) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+  WriteDescriptor32(0xf800, 0x200000);
+
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_invalid_descriptor_first_entry) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+  WriteDescriptor32(0xf800, 0);
+
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_invalid_descriptor_version) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+  WriteDescriptor32(0xf800, 0x20000);
+  // Set the version to an invalid value.
+  memory_->SetData32(0xf800, 2);
+
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_32) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
+
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf != nullptr);
+
+  // Clear the memory and verify all of the data is cached.
+  memory_->Clear();
+  Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf2 != nullptr);
+  EXPECT_EQ(elf, elf2);
+}
+
+TEST_F(JitDebugTest, get_elf_x86) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32Pack(0x200000, 0, 0, 0x4000, 0x1000);
+
+  jit_debug_->SetArch(ARCH_X86);
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf != nullptr);
+
+  // Clear the memory and verify all of the data is cached.
+  memory_->Clear();
+  Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf2 != nullptr);
+  EXPECT_EQ(elf, elf2);
+}
+
+TEST_F(JitDebugTest, get_elf_64) {
+  CreateElf<Elf64_Ehdr, Elf64_Shdr>(0x4000, ELFCLASS64, EM_AARCH64, 0x1500, 0x200);
+
+  WriteDescriptor64(0xf800, 0x200000);
+  WriteEntry64(0x200000, 0, 0, 0x4000, 0x1000);
+
+  jit_debug_->SetArch(ARCH_ARM64);
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf != nullptr);
+
+  // Clear the memory and verify all of the data is cached.
+  memory_->Clear();
+  Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf2 != nullptr);
+  EXPECT_EQ(elf, elf2);
+}
+
+TEST_F(JitDebugTest, get_elf_multiple_entries) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x5000, ELFCLASS32, EM_ARM, 0x2300, 0x400);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32Pad(0x200000, 0, 0x200100, 0x4000, 0x1000);
+  WriteEntry32Pad(0x200100, 0x200100, 0, 0x5000, 0x1000);
+
+  Elf* elf_2 = jit_debug_->GetElf(maps_.get(), 0x2400);
+  ASSERT_TRUE(elf_2 != nullptr);
+
+  Elf* elf_1 = jit_debug_->GetElf(maps_.get(), 0x1600);
+  ASSERT_TRUE(elf_1 != nullptr);
+
+  // Clear the memory and verify all of the data is cached.
+  memory_->Clear();
+  EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x1500));
+  EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x16ff));
+  EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x2300));
+  EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x26ff));
+  EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x1700));
+  EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x2700));
+}
+
+TEST_F(JitDebugTest, get_elf_search_libs) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
+
+  // Only search a given named list of libs.
+  std::vector<std::string> libs{"libart.so"};
+  jit_debug_.reset(new JitDebug(process_memory_, libs));
+  jit_debug_->SetArch(ARCH_ARM);
+  EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) == nullptr);
+
+  // Change the name of the map that includes the value and verify this works.
+  MapInfo* map_info = maps_->Get(5);
+  map_info->name = "/system/lib/libart.so";
+  jit_debug_.reset(new JitDebug(process_memory_, libs));
+  // Make sure that clearing our copy of the libs doesn't affect the
+  // JitDebug object.
+  libs.clear();
+  jit_debug_->SetArch(ARCH_ARM);
+  EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index b81b2ca..8f7d913 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -45,7 +45,7 @@
 
   void IterateRegisters(std::function<void(const char*, uint64_t)>) override {}
 
-  bool Format32Bit() { return false; }
+  bool Is32Bit() { return false; }
 
   uint64_t GetAdjustedPc(uint64_t rel_pc, Elf*) override { return rel_pc - 2; }
 
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 3f84890..15d5458 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -188,7 +188,7 @@
 
   regs_arm.set_pc(0x1500);
   EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info));
-  EXPECT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, invalid_elf));
+  EXPECT_EQ(0x4fcU, regs_arm.GetAdjustedPc(0x500U, invalid_elf));
 
   regs_arm64.set_pc(0x1600);
   EXPECT_EQ(0x600U, invalid_elf->GetRelPc(regs_arm64.pc(), &map_info));
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index da258a6..45a7b58 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -330,9 +330,69 @@
   ASSERT_EQ(3U, func_offset);
 }
 
+TYPED_TEST_P(SymbolsTest, get_global) {
+  uint64_t start_offset = 0x1000;
+  uint64_t str_offset = 0xa000;
+  Symbols symbols(start_offset, 4 * sizeof(TypeParam), sizeof(TypeParam), str_offset, 0x1000);
+
+  TypeParam sym;
+  memset(&sym, 0, sizeof(sym));
+  sym.st_shndx = SHN_COMMON;
+  sym.st_info = STT_OBJECT | (STB_GLOBAL << 4);
+  sym.st_name = 0x100;
+  this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+  this->memory_.SetMemory(str_offset + 0x100, "global_0");
+
+  start_offset += sizeof(sym);
+  memset(&sym, 0, sizeof(sym));
+  sym.st_shndx = SHN_COMMON;
+  sym.st_info = STT_FUNC;
+  sym.st_name = 0x200;
+  sym.st_value = 0x10000;
+  sym.st_size = 0x100;
+  this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+  this->memory_.SetMemory(str_offset + 0x200, "function_0");
+
+  start_offset += sizeof(sym);
+  memset(&sym, 0, sizeof(sym));
+  sym.st_shndx = SHN_COMMON;
+  sym.st_info = STT_OBJECT | (STB_GLOBAL << 4);
+  sym.st_name = 0x300;
+  this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+  this->memory_.SetMemory(str_offset + 0x300, "global_1");
+
+  start_offset += sizeof(sym);
+  memset(&sym, 0, sizeof(sym));
+  sym.st_shndx = SHN_COMMON;
+  sym.st_info = STT_FUNC;
+  sym.st_name = 0x400;
+  sym.st_value = 0x12000;
+  sym.st_size = 0x100;
+  this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+  this->memory_.SetMemory(str_offset + 0x400, "function_1");
+
+  uint64_t offset;
+  EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_0", &offset));
+  EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_1", &offset));
+  EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_0", &offset));
+  EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_1", &offset));
+
+  EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_0", &offset));
+  EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_1", &offset));
+
+  std::string name;
+  EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, 0, &this->memory_, &name, &offset));
+  EXPECT_EQ("function_0", name);
+  EXPECT_EQ(2U, offset);
+
+  EXPECT_TRUE(symbols.GetName<TypeParam>(0x12004, 0, &this->memory_, &name, &offset));
+  EXPECT_EQ("function_1", name);
+  EXPECT_EQ(4U, offset);
+}
+
 REGISTER_TYPED_TEST_CASE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
                            multiple_entries_nonstandard_size, load_bias, symtab_value_out_of_bounds,
-                           symtab_read_cached);
+                           symtab_read_cached, get_global);
 
 typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 8f28036..9216204 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -25,14 +25,17 @@
 #include <string>
 #include <vector>
 
+#include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/RegsArm.h>
 #include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
 #include <unwindstack/Unwinder.h>
 
 #include "MachineArm.h"
 #include "MachineArm64.h"
+#include "MachineX86.h"
 
 #include "ElfTestUtils.h"
 
@@ -92,7 +95,7 @@
       "  #00 pc 0001a9f8  libc.so (abort+63)\n"
       "  #01 pc 00006a1b  libbase.so (_ZN7android4base14DefaultAborterEPKc+6)\n"
       "  #02 pc 00007441  libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n"
-      "  #03 pc 00015149  /does/not/exist/libhidlbase.so\n",
+      "  #03 pc 00015147  /does/not/exist/libhidlbase.so\n",
       frame_info);
 }
 
@@ -200,4 +203,207 @@
       frame_info);
 }
 
+static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
+  MemoryOffline* memory = new MemoryOffline;
+  ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
+  parts->Add(memory);
+}
+
+TEST(UnwindOfflineTest, jit_debug_x86_32) {
+  std::string dir(TestGetFileDirectory() + "offline/jit_debug_x86_32/");
+
+  MemoryOfflineParts* memory = new MemoryOfflineParts;
+  AddMemory(dir + "descriptor.data", memory);
+  AddMemory(dir + "stack.data", memory);
+  for (size_t i = 0; i < 7; i++) {
+    AddMemory(dir + "entry" + std::to_string(i) + ".data", memory);
+    AddMemory(dir + "jit" + std::to_string(i) + ".data", memory);
+  }
+
+  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(69U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 00068fb8  libarttestd.so (_ZN3artL13CauseSegfaultEv+72)\n"
+      "  #01 pc 00067f00  libarttestd.so (Java_Main_unwindInProcess+10032)\n"
+      "  #02 pc 000021a8 (offset 0x2000)  137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
+      "boolean)+136)\n"
+      "  #03 pc 0000fe81  anonymous:ee74c000 (boolean Main.bar(boolean)+65)\n"
+      "  #04 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
+      "  #05 pc 00146ab5  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+      "  #06 pc 0039cf0d  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "  #07 pc 00392552  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+354)\n"
+      "  #08 pc 0039399a  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "  #09 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
+      "  #10 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
+      "  #11 pc 0000fe04  anonymous:ee74c000 (int Main.compare(Main, Main)+52)\n"
+      "  #12 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
+      "  #13 pc 00146ab5  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+      "  #14 pc 0039cf0d  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "  #15 pc 00392552  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+354)\n"
+      "  #16 pc 0039399a  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "  #17 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
+      "  #18 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
+      "  #19 pc 0000fd3c  anonymous:ee74c000 (int Main.compare(java.lang.Object, "
+      "java.lang.Object)+108)\n"
+      "  #20 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
+      "  #21 pc 00146ab5  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+      "  #22 pc 0039cf0d  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "  #23 pc 00392552  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+354)\n"
+      "  #24 pc 0039399a  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "  #25 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
+      "  #26 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
+      "  #27 pc 0000fbdc  anonymous:ee74c000 (int "
+      "java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
+      "java.util.Comparator)+332)\n"
+      "  #28 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)\n"
+      "  #29 pc 00146acb  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+      "  #30 pc 0039cf0d  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "  #31 pc 00392552  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+354)\n"
+      "  #32 pc 0039399a  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "  #33 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
+      "  #34 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
+      "  #35 pc 0000f625  anonymous:ee74c000 (boolean Main.foo()+165)\n"
+      "  #36 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
+      "  #37 pc 00146ab5  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+      "  #38 pc 0039cf0d  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "  #39 pc 00392552  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+354)\n"
+      "  #40 pc 0039399a  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "  #41 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
+      "  #42 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
+      "  #43 pc 0000eedc  anonymous:ee74c000 (void Main.runPrimary()+60)\n"
+      "  #44 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
+      "  #45 pc 00146ab5  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+      "  #46 pc 0039cf0d  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "  #47 pc 00392552  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+354)\n"
+      "  #48 pc 0039399a  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "  #49 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
+      "  #50 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
+      "  #51 pc 0000ac22  anonymous:ee74c000 (void Main.main(java.lang.String[])+98)\n"
+      "  #52 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)\n"
+      "  #53 pc 00146acb  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+      "  #54 pc 0039cf0d  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "  #55 pc 00392552  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+354)\n"
+      "  #56 pc 0039399a  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "  #57 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
+      "  #58 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
+      "  #59 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)\n"
+      "  #60 pc 00146acb  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+      "  #61 pc 005aac95  libartd.so "
+      "(_ZN3artL18InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_9ArtMethodEPNS_"
+      "8ArgArrayEPNS_6JValueEPKc+85)\n"
+      "  #62 pc 005aab5a  libartd.so "
+      "(_ZN3art17InvokeWithVarArgsERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_"
+      "jmethodIDPc+362)\n"
+      "  #63 pc 0048a3dd  libartd.so "
+      "(_ZN3art3JNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+125)\n"
+      "  #64 pc 0018448c  libartd.so "
+      "(_ZN3art8CheckJNI11CallMethodVEPKcP7_JNIEnvP8_jobjectP7_jclassP10_jmethodIDPcNS_"
+      "9Primitive4TypeENS_10InvokeTypeE+1964)\n"
+      "  #65 pc 0017cf06  libartd.so "
+      "(_ZN3art8CheckJNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+70)\n"
+      "  #66 pc 00001d8c  dalvikvm32 "
+      "(_ZN7_JNIEnv20CallStaticVoidMethodEP7_jclassP10_jmethodIDz+60)\n"
+      "  #67 pc 00001a80  dalvikvm32 (main+1312)\n"
+      "  #68 pc 00018275  libc.so\n",
+      frame_info);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/137-cfi.odex b/libunwindstack/tests/files/offline/jit_debug_x86_32/137-cfi.odex
new file mode 100644
index 0000000..870ac0a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/137-cfi.odex
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/dalvikvm32 b/libunwindstack/tests/files/offline/jit_debug_x86_32/dalvikvm32
new file mode 100644
index 0000000..76ffad9
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/dalvikvm32
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/descriptor.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/descriptor.data
new file mode 100644
index 0000000..466dae2
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry0.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry0.data
new file mode 100644
index 0000000..3a725e8
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry1.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry1.data
new file mode 100644
index 0000000..767550f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry2.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry2.data
new file mode 100644
index 0000000..e7e492e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry3.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry3.data
new file mode 100644
index 0000000..65f9cd4
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry4.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry4.data
new file mode 100644
index 0000000..30aa28c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry5.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry5.data
new file mode 100644
index 0000000..3c89673
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry6.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry6.data
new file mode 100644
index 0000000..9c9b83c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit0.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit0.data
new file mode 100644
index 0000000..eaad142
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit1.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit1.data
new file mode 100644
index 0000000..d534816
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit2.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit2.data
new file mode 100644
index 0000000..dbeb886
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit3.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit3.data
new file mode 100644
index 0000000..bf2142d
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit4.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit4.data
new file mode 100644
index 0000000..e2ba1b0
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit5.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit5.data
new file mode 100644
index 0000000..c27ba54
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit6.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit6.data
new file mode 100644
index 0000000..5fc8fae
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so b/libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so
new file mode 100644
index 0000000..70352db
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so
new file mode 100644
index 0000000..e72e673
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/libc.so b/libunwindstack/tests/files/offline/jit_debug_x86_32/libc.so
new file mode 100644
index 0000000..9c78790
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86_32/maps.txt
new file mode 100644
index 0000000..db4f9f7
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/maps.txt
@@ -0,0 +1,6 @@
+56573000-56577000 r-xp 0 00:00 0   dalvikvm32
+eb833000-eb8cc000 r-xp 0 00:00 0   libarttestd.so
+ec606000-ec607000 r-xp 2000 00:00 0   137-cfi.odex
+ee74c000-f074c000 r-xp 0 00:00 0   anonymous:ee74c000
+f6be1000-f732b000 r-xp 0 00:00 0   libartd.so
+f734b000-f74fc000 r-xp 0 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/regs.txt b/libunwindstack/tests/files/offline/jit_debug_x86_32/regs.txt
new file mode 100644
index 0000000..f68305b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/regs.txt
@@ -0,0 +1,9 @@
+eax: eb8cccd0
+ebx: eb8cccd0
+ecx: ff
+edx: ffeb2ca8
+ebp: ffeb5298
+edi: ffeb5c08
+esi: ffeb5c00
+esp: ffeb5280
+eip: eb89bfb8
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/stack.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/stack.data
new file mode 100644
index 0000000..c345762
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/stack.data
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index 81bedb7..07e48af 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -27,6 +27,7 @@
 #include <unistd.h>
 
 #include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
@@ -90,6 +91,8 @@
 
   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());
   unwinder.Unwind();
 
   // Print the frames.
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index d64ef8f..74868d4 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -33,6 +33,7 @@
 #include <vector>
 
 #include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
@@ -191,7 +192,9 @@
   // elf files are involved.
   uint64_t sp = regs->sp();
   auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+  unwindstack::JitDebug jit_debug(process_memory);
   unwindstack::Unwinder unwinder(1024, &maps, regs, process_memory);
+  unwinder.SetJitDebug(&jit_debug, regs->Arch());
   unwinder.Unwind();
 
   std::unordered_map<uint64_t, map_info_t> maps_by_start;
@@ -214,6 +217,10 @@
     }
   }
 
+  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+    printf("%s\n", unwinder.FormatFrame(i).c_str());
+  }
+
   if (!SaveStack(pid, sp, last_sp)) {
     return 1;
   }