Merge "Split .debug_info section to compilation units."
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