Split .debug_info section to compilation units.

This resolves old TODO in the code.  It is also very important
for performance and memory usage of any tools which might be
consuming the debug data.  Without this split, the tools
would be forced to decode (and probably store in memory) all of
debug data even for a simple query.  With this change it should
be easy to find only the relevant compilation unit.

Change-Id: I9f285eaa01632b6adea39cae866df6f76e9d0062
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index 6fe05a4..39233ce 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -188,12 +188,6 @@
                         std::vector<uint8_t>* debug_line) {
   const std::vector<OatWriter::DebugInfo>& method_infos = oat_writer->GetMethodDebugInfo();
   const InstructionSet isa = compiler->GetInstructionSet();
-  uint32_t cunit_low_pc = static_cast<uint32_t>(-1);
-  uint32_t cunit_high_pc = 0;
-  for (auto method_info : method_infos) {
-    cunit_low_pc = std::min(cunit_low_pc, method_info.low_pc_);
-    cunit_high_pc = std::max(cunit_high_pc, method_info.high_pc_);
-  }
 
   // Find all addresses (low_pc) which contain deduped methods.
   // The first instance of method is not marked deduped_, but the rest is.
@@ -204,173 +198,187 @@
     }
   }
 
+  // Group the methods into compilation units based on source file.
+  std::vector<std::vector<const OatWriter::DebugInfo*>> compilation_units;
+  const char* last_source_file = nullptr;
+  for (const auto& mi : method_infos) {
+    // Attribute given instruction range only to single method.
+    // Otherwise the debugger might get really confused.
+    if (!mi.deduped_) {
+      auto& dex_class_def = mi.dex_file_->GetClassDef(mi.class_def_index_);
+      const char* source_file = mi.dex_file_->GetSourceFile(dex_class_def);
+      if (compilation_units.empty() || source_file != last_source_file) {
+        compilation_units.push_back(std::vector<const OatWriter::DebugInfo*>());
+      }
+      compilation_units.back().push_back(&mi);
+      last_source_file = source_file;
+    }
+  }
+
   // Write .debug_info section.
