Move dex pc frame creation into libunwindstack.

Test: Compiles, all unit tests pass.
Test: Ran 137-cfi art test in interpreter and verified interpreter
Test: frames still show up.

Change-Id: Icea90194986faa733a873e8cf467fc2513eb5573
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index f70ed7b..d22e1e8 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -31,8 +31,74 @@
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Unwinder.h>
 
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+#include <unwindstack/DexFiles.h>
+#endif
+
 namespace unwindstack {
 
+// Inject extra 'virtual' frame that represents the dex pc data.
+// The dex pc is a magic register defined in the Mterp interpreter,
+// and thus it will be restored/observed in the frame after it.
+// Adding the dex frame first here will create something like:
+//   #7 pc 0015fa20 core.vdex   java.util.Arrays.binarySearch+8
+//   #8 pc 006b1ba1 libartd.so  ExecuteMterpImpl+14625
+//   #9 pc 0039a1ef libartd.so  art::interpreter::Execute+719
+void Unwinder::FillInDexFrame() {
+  size_t frame_num = frames_.size();
+  frames_.resize(frame_num + 1);
+  FrameData* frame = &frames_.at(frame_num);
+
+  uint64_t dex_pc = regs_->dex_pc();
+  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;
+  }
+
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  dex_files_->GetMethodInformation(dex_offset, info, &frame->function_name, &frame->function_offset);
+#endif
+}
+
 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);
@@ -40,7 +106,6 @@
   frame->num = frame_num;
   frame->sp = regs_->sp();
   frame->rel_pc = adjusted_rel_pc;
-  frame->dex_pc = regs_->dex_pc();
 
   if (map_info == nullptr) {
     frame->pc = regs_->pc();
@@ -128,6 +193,11 @@
     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()) {
+      if (regs_->dex_pc() != 0) {
+        // Add a frame to represent the dex file.
+        FillInDexFrame();
+      }
+
       FillInFrame(map_info, elf, adjusted_rel_pc, adjusted_pc);
 
       // Once a frame is added, stop skipping frames.
@@ -240,4 +310,11 @@
   jit_debug_ = jit_debug;
 }
 
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+void Unwinder::SetDexFiles(DexFiles* dex_files, ArchEnum arch) {
+  dex_files->SetArch(arch);
+  dex_files_ = dex_files;
+}
+#endif
+
 }  // namespace unwindstack