Fix dexlayout debug info printing

Parsing local info and position info made shared debug info print
incorrectly.  Remove the dexlayout structs that represent the pre-parsed
versions and print from the byte buffer.

Bug: 67664147
Test: make test-art-host
Change-Id: Iae33ae8ff486914d8d7a5973f81faaf3c95615a6
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index 0c944ce..f75eacc 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -39,24 +39,6 @@
   return value;
 }
 
-static bool GetPositionsCb(void* context, const DexFile::PositionInfo& entry) {
-  DebugInfoItem* debug_info = reinterpret_cast<DebugInfoItem*>(context);
-  PositionInfoVector& positions = debug_info->GetPositionInfo();
-  positions.push_back(std::unique_ptr<PositionInfo>(new PositionInfo(entry.address_, entry.line_)));
-  return false;
-}
-
-static void GetLocalsCb(void* context, const DexFile::LocalInfo& entry) {
-  DebugInfoItem* debug_info = reinterpret_cast<DebugInfoItem*>(context);
-  LocalInfoVector& locals = debug_info->GetLocalInfo();
-  const char* name = entry.name_ != nullptr ? entry.name_ : "(null)";
-  const char* descriptor = entry.descriptor_ != nullptr ? entry.descriptor_ : "";
-  const char* signature = entry.signature_ != nullptr ? entry.signature_ : "";
-  locals.push_back(std::unique_ptr<LocalInfo>(
-      new LocalInfo(name, descriptor, signature, entry.start_address_, entry.end_address_,
-                    entry.reg_)));
-}
-
 static uint32_t GetDebugInfoStreamSize(const uint8_t* debug_info_stream) {
   const uint8_t* stream = debug_info_stream;
   DecodeUnsignedLeb128(&stream);  // line_start
@@ -694,12 +676,6 @@
     }
     debug_info = code_item->DebugInfo();
   }
-  if (debug_info != nullptr) {
-    bool is_static = (access_flags & kAccStatic) != 0;
-    dex_file.DecodeDebugLocalInfo(
-        disk_code_item, is_static, cdii.GetMemberIndex(), GetLocalsCb, debug_info);
-    dex_file.DecodeDebugPositionInfo(disk_code_item, GetPositionsCb, debug_info);
-  }
   return new MethodItem(access_flags, method_id, code_item);
 }
 
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 5dcc87d..df3484c 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -966,39 +966,6 @@
   DISALLOW_COPY_AND_ASSIGN(CodeItem);
 };
 
-struct PositionInfo {
-  PositionInfo(uint32_t address, uint32_t line) : address_(address), line_(line) { }
-
-  uint32_t address_;
-  uint32_t line_;
-};
-
-using PositionInfoVector = std::vector<std::unique_ptr<PositionInfo>>;
-
-struct LocalInfo {
-  LocalInfo(const char* name,
-            const char* descriptor,
-            const char* signature,
-            uint32_t start_address,
-            uint32_t end_address,
-            uint16_t reg)
-      : name_(name),
-        descriptor_(descriptor),
-        signature_(signature),
-        start_address_(start_address),
-        end_address_(end_address),
-        reg_(reg) { }
-
-  std::string name_;
-  std::string descriptor_;
-  std::string signature_;
-  uint32_t start_address_;
-  uint32_t end_address_;
-  uint16_t reg_;
-};
-
-using LocalInfoVector = std::vector<std::unique_ptr<LocalInfo>>;
-
 class DebugInfoItem : public Item {
  public:
   DebugInfoItem(uint32_t debug_info_size, uint8_t* debug_info)
@@ -1007,16 +974,10 @@
   uint32_t GetDebugInfoSize() const { return debug_info_size_; }
   uint8_t* GetDebugInfo() const { return debug_info_.get(); }
 
-  PositionInfoVector& GetPositionInfo() { return positions_; }
-  LocalInfoVector& GetLocalInfo() { return locals_; }
-
  private:
   uint32_t debug_info_size_;
   std::unique_ptr<uint8_t[]> debug_info_;
 
-  PositionInfoVector positions_;
-  LocalInfoVector locals_;
-
   DISALLOW_COPY_AND_ASSIGN(DebugInfoItem);
 };
 
diff --git a/dexlayout/dex_verify.cc b/dexlayout/dex_verify.cc
index 5458129..18ddc86 100644
--- a/dexlayout/dex_verify.cc
+++ b/dexlayout/dex_verify.cc
@@ -893,109 +893,24 @@
     }
     return true;
   }