-  size_t debug_abbrev_offset = debug_abbrev->size();
-  DebugInfoEntryWriter<> info(false /* 32 bit */, debug_abbrev);
-  info.StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes);
-  info.WriteStrp(DW_AT_producer, "Android dex2oat", debug_str);
-  info.WriteData1(DW_AT_language, DW_LANG_Java);
-  info.WriteAddr(DW_AT_low_pc, cunit_low_pc + text_section_offset);
-  info.WriteAddr(DW_AT_high_pc, cunit_high_pc + text_section_offset);
-  info.WriteData4(DW_AT_stmt_list, debug_line->size());
-  for (auto method_info : method_infos) {
-    std::string method_name = PrettyMethod(method_info.dex_method_index_,
-                                           *method_info.dex_file_, true);
-    if (deduped_addresses.find(method_info.low_pc_) != deduped_addresses.end()) {
-      method_name += " [DEDUPED]";
-    }
-    info.StartTag(DW_TAG_subprogram, DW_CHILDREN_no);
-    info.WriteStrp(DW_AT_name, method_name.data(), debug_str);
-    info.WriteAddr(DW_AT_low_pc, method_info.low_pc_ + text_section_offset);
-    info.WriteAddr(DW_AT_high_pc, method_info.high_pc_ + text_section_offset);
-    info.EndTag();  // DW_TAG_subprogram
-  }
-  info.EndTag();  // DW_TAG_compile_unit
-  auto* debug_info_patches = oat_writer->GetAbsolutePatchLocationsFor(".debug_info");
-  WriteDebugInfoCU(debug_abbrev_offset, info, debug_info, debug_info_patches);
-
-  // TODO: in gdb info functions <regexp> - reports Java functions, but
-  // source file is <unknown> because .debug_line is formed as one
-  // compilation unit. To fix this it is possible to generate
-  // a separate compilation unit for every distinct Java source.
-  // Each of the these compilation units can have several non-adjacent
-  // method ranges.
-
-  // Write .debug_line section.
-  std::vector<FileEntry> files;
-  std::unordered_map<std::string, size_t> files_map;
-  std::vector<std::string> directories;
-  std::unordered_map<std::string, size_t> directories_map;
-  int code_factor_bits_ = 0;
-  int dwarf_isa = -1;
-  switch (isa) {
-    case kArm:  // arm actually means thumb2.
-    case kThumb2:
-      code_factor_bits_ = 1;  // 16-bit instuctions
-      dwarf_isa = 1;  // DW_ISA_ARM_thumb.
-      break;
-    case kArm64:
-    case kMips:
-    case kMips64:
-      code_factor_bits_ = 2;  // 32-bit instructions
-      break;
-    case kNone:
-    case kX86:
-    case kX86_64:
-      break;
-  }
-  DebugLineOpCodeWriter<> opcodes(false /* 32bit */, code_factor_bits_);
-  opcodes.SetAddress(text_section_offset + cunit_low_pc);
-  if (dwarf_isa != -1) {
-    opcodes.SetISA(dwarf_isa);
-  }
-  for (const OatWriter::DebugInfo& mi : method_infos) {
-    // Addresses in the line table should be unique and increasing.
-    if (mi.deduped_) {
-      continue;
+  for (const auto& compilation_unit : compilation_units) {
+    uint32_t cunit_low_pc = 0xFFFFFFFFU;
+    uint32_t cunit_high_pc = 0;
+    for (auto method_info : compilation_unit) {
+      cunit_low_pc = std::min(cunit_low_pc, method_info->low_pc_);
+      cunit_high_pc = std::max(cunit_high_pc, method_info->high_pc_);
     }
 
-    struct DebugInfoCallbacks {
-      static bool NewPosition(void* ctx, uint32_t address, uint32_t line) {
-        auto* context = reinterpret_cast<DebugInfoCallbacks*>(ctx);
-        context->dex2line_.push_back({address, static_cast<int32_t>(line)});
-        return false;
+    size_t debug_abbrev_offset = debug_abbrev->size();
+    DebugInfoEntryWriter<> info(false /* 32 bit */, debug_abbrev);
+    info.StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes);
+    info.WriteStrp(DW_AT_producer, "Android dex2oat", debug_str);
+    info.WriteData1(DW_AT_language, DW_LANG_Java);
+    info.WriteAddr(DW_AT_low_pc, cunit_low_pc + text_section_offset);
+    info.WriteAddr(DW_AT_high_pc, cunit_high_pc + text_section_offset);
+    info.WriteData4(DW_AT_stmt_list, debug_line->size());
+    for (auto method_info : compilation_unit) {
+      std::string method_name = PrettyMethod(method_info->dex_method_index_,
+                                             *method_info->dex_file_, true);
+      if (deduped_addresses.find(method_info->low_pc_) != deduped_addresses.end()) {
+        method_name += " [DEDUPED]";
       }
-      DefaultSrcMap dex2line_;
-    } debug_info_callbacks;
-
-    const DexFile* dex = mi.dex_file_;
-    if (mi.code_item_ != nullptr) {
-      dex->DecodeDebugInfo(mi.code_item_,
-                           (mi.access_flags_ & kAccStatic) != 0,
-                           mi.dex_method_index_,
-                           DebugInfoCallbacks::NewPosition,
-                           nullptr,
-                           &debug_info_callbacks);
+      info.StartTag(DW_TAG_subprogram, DW_CHILDREN_no);
+      info.WriteStrp(DW_AT_name, method_name.data(), debug_str);
+      info.WriteAddr(DW_AT_low_pc, method_info->low_pc_ + text_section_offset);
+      info.WriteAddr(DW_AT_high_pc, method_info->high_pc_ + text_section_offset);
+      info.EndTag();  // DW_TAG_subprogram
     }
+    info.EndTag();  // DW_TAG_compile_unit
+    auto* debug_info_patches = oat_writer->GetAbsolutePatchLocationsFor(".debug_info");
+    WriteDebugInfoCU(debug_abbrev_offset, info, debug_info, debug_info_patches);
 
-    // Get and deduplicate directory and filename.
-    int file_index = 0;  // 0 - primary source file of the compilation.
-    auto& dex_class_def = dex->GetClassDef(mi.class_def_index_);
-    const char* source_file = dex->GetSourceFile(dex_class_def);
-    if (source_file != nullptr) {
-      std::string file_name(source_file);
-      size_t file_name_slash = file_name.find_last_of('/');
-      std::string class_name(dex->GetClassDescriptor(dex_class_def));
-      size_t class_name_slash = class_name.find_last_of('/');
-      std::string full_path(file_name);
-
-      // Guess directory from package name.
-      int directory_index = 0;  // 0 - current directory of the compilation.
-      if (file_name_slash == std::string::npos &&  // Just filename.
-          class_name.front() == 'L' &&  // Type descriptor for a class.
-          class_name_slash != std::string::npos) {  // Has package name.
-        std::string package_name = class_name.substr(1, class_name_slash - 1);
-        auto it = directories_map.find(package_name);
-        if (it == directories_map.end()) {
-          directory_index = 1 + directories.size();
-          directories_map.emplace(package_name, directory_index);
-          directories.push_back(package_name);
-        } else {
-          directory_index = it->second;
+    // Write .debug_line section.
+    std::vector<FileEntry> files;
+    std::unordered_map<std::string, size_t> files_map;
+    std::vector<std::string> directories;
+    std::unordered_map<std::string, size_t> directories_map;
+    int code_factor_bits_ = 0;
+    int dwarf_isa = -1;
+    switch (isa) {
+      case kArm:  // arm actually means thumb2.
+      case kThumb2:
+        code_factor_bits_ = 1;  // 16-bit instuctions
+        dwarf_isa = 1;  // DW_ISA_ARM_thumb.
+        break;
+      case kArm64:
+      case kMips:
+      case kMips64:
+        code_factor_bits_ = 2;  // 32-bit instructions
+        break;
+      case kNone:
+      case kX86:
+      case kX86_64:
+        break;
+    }
+    DebugLineOpCodeWriter<> opcodes(false /* 32bit */, code_factor_bits_);
+    opcodes.SetAddress(text_section_offset + cunit_low_pc);
+    if (dwarf_isa != -1) {
+      opcodes.SetISA(dwarf_isa);
+    }
+    for (const OatWriter::DebugInfo* mi : compilation_unit) {
+      struct DebugInfoCallbacks {
+        static bool NewPosition(void* ctx, uint32_t address, uint32_t line) {
+          auto* context = reinterpret_cast<DebugInfoCallbacks*>(ctx);
+          context->dex2line_.push_back({address, static_cast<int32_t>(line)});
+          return false;
         }
-        full_path = package_name + "/" + file_name;
+        DefaultSrcMap dex2line_;
+      } debug_info_callbacks;
+
+      const DexFile* dex = mi->dex_file_;
+      if (mi->code_item_ != nullptr) {
+        dex->DecodeDebugInfo(mi->code_item_,
+                             (mi->access_flags_ & kAccStatic) != 0,
+                             mi->dex_method_index_,
+                             DebugInfoCallbacks::NewPosition,
+                             nullptr,
+                             &debug_info_callbacks);
       }
 
-      // Add file entry.
-      auto it2 = files_map.find(full_path);
-      if (it2 == files_map.end()) {
-        file_index = 1 + files.size();
-        files_map.emplace(full_path, file_index);
-        files.push_back(FileEntry {
-          file_name,
-          directory_index,
-          0,  // Modification time - NA.
-          0,  // File size - NA.
-        });
-      } else {
-        file_index = it2->second;
-      }
-    }
-    opcodes.SetFile(file_index);
+      // Get and deduplicate directory and filename.
+      int file_index = 0;  // 0 - primary source file of the compilation.
+      auto& dex_class_def = dex->GetClassDef(mi->class_def_index_);
+      const char* source_file = dex->GetSourceFile(dex_class_def);
+      if (source_file != nullptr) {
+        std::string file_name(source_file);
+        size_t file_name_slash = file_name.find_last_of('/');
+        std::string class_name(dex->GetClassDescriptor(dex_class_def));
+        size_t class_name_slash = class_name.find_last_of('/');
+        std::string full_path(file_name);
 
-    // Generate mapping opcodes from PC to Java lines.
-    const DefaultSrcMap& dex2line_map = debug_info_callbacks.dex2line_;
-    uint32_t low_pc = text_section_offset + mi.low_pc_;
-    if (file_index != 0 && !dex2line_map.empty()) {
-      bool first = true;
-      for (SrcMapElem pc2dex : mi.compiled_method_->GetSrcMappingTable()) {
-        uint32_t pc = pc2dex.from_;
-        int dex_pc = pc2dex.to_;
-        auto dex2line = dex2line_map.Find(static_cast<uint32_t>(dex_pc));
-        if (dex2line.first) {
-          int line = dex2line.second;
-          if (first) {
-            first = false;
-            if (pc > 0) {
-              // Assume that any preceding code is prologue.
-              int first_line = dex2line_map.front().to_;
-              // Prologue is not a sensible place for a breakpoint.
-              opcodes.NegateStmt();
-              opcodes.AddRow(low_pc, first_line);
-              opcodes.NegateStmt();
-              opcodes.SetPrologueEnd();
+        // Guess directory from package name.
+        int directory_index = 0;  // 0 - current directory of the compilation.
+        if (file_name_slash == std::string::npos &&  // Just filename.
+            class_name.front() == 'L' &&  // Type descriptor for a class.
+            class_name_slash != std::string::npos) {  // Has package name.
+          std::string package_name = class_name.substr(1, class_name_slash - 1);
+          auto it = directories_map.find(package_name);
+          if (it == directories_map.end()) {
+            directory_index = 1 + directories.size();
+            directories_map.emplace(package_name, directory_index);
+            directories.push_back(package_name);
+          } else {
+            directory_index = it->second;
+          }
+          full_path = package_name + "/" + file_name;
+        }
+
+        // Add file entry.
+        auto it2 = files_map.find(full_path);
+        if (it2 == files_map.end()) {
+          file_index = 1 + files.size();
+          files_map.emplace(full_path, file_index);
+          files.push_back(FileEntry {
+            file_name,
+            directory_index,
+            0,  // Modification time - NA.
+            0,  // File size - NA.
+          });
+        } else {
+          file_index = it2->second;
+        }
+      }
+      opcodes.SetFile(file_index);
+
+      // Generate mapping opcodes from PC to Java lines.
+      const DefaultSrcMap& dex2line_map = debug_info_callbacks.dex2line_;
+      uint32_t low_pc = text_section_offset + mi->low_pc_;
+      if (file_index != 0 && !dex2line_map.empty()) {
+        bool first = true;
+        for (SrcMapElem pc2dex : mi->compiled_method_->GetSrcMappingTable()) {
+          uint32_t pc = pc2dex.from_;
+          int dex_pc = pc2dex.to_;
+          auto dex2line = dex2line_map.Find(static_cast<uint32_t>(dex_pc));
+          if (dex2line.first) {
+            int line = dex2line.second;
+            if (first) {
+              first = false;
+              if (pc > 0) {
+                // Assume that any preceding code is prologue.
+                int first_line = dex2line_map.front().to_;
+                // Prologue is not a sensible place for a breakpoint.
+                opcodes.NegateStmt();
+                opcodes.AddRow(low_pc, first_line);
+                opcodes.NegateStmt();
+                opcodes.SetPrologueEnd();
+              }
+              opcodes.AddRow(low_pc + pc, line);
+            } else if (line != opcodes.CurrentLine()) {
+              opcodes.AddRow(low_pc + pc, line);
             }
-            opcodes.AddRow(low_pc + pc, line);
-          } else if (line != opcodes.CurrentLine()) {
-            opcodes.AddRow(low_pc + pc, line);
           }
         }
+      } else {
+        // line 0 - instruction cannot be attributed to any source line.
+        opcodes.AddRow(low_pc, 0);
       }
-    } else {
-      // line 0 - instruction cannot be attributed to any source line.
-      opcodes.AddRow(low_pc, 0);
     }
+    opcodes.AdvancePC(text_section_offset + cunit_high_pc);
+    opcodes.EndSequence();
+    auto* debug_line_patches = oat_writer->GetAbsolutePatchLocationsFor(".debug_line");
+    WriteDebugLineTable(directories, files, opcodes, debug_line, debug_line_patches);
   }
-  opcodes.AdvancePC(text_section_offset + cunit_high_pc);
-  opcodes.EndSequence();
-  auto* debug_line_patches = oat_writer->GetAbsolutePatchLocationsFor(".debug_line");
-  WriteDebugLineTable(directories, files, opcodes, debug_line, debug_line_patches);
 }
 
 }  // namespace dwarf