-  if (!VerifyPositionInfo(orig->GetPositionInfo(),
-                          output->GetPositionInfo(),
-                          orig->GetOffset(),
-                          error_msg)) {
+  // TODO: Test for debug equivalence rather than byte array equality.
+  uint32_t orig_size = orig->GetDebugInfoSize();
+  uint32_t output_size = output->GetDebugInfoSize();
+  if (orig_size != output_size) {
+    *error_msg = "DebugInfoSize disagreed.";
     return false;
   }
-  return VerifyLocalInfo(orig->GetLocalInfo(),
-                         output->GetLocalInfo(),
-                         orig->GetOffset(),
-                         error_msg);
-}
-
-bool VerifyPositionInfo(dex_ir::PositionInfoVector& orig,
-                        dex_ir::PositionInfoVector& output,
-                        uint32_t orig_offset,
-                        std::string* error_msg) {
-  if (orig.size() != output.size()) {
-    *error_msg = StringPrintf(
-        "Mismatched number of positions for debug info at offset %x: %zu vs %zu.",
-        orig_offset,
-        orig.size(),
-        output.size());
+  uint8_t* orig_data = orig->GetDebugInfo();
+  uint8_t* output_data = output->GetDebugInfo();
+  if ((orig_data == nullptr && output_data != nullptr) ||
+      (orig_data != nullptr && output_data == nullptr)) {
+    *error_msg = "DebugInfo null/non-null mismatch.";
     return false;
   }
-  for (size_t i = 0; i < orig.size(); ++i) {
-    if (orig[i]->address_ != output[i]->address_) {
-      *error_msg = StringPrintf(
-          "Mismatched position address for debug info at offset %x: %u vs %u.",
-          orig_offset,
-          orig[i]->address_,
-          output[i]->address_);
-      return false;
-    }
-    if (orig[i]->line_ != output[i]->line_) {
-      *error_msg = StringPrintf("Mismatched position line for debug info at offset %x: %u vs %u.",
-                                orig_offset,
-                                orig[i]->line_,
-                                output[i]->line_);
-      return false;
-    }
-  }
-  return true;
-}
-
-bool VerifyLocalInfo(dex_ir::LocalInfoVector& orig,
-                     dex_ir::LocalInfoVector& output,
-                     uint32_t orig_offset,
-                     std::string* error_msg) {
-  if (orig.size() != output.size()) {
-    *error_msg = StringPrintf(
-        "Mismatched number of locals for debug info at offset %x: %zu vs %zu.",
-        orig_offset,
-        orig.size(),
-        output.size());
+  if (memcmp(orig_data, output_data, orig_size) != 0) {
+    *error_msg = "DebugInfo bytes mismatch.";
     return false;
   }
-  for (size_t i = 0; i < orig.size(); ++i) {
-    if (orig[i]->name_ != output[i]->name_) {
-      *error_msg = StringPrintf("Mismatched local name for debug info at offset %x: %s vs %s.",
-                                orig_offset,
-                                orig[i]->name_.c_str(),
-                                output[i]->name_.c_str());
-      return false;
-    }
-    if (orig[i]->descriptor_ != output[i]->descriptor_) {
-      *error_msg = StringPrintf(
-          "Mismatched local descriptor for debug info at offset %x: %s vs %s.",
-          orig_offset,
-          orig[i]->descriptor_.c_str(),
-          output[i]->descriptor_.c_str());
-      return false;
-    }
-    if (orig[i]->signature_ != output[i]->signature_) {
-      *error_msg = StringPrintf("Mismatched local signature for debug info at offset %x: %s vs %s.",
-                                orig_offset,
-                                orig[i]->signature_.c_str(),
-                                output[i]->signature_.c_str());
-      return false;
-    }
-    if (orig[i]->start_address_ != output[i]->start_address_) {
-      *error_msg = StringPrintf(
-          "Mismatched local start address for debug info at offset %x: %u vs %u.",
-          orig_offset,
-          orig[i]->start_address_,
-          output[i]->start_address_);
-      return false;
-    }
-    if (orig[i]->end_address_ != output[i]->end_address_) {
-      *error_msg = StringPrintf(
-          "Mismatched local end address for debug info at offset %x: %u vs %u.",
-          orig_offset,
-          orig[i]->end_address_,
-          output[i]->end_address_);
-      return false;
-    }
-    if (orig[i]->reg_ != output[i]->reg_) {
-      *error_msg = StringPrintf("Mismatched local reg for debug info at offset %x: %u vs %u.",
-                                orig_offset,
-                                orig[i]->reg_,
-                                output[i]->reg_);
-      return false;
-    }
-  }
   return true;
 }
 
diff --git a/dexlayout/dex_verify.h b/dexlayout/dex_verify.h
index 58c95d6..998939b 100644
--- a/dexlayout/dex_verify.h
+++ b/dexlayout/dex_verify.h
@@ -100,14 +100,6 @@
 bool VerifyDebugInfo(dex_ir::DebugInfoItem* orig,
                      dex_ir::DebugInfoItem* output,
                      std::string* error_msg);
-bool VerifyPositionInfo(dex_ir::PositionInfoVector& orig,
-                        dex_ir::PositionInfoVector& output,
-                        uint32_t orig_offset,
-                        std::string* error_msg);
-bool VerifyLocalInfo(dex_ir::LocalInfoVector& orig,
-                     dex_ir::LocalInfoVector& output,
-                     uint32_t orig_offset,
-                     std::string* error_msg);
 bool VerifyTries(dex_ir::TryItemVector* orig,
                  dex_ir::TryItemVector* output,
                  uint32_t orig_offset,
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index ade0072..40449ae 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -820,37 +820,6 @@
 }
 
 /*
- * Dumps all positions table entries associated with the code.
- */
-void DexLayout::DumpPositionInfo(const dex_ir::CodeItem* code) {
-  dex_ir::DebugInfoItem* debug_info = code->DebugInfo();
-  if (debug_info == nullptr) {
-    return;
-  }
-  std::vector<std::unique_ptr<dex_ir::PositionInfo>>& positions = debug_info->GetPositionInfo();
-  for (size_t i = 0; i < positions.size(); ++i) {
-    fprintf(out_file_, "        0x%04x line=%d\n", positions[i]->address_, positions[i]->line_);
-  }
-}
-
-/*
- * Dumps all locals table entries associated with the code.
- */
-void DexLayout::DumpLocalInfo(const dex_ir::CodeItem* code) {
-  dex_ir::DebugInfoItem* debug_info = code->DebugInfo();
-  if (debug_info == nullptr) {
-    return;
-  }
-  std::vector<std::unique_ptr<dex_ir::LocalInfo>>& locals = debug_info->GetLocalInfo();
-  for (size_t i = 0; i < locals.size(); ++i) {
-    dex_ir::LocalInfo* entry = locals[i].get();
-    fprintf(out_file_, "        0x%04x - 0x%04x reg=%d %s %s %s\n",
-            entry->start_address_, entry->end_address_, entry->reg_,
-            entry->name_.c_str(), entry->descriptor_.c_str(), entry->signature_.c_str());
-  }
-}
-
-/*
  * Dumps a single instruction.
  */
 void DexLayout::DumpInstruction(const dex_ir::CodeItem* code,
@@ -1093,9 +1062,59 @@
 }
 
 /*
+ * Callback for dumping each positions table entry.
+ */
+static bool DumpPositionsCb(void* context, const DexFile::PositionInfo& entry) {
+  FILE* out_file = reinterpret_cast<FILE*>(context);
+  fprintf(out_file, "        0x%04x line=%d\n", entry.address_, entry.line_);
+  return false;
+}
+
+/*
+ * Callback for dumping locals table entry.
+ */
+static void DumpLocalsCb(void* context, const DexFile::LocalInfo& entry) {
+  const char* signature = entry.signature_ != nullptr ? entry.signature_ : "";
+  FILE* out_file = reinterpret_cast<FILE*>(context);
+  fprintf(out_file, "        0x%04x - 0x%04x reg=%d %s %s %s\n",
+          entry.start_address_, entry.end_address_, entry.reg_,
+          entry.name_, entry.descriptor_, signature);
+}
+
+/*
+ * Lookup functions.
+ */
+static const char* StringDataByIdx(uint32_t idx, dex_ir::Collections& collections) {
+  dex_ir::StringId* string_id = collections.GetStringIdOrNullPtr(idx);
+  if (string_id == nullptr) {
+    return nullptr;
+  }
+  return string_id->Data();
+}
+
+static const char* StringDataByTypeIdx(uint16_t idx, dex_ir::Collections& collections) {
+  dex_ir::TypeId* type_id = collections.GetTypeIdOrNullPtr(idx);
+  if (type_id == nullptr) {
+    return nullptr;
+  }
+  dex_ir::StringId* string_id = type_id->GetStringId();
+  if (string_id == nullptr) {
+    return nullptr;
+  }
+  return string_id->Data();
+}
+
+
+/*
  * Dumps code of a method.
  */
-void DexLayout::DumpCode(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset) {
+void DexLayout::DumpCode(uint32_t idx,
+                         const dex_ir::CodeItem* code,
+                         uint32_t code_offset,
+                         const char* declaring_class_descriptor,
+                         const char* method_name,
+                         bool is_static,
+                         const dex_ir::ProtoId* proto) {
   fprintf(out_file_, "      registers     : %d\n", code->RegistersSize());
   fprintf(out_file_, "      ins           : %d\n", code->InsSize());
   fprintf(out_file_, "      outs          : %d\n", code->OutsSize());
@@ -1111,10 +1130,48 @@
   DumpCatches(code);
 
   // Positions and locals table in the debug info.
+  dex_ir::DebugInfoItem* debug_info = code->DebugInfo();
   fprintf(out_file_, "      positions     : \n");
-  DumpPositionInfo(code);
+  if (debug_info != nullptr) {
+    DexFile::DecodeDebugPositionInfo(debug_info->GetDebugInfo(),
+                                     [this](uint32_t idx) {
+                                       return StringDataByIdx(idx, this->header_->GetCollections());
+                                     },
+                                     DumpPositionsCb,
+                                     out_file_);
+  }
   fprintf(out_file_, "      locals        : \n");
-  DumpLocalInfo(code);
+  if (debug_info != nullptr) {
+    std::vector<const char*> arg_descriptors;
+    const dex_ir::TypeList* parameters = proto->Parameters();
+    if (parameters != nullptr) {
+      const dex_ir::TypeIdVector* parameter_type_vector = parameters->GetTypeList();
+      if (parameter_type_vector != nullptr) {
+        for (const dex_ir::TypeId* type_id : *parameter_type_vector) {
+          arg_descriptors.push_back(type_id->GetStringId()->Data());
+        }
+      }
+    }
+    DexFile::DecodeDebugLocalInfo(debug_info->GetDebugInfo(),
+                                  "DexLayout in-memory",
+                                  declaring_class_descriptor,
+                                  arg_descriptors,
+                                  method_name,
+                                  is_static,
+                                  code->RegistersSize(),
+                                  code->InsSize(),
+                                  code->InsnsSize(),
+                                  [this](uint32_t idx) {
+                                    return StringDataByIdx(idx, this->header_->GetCollections());
+                                  },
+                                  [this](uint32_t idx) {
+                                    return
+                                        StringDataByTypeIdx(dchecked_integral_cast<uint16_t>(idx),
+                                                            this->header_->GetCollections());
+                                  },
+                                  DumpLocalsCb,
+                                  out_file_);
+  }
 }
 
 /*
@@ -1141,7 +1198,13 @@
       fprintf(out_file_, "      code          : (none)\n");
     } else {
       fprintf(out_file_, "      code          -\n");
-      DumpCode(idx, code, code->GetOffset());
+      DumpCode(idx,
+               code,
+               code->GetOffset(),
+               back_descriptor,
+               name,
+               (flags & kAccStatic) != 0,
+               method_id->Proto());
     }
     if (options_.disassemble_) {
       fputc('\n', out_file_);
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index 9f6e8a4..180d9bc 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -96,7 +96,13 @@
   void DumpClass(int idx, char** last_package);
   void DumpClassAnnotations(int idx);
   void DumpClassDef(int idx);
-  void DumpCode(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset);
+  void DumpCode(uint32_t idx,
+                const dex_ir::CodeItem* code,
+                uint32_t code_offset,
+                const char* declaring_class_descriptor,
+                const char* method_name,
+                bool is_static,
+                const dex_ir::ProtoId* proto);
   void DumpEncodedAnnotation(dex_ir::EncodedAnnotation* annotation);
   void DumpEncodedValue(const dex_ir::EncodedValue* data);
   void DumpFileHeader();
diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h
index 1b7c318..5dfbd9b 100644
--- a/runtime/dex_file-inl.h
+++ b/runtime/dex_file-inl.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_DEX_FILE_INL_H_
 
 #include "base/bit_utils.h"
+#include "base/casts.h"
 #include "base/logging.h"
 #include "base/stringpiece.h"
 #include "dex_file.h"
@@ -220,6 +221,280 @@
   }
 }
 
+template<typename NewLocalCallback, typename IndexToStringData, typename TypeIndexToStringData>
+bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream,
+                                   const std::string& location,
+                                   const char* declaring_class_descriptor,
+                                   const std::vector<const char*>& arg_descriptors,
+                                   const std::string& method_name,
+                                   bool is_static,
+                                   uint16_t registers_size,
+                                   uint16_t ins_size,
+                                   uint16_t insns_size_in_code_units,
+                                   IndexToStringData index_to_string_data,
+                                   TypeIndexToStringData type_index_to_string_data,
+                                   NewLocalCallback new_local_callback,
+                                   void* context) {
+  if (stream == nullptr) {
+    return false;
+  }
+  std::vector<LocalInfo> local_in_reg(registers_size);
+
+  uint16_t arg_reg = registers_size - ins_size;
+  if (!is_static) {
+    const char* descriptor = declaring_class_descriptor;
+    local_in_reg[arg_reg].name_ = "this";
+    local_in_reg[arg_reg].descriptor_ = descriptor;
+    local_in_reg[arg_reg].signature_ = nullptr;
+    local_in_reg[arg_reg].start_address_ = 0;
+    local_in_reg[arg_reg].reg_ = arg_reg;
+    local_in_reg[arg_reg].is_live_ = true;
+    arg_reg++;
+  }
+
+  DecodeUnsignedLeb128(&stream);  // Line.
+  uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
+  uint32_t i;
+  if (parameters_size != arg_descriptors.size()) {
+    LOG(ERROR) << "invalid stream - problem with parameter iterator in " << location
+               << " for method " << method_name;
+    return false;
+  }
+  for (i = 0; i < parameters_size && i < arg_descriptors.size(); ++i) {
+    if (arg_reg >= registers_size) {
+      LOG(ERROR) << "invalid stream - arg reg >= reg size (" << arg_reg
+                 << " >= " << registers_size << ") in " << location;
+      return false;
+    }
+    uint32_t name_idx = DecodeUnsignedLeb128P1(&stream);
+    const char* descriptor = arg_descriptors[i];
+    local_in_reg[arg_reg].name_ = index_to_string_data(name_idx);
+    local_in_reg[arg_reg].descriptor_ = descriptor;
+    local_in_reg[arg_reg].signature_ = nullptr;
+    local_in_reg[arg_reg].start_address_ = 0;
+    local_in_reg[arg_reg].reg_ = arg_reg;
+    local_in_reg[arg_reg].is_live_ = true;
+    switch (*descriptor) {
+      case 'D':
+      case 'J':
+        arg_reg += 2;
+        break;
+      default:
+        arg_reg += 1;
+        break;
+    }
+  }
+
+  uint32_t address = 0;
+  for (;;)  {
+    uint8_t opcode = *stream++;
+    switch (opcode) {
+      case DBG_END_SEQUENCE:
+        // Emit all variables which are still alive at the end of the method.
+        for (uint16_t reg = 0; reg < registers_size; reg++) {
+          if (local_in_reg[reg].is_live_) {
+            local_in_reg[reg].end_address_ = insns_size_in_code_units;
+            new_local_callback(context, local_in_reg[reg]);
+          }
+        }
+        return true;
+      case DBG_ADVANCE_PC:
+        address += DecodeUnsignedLeb128(&stream);
+        break;
+      case DBG_ADVANCE_LINE:
+        DecodeSignedLeb128(&stream);  // Line.
+        break;
+      case DBG_START_LOCAL:
+      case DBG_START_LOCAL_EXTENDED: {
+        uint16_t reg = DecodeUnsignedLeb128(&stream);
+        if (reg >= registers_size) {
+          LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= "
+                     << registers_size << ") in " << location;
+          return false;
+        }
+
+        uint32_t name_idx = DecodeUnsignedLeb128P1(&stream);
+        uint16_t descriptor_idx = DecodeUnsignedLeb128P1(&stream);
+        uint32_t signature_idx = dex::kDexNoIndex;
+        if (opcode == DBG_START_LOCAL_EXTENDED) {
+          signature_idx = DecodeUnsignedLeb128P1(&stream);
+        }
+
+        // Emit what was previously there, if anything
+        if (local_in_reg[reg].is_live_) {
+          local_in_reg[reg].end_address_ = address;
+          new_local_callback(context, local_in_reg[reg]);
+        }
+
+        local_in_reg[reg].name_ = index_to_string_data(name_idx);
+        local_in_reg[reg].descriptor_ = type_index_to_string_data(descriptor_idx);;
+        local_in_reg[reg].signature_ = index_to_string_data(signature_idx);
+        local_in_reg[reg].start_address_ = address;
+        local_in_reg[reg].reg_ = reg;
+        local_in_reg[reg].is_live_ = true;
+        break;
+      }
+      case DBG_END_LOCAL: {
+        uint16_t reg = DecodeUnsignedLeb128(&stream);
+        if (reg >= registers_size) {
+          LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= "
+                     << registers_size << ") in " << location;
+          return false;
+        }
+        // If the register is live, close it properly. Otherwise, closing an already
+        // closed register is sloppy, but harmless if no further action is taken.
+        if (local_in_reg[reg].is_live_) {
+          local_in_reg[reg].end_address_ = address;
+          new_local_callback(context, local_in_reg[reg]);
+          local_in_reg[reg].is_live_ = false;
+        }
+        break;
+      }
+      case DBG_RESTART_LOCAL: {
+        uint16_t reg = DecodeUnsignedLeb128(&stream);
+        if (reg >= registers_size) {
+          LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= "
+                     << registers_size << ") in " << location;
+          return false;
+        }
+        // If the register is live, the "restart" is superfluous,
+        // and we don't want to mess with the existing start address.
+        if (!local_in_reg[reg].is_live_) {
+          local_in_reg[reg].start_address_ = address;
+          local_in_reg[reg].is_live_ = true;
+        }
+        break;
+      }
+      case DBG_SET_PROLOGUE_END:
+      case DBG_SET_EPILOGUE_BEGIN:
+        break;
+      case DBG_SET_FILE:
+        DecodeUnsignedLeb128P1(&stream);  // name.
+        break;
+      default:
+        address += (opcode - DBG_FIRST_SPECIAL) / DBG_LINE_RANGE;
+        break;
+    }
+  }
+}
+
+template<typename NewLocalCallback>
+bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item,
+                                   bool is_static,
+                                   uint32_t method_idx,
+                                   NewLocalCallback new_local_callback,
+                                   void* context) const {
+  if (code_item == nullptr) {
+    return false;
+  }
+  std::vector<const char*> arg_descriptors;
+  DexFileParameterIterator it(*this, GetMethodPrototype(GetMethodId(method_idx)));
+  for (; it.HasNext(); it.Next()) {
+    arg_descriptors.push_back(it.GetDescriptor());
+  }
+  return DecodeDebugLocalInfo(GetDebugInfoStream(code_item),
+                              GetLocation(),
+                              GetMethodDeclaringClassDescriptor(GetMethodId(method_idx)),
+                              arg_descriptors,
+                              this->PrettyMethod(method_idx),
+                              is_static,
+                              code_item->registers_size_,
+                              code_item->ins_size_,
+                              code_item->insns_size_in_code_units_,
+                              [this](uint32_t idx) {
+                                return StringDataByIdx(dex::StringIndex(idx));
+                              },
+                              [this](uint32_t idx) {
+                                return StringByTypeIdx(dex::TypeIndex(
+                                    dchecked_integral_cast<uint16_t>(idx)));
+                              },
+                              new_local_callback,
+                              context);
+}
+
+template<typename DexDebugNewPosition, typename IndexToStringData>
+bool DexFile::DecodeDebugPositionInfo(const uint8_t* stream,
+                                      IndexToStringData index_to_string_data,
+                                      DexDebugNewPosition position_functor,
+                                      void* context) {
+  if (stream == nullptr) {
+    return false;
+  }
+
+  PositionInfo entry = PositionInfo();
+  entry.line_ = DecodeUnsignedLeb128(&stream);
+  uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
+  for (uint32_t i = 0; i < parameters_size; ++i) {
+    DecodeUnsignedLeb128P1(&stream);  // Parameter name.
+  }
+
+  for (;;)  {
+    uint8_t opcode = *stream++;
+    switch (opcode) {
+      case DBG_END_SEQUENCE:
+        return true;  // end of stream.
+      case DBG_ADVANCE_PC:
+        entry.address_ += DecodeUnsignedLeb128(&stream);
+        break;
+      case DBG_ADVANCE_LINE:
+        entry.line_ += DecodeSignedLeb128(&stream);
+        break;
+      case DBG_START_LOCAL:
+        DecodeUnsignedLeb128(&stream);  // reg.
+        DecodeUnsignedLeb128P1(&stream);  // name.
+        DecodeUnsignedLeb128P1(&stream);  // descriptor.
+        break;
+      case DBG_START_LOCAL_EXTENDED:
+        DecodeUnsignedLeb128(&stream);  // reg.
+        DecodeUnsignedLeb128P1(&stream);  // name.
+        DecodeUnsignedLeb128P1(&stream);  // descriptor.
+        DecodeUnsignedLeb128P1(&stream);  // signature.
+        break;
+      case DBG_END_LOCAL:
+      case DBG_RESTART_LOCAL:
+        DecodeUnsignedLeb128(&stream);  // reg.
+        break;
+      case DBG_SET_PROLOGUE_END:
+        entry.prologue_end_ = true;
+        break;
+      case DBG_SET_EPILOGUE_BEGIN:
+        entry.epilogue_begin_ = true;
+        break;
+      case DBG_SET_FILE: {
+        uint32_t name_idx = DecodeUnsignedLeb128P1(&stream);
+        entry.source_file_ = index_to_string_data(name_idx);
+        break;
+      }
+      default: {
+        int adjopcode = opcode - DBG_FIRST_SPECIAL;
+        entry.address_ += adjopcode / DBG_LINE_RANGE;
+        entry.line_ += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
+        if (position_functor(context, entry)) {
+          return true;  // early exit.
+        }
+        entry.prologue_end_ = false;
+        entry.epilogue_begin_ = false;
+        break;
+      }
+    }
+  }
+}
+
+template<typename DexDebugNewPosition>
+bool DexFile::DecodeDebugPositionInfo(const CodeItem* code_item,
+                                      DexDebugNewPosition position_functor,
+                                      void* context) const {
+  if (code_item == nullptr) {
+    return false;
+  }
+  return DecodeDebugPositionInfo(GetDebugInfoStream(code_item),
+                                 [this](uint32_t idx) {
+                                   return StringDataByIdx(dex::StringIndex(idx));
+                                 },
+                                 position_functor,
+                                 context);
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_DEX_FILE_INL_H_
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 08c047d..f2c43f7 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -537,228 +537,6 @@
   }
 }
 
-bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item, bool is_static, uint32_t method_idx,
-                                   DexDebugNewLocalCb local_cb, void* context) const {
-  DCHECK(local_cb != nullptr);
-  if (code_item == nullptr) {
-    return false;
-  }
-  const uint8_t* stream = GetDebugInfoStream(code_item);
-  if (stream == nullptr) {
-    return false;
-  }
-  std::vector<LocalInfo> local_in_reg(code_item->registers_size_);
-
-  uint16_t arg_reg = code_item->registers_size_ - code_item->ins_size_;
-  if (!is_static) {
-    const char* descriptor = GetMethodDeclaringClassDescriptor(GetMethodId(method_idx));
-    local_in_reg[arg_reg].name_ = "this";
-    local_in_reg[arg_reg].descriptor_ = descriptor;
-    local_in_reg[arg_reg].signature_ = nullptr;
-    local_in_reg[arg_reg].start_address_ = 0;
-    local_in_reg[arg_reg].reg_ = arg_reg;
-    local_in_reg[arg_reg].is_live_ = true;
-    arg_reg++;
-  }
-
-  DexFileParameterIterator it(*this, GetMethodPrototype(GetMethodId(method_idx)));
-  DecodeUnsignedLeb128(&stream);  // Line.
-  uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
-  uint32_t i;
-  for (i = 0; i < parameters_size && it.HasNext(); ++i, it.Next()) {
-    if (arg_reg >= code_item->registers_size_) {
-      LOG(ERROR) << "invalid stream - arg reg >= reg size (" << arg_reg
-                 << " >= " << code_item->registers_size_ << ") in " << GetLocation();
-      return false;
-    }
-    uint32_t name_idx = DecodeUnsignedLeb128P1(&stream);
-    const char* descriptor = it.GetDescriptor();
-    local_in_reg[arg_reg].name_ = StringDataByIdx(dex::StringIndex(name_idx));
-    local_in_reg[arg_reg].descriptor_ = descriptor;
-    local_in_reg[arg_reg].signature_ = nullptr;
-    local_in_reg[arg_reg].start_address_ = 0;
-    local_in_reg[arg_reg].reg_ = arg_reg;
-    local_in_reg[arg_reg].is_live_ = true;
-    switch (*descriptor) {
-      case 'D':
-      case 'J':
-        arg_reg += 2;
-        break;
-      default:
-        arg_reg += 1;
-        break;
-    }
-  }
-  if (i != parameters_size || it.HasNext()) {
-    LOG(ERROR) << "invalid stream - problem with parameter iterator in " << GetLocation()
-               << " for method " << this->PrettyMethod(method_idx);
-    return false;
-  }
-
-  uint32_t address = 0;
-  for (;;)  {
-    uint8_t opcode = *stream++;
-    switch (opcode) {
-      case DBG_END_SEQUENCE:
-        // Emit all variables which are still alive at the end of the method.
-        for (uint16_t reg = 0; reg < code_item->registers_size_; reg++) {
-          if (local_in_reg[reg].is_live_) {
-            local_in_reg[reg].end_address_ = code_item->insns_size_in_code_units_;
-            local_cb(context, local_in_reg[reg]);
-          }
-        }
-        return true;
-      case DBG_ADVANCE_PC:
-        address += DecodeUnsignedLeb128(&stream);
-        break;
-      case DBG_ADVANCE_LINE:
-        DecodeSignedLeb128(&stream);  // Line.
-        break;
-      case DBG_START_LOCAL:
-      case DBG_START_LOCAL_EXTENDED: {
-        uint16_t reg = DecodeUnsignedLeb128(&stream);
-        if (reg >= code_item->registers_size_) {
-          LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= "
-                     << code_item->registers_size_ << ") in " << GetLocation();
-          return false;
-        }
-
-        uint32_t name_idx = DecodeUnsignedLeb128P1(&stream);
-        uint16_t descriptor_idx = DecodeUnsignedLeb128P1(&stream);
-        uint32_t signature_idx = dex::kDexNoIndex;
-        if (opcode == DBG_START_LOCAL_EXTENDED) {
-          signature_idx = DecodeUnsignedLeb128P1(&stream);
-        }
-
-        // Emit what was previously there, if anything
-        if (local_in_reg[reg].is_live_) {
-          local_in_reg[reg].end_address_ = address;
-          local_cb(context, local_in_reg[reg]);
-        }
-
-        local_in_reg[reg].name_ = StringDataByIdx(dex::StringIndex(name_idx));
-        local_in_reg[reg].descriptor_ =
-            StringByTypeIdx(dex::TypeIndex(dchecked_integral_cast<uint16_t>(descriptor_idx)));;
-        local_in_reg[reg].signature_ = StringDataByIdx(dex::StringIndex(signature_idx));
-        local_in_reg[reg].start_address_ = address;
-        local_in_reg[reg].reg_ = reg;
-        local_in_reg[reg].is_live_ = true;
-        break;
-      }
-      case DBG_END_LOCAL: {
-        uint16_t reg = DecodeUnsignedLeb128(&stream);
-        if (reg >= code_item->registers_size_) {
-          LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= "
-                     << code_item->registers_size_ << ") in " << GetLocation();
-          return false;
-        }
-        // If the register is live, close it properly. Otherwise, closing an already
-        // closed register is sloppy, but harmless if no further action is taken.
-        if (local_in_reg[reg].is_live_) {
-          local_in_reg[reg].end_address_ = address;
-          local_cb(context, local_in_reg[reg]);
-          local_in_reg[reg].is_live_ = false;
-        }
-        break;
-      }
-      case DBG_RESTART_LOCAL: {
-        uint16_t reg = DecodeUnsignedLeb128(&stream);
-        if (reg >= code_item->registers_size_) {
-          LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= "
-                     << code_item->registers_size_ << ") in " << GetLocation();
-          return false;
-        }
-        // If the register is live, the "restart" is superfluous,
-        // and we don't want to mess with the existing start address.
-        if (!local_in_reg[reg].is_live_) {
-          local_in_reg[reg].start_address_ = address;
-          local_in_reg[reg].is_live_ = true;
-        }
-        break;
-      }
-      case DBG_SET_PROLOGUE_END:
-      case DBG_SET_EPILOGUE_BEGIN:
-        break;
-      case DBG_SET_FILE:
-        DecodeUnsignedLeb128P1(&stream);  // name.
-        break;
-      default:
-        address += (opcode - DBG_FIRST_SPECIAL) / DBG_LINE_RANGE;
-        break;
-    }
-  }
-}
-
-bool DexFile::DecodeDebugPositionInfo(const CodeItem* code_item, DexDebugNewPositionCb position_cb,
-                                      void* context) const {
-  DCHECK(position_cb != nullptr);
-  if (code_item == nullptr) {
-    return false;
-  }
-  const uint8_t* stream = GetDebugInfoStream(code_item);
-  if (stream == nullptr) {
-    return false;
-  }
-
-  PositionInfo entry = PositionInfo();
-  entry.line_ = DecodeUnsignedLeb128(&stream);
-  uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
-  for (uint32_t i = 0; i < parameters_size; ++i) {
-    DecodeUnsignedLeb128P1(&stream);  // Parameter name.
-  }
-
-  for (;;)  {
-    uint8_t opcode = *stream++;
-    switch (opcode) {
-      case DBG_END_SEQUENCE:
-        return true;  // end of stream.
-      case DBG_ADVANCE_PC:
-        entry.address_ += DecodeUnsignedLeb128(&stream);
-        break;
-      case DBG_ADVANCE_LINE:
-        entry.line_ += DecodeSignedLeb128(&stream);
-        break;
-      case DBG_START_LOCAL:
-        DecodeUnsignedLeb128(&stream);  // reg.
-        DecodeUnsignedLeb128P1(&stream);  // name.
-        DecodeUnsignedLeb128P1(&stream);  // descriptor.
-        break;
-      case DBG_START_LOCAL_EXTENDED:
-        DecodeUnsignedLeb128(&stream);  // reg.
-        DecodeUnsignedLeb128P1(&stream);  // name.
-        DecodeUnsignedLeb128P1(&stream);  // descriptor.
-        DecodeUnsignedLeb128P1(&stream);  // signature.
-        break;
-      case DBG_END_LOCAL:
-      case DBG_RESTART_LOCAL:
-        DecodeUnsignedLeb128(&stream);  // reg.
-        break;
-      case DBG_SET_PROLOGUE_END:
-        entry.prologue_end_ = true;
-        break;
-      case DBG_SET_EPILOGUE_BEGIN:
-        entry.epilogue_begin_ = true;
-        break;
-      case DBG_SET_FILE: {
-        uint32_t name_idx = DecodeUnsignedLeb128P1(&stream);
-        entry.source_file_ = StringDataByIdx(dex::StringIndex(name_idx));
-        break;
-      }
-      default: {
-        int adjopcode = opcode - DBG_FIRST_SPECIAL;
-        entry.address_ += adjopcode / DBG_LINE_RANGE;
-        entry.line_ += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
-        if (position_cb(context, entry)) {
-          return true;  // early exit.
-        }
-        entry.prologue_end_ = false;
-        entry.epilogue_begin_ = false;
-        break;
-      }
-    }
-  }
-}
-
 bool DexFile::LineNumForPcCb(void* raw_context, const PositionInfo& entry) {
   LineNumFromPcContext* context = reinterpret_cast<LineNumFromPcContext*>(raw_context);
 
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 5759684..6868d52 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -772,10 +772,6 @@
     bool epilogue_begin_ = false;
   };
 
-  // Callback for "new position table entry".
-  // Returning true causes the decoder to stop early.
-  typedef bool (*DexDebugNewPositionCb)(void* context, const PositionInfo& entry);
-
   struct LocalInfo {
     LocalInfo() = default;
 
@@ -899,11 +895,36 @@
   };
 
   // Returns false if there is no debugging information or if it cannot be decoded.
-  bool DecodeDebugLocalInfo(const CodeItem* code_item, bool is_static, uint32_t method_idx,
-                            DexDebugNewLocalCb local_cb, void* context) const;
+  template<typename NewLocalCallback, typename IndexToStringData, typename TypeIndexToStringData>
+  static bool DecodeDebugLocalInfo(const uint8_t* stream,
+                                   const std::string& location,
+                                   const char* declaring_class_descriptor,
+                                   const std::vector<const char*>& arg_descriptors,
+                                   const std::string& method_name,
+                                   bool is_static,
+                                   uint16_t registers_size,
+                                   uint16_t ins_size,
+                                   uint16_t insns_size_in_code_units,
+                                   IndexToStringData index_to_string_data,
+                                   TypeIndexToStringData type_index_to_string_data,
+                                   NewLocalCallback new_local,
+                                   void* context);
+  template<typename NewLocalCallback>
+  bool DecodeDebugLocalInfo(const CodeItem* code_item,
+                            bool is_static,
+                            uint32_t method_idx,
+                            NewLocalCallback new_local,
+                            void* context) const;
 
   // Returns false if there is no debugging information or if it cannot be decoded.
-  bool DecodeDebugPositionInfo(const CodeItem* code_item, DexDebugNewPositionCb position_cb,
+  template<typename DexDebugNewPosition, typename IndexToStringData>
+  static bool DecodeDebugPositionInfo(const uint8_t* stream,
+                                      IndexToStringData index_to_string_data,
+                                      DexDebugNewPosition position_functor,
+                                      void* context);
+  template<typename DexDebugNewPosition>
+  bool DecodeDebugPositionInfo(const CodeItem* code_item,
+                               DexDebugNewPosition position_functor,
                                void* context) const;
 
   const char* GetSourceFile(const ClassDef& class_def) const {