Refactor jit debugger interface and its ELF creation.

Make it possible to store more then one method per entry,
and ref-count the number of live methods per entry.

Test: m test-art-host-gtest
Change-Id: I45d69185e85e47fbee88a8d1f549ede9875a3c0a
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index bb2a214..df5bb37 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -137,10 +137,17 @@
     InstructionSet isa,
     const InstructionSetFeatures* features,
     bool mini_debug_info,
-    const MethodDebugInfo& mi) {
-  CHECK_EQ(mi.is_code_address_text_relative, false);
+    ArrayRef<const MethodDebugInfo> method_infos) {
+  CHECK_GT(method_infos.size(), 0u);
+  uint64_t min_address = std::numeric_limits<uint64_t>::max();
+  uint64_t max_address = 0;
+  for (const MethodDebugInfo& mi : method_infos) {
+    CHECK_EQ(mi.is_code_address_text_relative, false);
+    min_address = std::min(min_address, mi.code_address);
+    max_address = std::max(max_address, mi.code_address + mi.code_size);
+  }
   DebugInfo debug_info{};
-  debug_info.compiled_methods = ArrayRef<const debug::MethodDebugInfo>(&mi, 1);
+  debug_info.compiled_methods = method_infos;
   std::vector<uint8_t> buffer;
   buffer.reserve(KB);
   linker::VectorOutputStream out("Debug ELF file", &buffer);
@@ -151,14 +158,14 @@
   if (mini_debug_info) {
     std::vector<uint8_t> mdi = MakeMiniDebugInfo(isa,
                                                  features,
-                                                 mi.code_address,
-                                                 mi.code_size,
+                                                 min_address,
+                                                 max_address - min_address,
                                                  /* dex_section_address */ 0,
                                                  /* dex_section_size */ 0,
                                                  debug_info);
     builder->WriteSection(".gnu_debugdata", &mdi);
   } else {
-    builder->GetText()->AllocateVirtualMemory(mi.code_address, mi.code_size);
+    builder->GetText()->AllocateVirtualMemory(min_address, max_address - min_address);
     WriteDebugInfo(builder.get(),
                    debug_info,
                    dwarf::DW_DEBUG_FRAME_FORMAT,
@@ -173,11 +180,11 @@
     InstructionSet isa,
     const InstructionSetFeatures* features,
     bool mini_debug_info,
-    const MethodDebugInfo& method_info) {
+    ArrayRef<const MethodDebugInfo> method_infos) {
   if (Is64BitInstructionSet(isa)) {
-    return MakeElfFileForJITInternal<ElfTypes64>(isa, features, mini_debug_info, method_info);
+    return MakeElfFileForJITInternal<ElfTypes64>(isa, features, mini_debug_info, method_infos);
   } else {
-    return MakeElfFileForJITInternal<ElfTypes32>(isa, features, mini_debug_info, method_info);
+    return MakeElfFileForJITInternal<ElfTypes32>(isa, features, mini_debug_info, method_infos);
   }
 }
 
diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h
index 8ad0c42..e442e00 100644
--- a/compiler/debug/elf_debug_writer.h
+++ b/compiler/debug/elf_debug_writer.h
@@ -54,7 +54,7 @@
     InstructionSet isa,
     const InstructionSetFeatures* features,
     bool mini_debug_info,
-    const MethodDebugInfo& method_info);
+    ArrayRef<const MethodDebugInfo> method_infos);
 
 std::vector<uint8_t> WriteDebugElfFileForClasses(
     InstructionSet isa,
diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h
index a853714..9c9e8b3 100644
--- a/compiler/debug/elf_symtab_writer.h
+++ b/compiler/debug/elf_symtab_writer.h
@@ -72,8 +72,8 @@
       continue;  // Add symbol only for the first instance.
     }
     size_t name_offset;
-    if (!info.trampoline_name.empty()) {
-      name_offset = strtab->Write(info.trampoline_name);
+    if (!info.custom_name.empty()) {
+      name_offset = strtab->Write(info.custom_name);
     } else {
       DCHECK(info.dex_file != nullptr);
       std::string name = info.dex_file->PrettyMethod(info.dex_method_index, !mini_debug_info);
diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h
index 43c8de2..d0b03ec 100644
--- a/compiler/debug/method_debug_info.h
+++ b/compiler/debug/method_debug_info.h
@@ -27,7 +27,7 @@
 namespace debug {
 
 struct MethodDebugInfo {
-  std::string trampoline_name;
+  std::string custom_name;
   const DexFile* dex_file;  // Native methods (trampolines) do not reference dex file.
   size_t class_def_index;
   uint32_t dex_method_index;
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 88e3e5b..2c62095 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -76,6 +76,7 @@
     const ArrayRef<mirror::Class*> types_array(types, count);
     std::vector<uint8_t> elf_file = debug::WriteDebugElfFileForClasses(
         kRuntimeISA, jit_compiler->GetCompilerDriver()->GetInstructionSetFeatures(), types_array);
+    MutexLock mu(Thread::Current(), g_jit_debug_mutex);
     CreateJITCodeEntry(std::move(elf_file));
   }
 }
diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc
index cedbe5d..6e0286a 100644
--- a/compiler/linker/arm/relative_patcher_arm_base.cc
+++ b/compiler/linker/arm/relative_patcher_arm_base.cc
@@ -250,12 +250,12 @@
     for (size_t i = start, num = data.NumberOfThunks(); i != num; ++i) {
       debug::MethodDebugInfo info = {};
       if (i == 0u) {
-        info.trampoline_name = base_name;
+        info.custom_name = base_name;
       } else {
         // Add a disambiguating tag for subsequent identical thunks. Since the `thunks_`
         // keeps records also for thunks in previous oat files, names based on the thunk
         // index shall be unique across the whole multi-oat output.
-        info.trampoline_name = base_name + "_" + std::to_string(i);
+        info.custom_name = base_name + "_" + std::to_string(i);
       }
       info.isa = instruction_set_;
       info.is_code_address_text_relative = true;
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index a3b1f0c..c35c490 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -382,6 +382,8 @@
                             PassObserver* pass_observer,
                             VariableSizedHandleScope* handles) const;
 
+  void GenerateJitDebugInfo(debug::MethodDebugInfo method_debug_info);
+
   std::unique_ptr<OptimizingCompilerStats> compilation_stats_;
 
   std::unique_ptr<std::ostream> visualizer_output_;
@@ -1230,7 +1232,7 @@
       const auto* method_header = reinterpret_cast<const OatQuickMethodHeader*>(code);
       const uintptr_t code_address = reinterpret_cast<uintptr_t>(method_header->GetCode());
       debug::MethodDebugInfo info = {};
-      DCHECK(info.trampoline_name.empty());
+      DCHECK(info.custom_name.empty());
       info.dex_file = dex_file;
       info.class_def_index = class_def_idx;
       info.dex_method_index = method_idx;
@@ -1246,14 +1248,7 @@
       info.frame_size_in_bytes = method_header->GetFrameSizeInBytes();
       info.code_info = nullptr;
       info.cfi = jni_compiled_method.GetCfi();
-      // If both flags are passed, generate full debug info.
-      const bool mini_debug_info = !compiler_options.GetGenerateDebugInfo();
-      std::vector<uint8_t> elf_file = debug::MakeElfFileForJIT(
-          GetCompilerDriver()->GetInstructionSet(),
-          GetCompilerDriver()->GetInstructionSetFeatures(),
-          mini_debug_info,
-          info);
-      CreateJITCodeEntryForAddress(code_address, std::move(elf_file));
+      GenerateJitDebugInfo(info);
     }
 
     Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed());
@@ -1361,7 +1356,7 @@
     const auto* method_header = reinterpret_cast<const OatQuickMethodHeader*>(code);
     const uintptr_t code_address = reinterpret_cast<uintptr_t>(method_header->GetCode());
     debug::MethodDebugInfo info = {};
-    DCHECK(info.trampoline_name.empty());
+    DCHECK(info.custom_name.empty());
     info.dex_file = dex_file;
     info.class_def_index = class_def_idx;
     info.dex_method_index = method_idx;
@@ -1377,14 +1372,7 @@
     info.frame_size_in_bytes = method_header->GetFrameSizeInBytes();
     info.code_info = stack_map_size == 0 ? nullptr : stack_map_data;
     info.cfi = ArrayRef<const uint8_t>(*codegen->GetAssembler()->cfi().data());
-    // If both flags are passed, generate full debug info.
-    const bool mini_debug_info = !compiler_options.GetGenerateDebugInfo();
-    std::vector<uint8_t> elf_file = debug::MakeElfFileForJIT(
-        GetCompilerDriver()->GetInstructionSet(),
-        GetCompilerDriver()->GetInstructionSetFeatures(),
-        mini_debug_info,
-        info);
-    CreateJITCodeEntryForAddress(code_address, std::move(elf_file));
+    GenerateJitDebugInfo(info);
   }
 
   Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed());
@@ -1408,4 +1396,22 @@
   return true;
 }
 
+void OptimizingCompiler::GenerateJitDebugInfo(debug::MethodDebugInfo info) {
+  const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions();
+  DCHECK(compiler_options.GenerateAnyDebugInfo());
+
+  // If both flags are passed, generate full debug info.
+  const bool mini_debug_info = !compiler_options.GetGenerateDebugInfo();
+
+  // Create entry for the single method that we just compiled.
+  std::vector<uint8_t> elf_file = debug::MakeElfFileForJIT(
+      GetCompilerDriver()->GetInstructionSet(),
+      GetCompilerDriver()->GetInstructionSetFeatures(),
+      mini_debug_info,
+      ArrayRef<const debug::MethodDebugInfo>(&info, 1));
+  MutexLock mu(Thread::Current(), g_jit_debug_mutex);
+  JITCodeEntry* entry = CreateJITCodeEntry(elf_file);
+  IncrementJITCodeEntryRefcount(entry, info.code_address);
+}
+
 }  // namespace art
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 849887c..66041bb 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -1336,7 +1336,7 @@
       bool has_code_info = method_header->IsOptimized();
       // Record debug information for this function if we are doing that.
       debug::MethodDebugInfo& info = writer_->method_info_[debug_info_idx];
-      DCHECK(info.trampoline_name.empty());
+      DCHECK(info.custom_name.empty());
       info.dex_file = method_ref.dex_file;
       info.class_def_index = class_def_index;
       info.dex_method_index = method_ref.index;
@@ -2420,7 +2420,7 @@
       (field) = compiler_driver_->Create ## fn_name();                      \
       if (generate_debug_info) {                                            \
         debug::MethodDebugInfo info = {};                                   \
-        info.trampoline_name = #fn_name;                                    \
+        info.custom_name = #fn_name;                                        \
         info.isa = instruction_set;                                         \
         info.is_code_address_text_relative = true;                          \
         /* Use the code offset rather than the `adjusted_offset`. */        \
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 2b90614..fcbf2f1 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -180,7 +180,7 @@
     #define DO_TRAMPOLINE(fn_name)                                                \
       if (oat_header.Get ## fn_name ## Offset() != 0) {                           \
         debug::MethodDebugInfo info = {};                                         \
-        info.trampoline_name = #fn_name;                                          \
+        info.custom_name = #fn_name;                                              \
         info.isa = oat_header.GetInstructionSet();                                \
         info.is_code_address_text_relative = true;                                \
         size_t code_offset = oat_header.Get ## fn_name ## Offset();               \
@@ -308,7 +308,7 @@
     const void* code_address = EntryPointToCodePointer(reinterpret_cast<void*>(entry_point));
 
     debug::MethodDebugInfo info = {};
-    DCHECK(info.trampoline_name.empty());
+    DCHECK(info.custom_name.empty());
     info.dex_file = &dex_file;
     info.class_def_index = class_def_index;
     info.dex_method_index = dex_method_index;
diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc
index 4d1c85a..0e295e2 100644
--- a/runtime/jit/debugger_interface.cc
+++ b/runtime/jit/debugger_interface.cc
@@ -42,6 +42,7 @@
     JITCodeEntry* prev_;
     const uint8_t *symfile_addr_;
     uint64_t symfile_size_;
+    uint32_t ref_count;  // ART internal field.
   };
 
   struct JITDescriptor {
@@ -69,10 +70,11 @@
   JITDescriptor __jit_debug_descriptor = { 1, JIT_NOACTION, nullptr, nullptr };
 }
 
-static Mutex g_jit_debug_mutex("JIT debug interface lock", kJitDebugInterfaceLock);
+Mutex g_jit_debug_mutex("JIT debug interface lock", kJitDebugInterfaceLock);
 
-static JITCodeEntry* CreateJITCodeEntryInternal(std::vector<uint8_t> symfile)
-    REQUIRES(g_jit_debug_mutex) {
+static size_t g_jit_debug_mem_usage = 0;
+
+JITCodeEntry* CreateJITCodeEntry(const std::vector<uint8_t>& symfile) {
   DCHECK_NE(symfile.size(), 0u);
 
   // Make a copy of the buffer. We want to shrink it anyway.
@@ -85,20 +87,20 @@
   entry->symfile_addr_ = symfile_copy;
   entry->symfile_size_ = symfile.size();
   entry->prev_ = nullptr;
-
+  entry->ref_count = 0;
   entry->next_ = __jit_debug_descriptor.first_entry_;
   if (entry->next_ != nullptr) {
     entry->next_->prev_ = entry;
   }
+  g_jit_debug_mem_usage += sizeof(JITCodeEntry) + entry->symfile_size_;
   __jit_debug_descriptor.first_entry_ = entry;
   __jit_debug_descriptor.relevant_entry_ = entry;
-
   __jit_debug_descriptor.action_flag_ = JIT_REGISTER_FN;
   (*__jit_debug_register_code_ptr)();
   return entry;
 }
 
-static void DeleteJITCodeEntryInternal(JITCodeEntry* entry) REQUIRES(g_jit_debug_mutex) {
+void DeleteJITCodeEntry(JITCodeEntry* entry) {
   if (entry->prev_ != nullptr) {
     entry->prev_->next_ = entry->next_;
   } else {
@@ -109,6 +111,7 @@
     entry->next_->prev_ = entry->prev_;
   }
 
+  g_jit_debug_mem_usage -= sizeof(JITCodeEntry) + entry->symfile_size_;
   __jit_debug_descriptor.relevant_entry_ = entry;
   __jit_debug_descriptor.action_flag_ = JIT_UNREGISTER_FN;
   (*__jit_debug_register_code_ptr)();
@@ -116,41 +119,33 @@
   delete entry;
 }
 
-JITCodeEntry* CreateJITCodeEntry(std::vector<uint8_t> symfile) {
-  Thread* self = Thread::Current();
-  MutexLock mu(self, g_jit_debug_mutex);
-  return CreateJITCodeEntryInternal(std::move(symfile));
+// Mapping from code address to entry. Used to manage life-time of the entries.
+static std::unordered_map<uintptr_t, JITCodeEntry*> g_jit_code_entries
+    GUARDED_BY(g_jit_debug_mutex);
+
+void IncrementJITCodeEntryRefcount(JITCodeEntry* entry, uintptr_t code_address) {
+  DCHECK(entry != nullptr);
+  DCHECK_EQ(g_jit_code_entries.count(code_address), 0u);
+  entry->ref_count++;
+  g_jit_code_entries.emplace(code_address, entry);
 }
 
-void DeleteJITCodeEntry(JITCodeEntry* entry) {
-  Thread* self = Thread::Current();
-  MutexLock mu(self, g_jit_debug_mutex);
-  DeleteJITCodeEntryInternal(entry);
-}
-
-// Mapping from address to entry.  It takes ownership of the entries
-// so that the user of the JIT interface does not have to store them.
-static std::unordered_map<uintptr_t, JITCodeEntry*> g_jit_code_entries;
-
-void CreateJITCodeEntryForAddress(uintptr_t address, std::vector<uint8_t> symfile) {
-  Thread* self = Thread::Current();
-  MutexLock mu(self, g_jit_debug_mutex);
-  DCHECK_NE(address, 0u);
-  DCHECK(g_jit_code_entries.find(address) == g_jit_code_entries.end());
-  JITCodeEntry* entry = CreateJITCodeEntryInternal(std::move(symfile));
-  g_jit_code_entries.emplace(address, entry);
-}
-
-bool DeleteJITCodeEntryForAddress(uintptr_t address) {
-  Thread* self = Thread::Current();
-  MutexLock mu(self, g_jit_debug_mutex);
-  const auto it = g_jit_code_entries.find(address);
-  if (it == g_jit_code_entries.end()) {
-    return false;
+void DecrementJITCodeEntryRefcount(JITCodeEntry* entry, uintptr_t code_address) {
+  DCHECK(entry != nullptr);
+  DCHECK(g_jit_code_entries[code_address] == entry);
+  if (--entry->ref_count == 0) {
+    DeleteJITCodeEntry(entry);
   }
-  DeleteJITCodeEntryInternal(it->second);
-  g_jit_code_entries.erase(it);
-  return true;
+  g_jit_code_entries.erase(code_address);
+}
+
+JITCodeEntry* GetJITCodeEntry(uintptr_t code_address) {
+  auto it = g_jit_code_entries.find(code_address);
+  return it == g_jit_code_entries.end() ? nullptr : it->second;
+}
+
+size_t GetJITCodeEntryMemUsage() {
+  return g_jit_debug_mem_usage + g_jit_code_entries.size() * 2 * sizeof(void*);
 }
 
 }  // namespace art
diff --git a/runtime/jit/debugger_interface.h b/runtime/jit/debugger_interface.h
index d9bf331..9aec988 100644
--- a/runtime/jit/debugger_interface.h
+++ b/runtime/jit/debugger_interface.h
@@ -21,28 +21,45 @@
 #include <memory>
 #include <vector>
 
+#include "base/array_ref.h"
+#include "base/mutex.h"
+
 namespace art {
 
 extern "C" {
   struct JITCodeEntry;
 }
 
+extern Mutex g_jit_debug_mutex;
+
 // Notify native debugger about new JITed code by passing in-memory ELF.
 // It takes ownership of the in-memory ELF file.
-JITCodeEntry* CreateJITCodeEntry(std::vector<uint8_t> symfile);
+JITCodeEntry* CreateJITCodeEntry(const std::vector<uint8_t>& symfile)
+    REQUIRES(g_jit_debug_mutex);
 
 // Notify native debugger that JITed code has been removed.
 // It also releases the associated in-memory ELF file.
-void DeleteJITCodeEntry(JITCodeEntry* entry);
+void DeleteJITCodeEntry(JITCodeEntry* entry)
+    REQUIRES(g_jit_debug_mutex);
 
-// Notify native debugger about new JITed code by passing in-memory ELF.
-// The address is used only to uniquely identify the entry.
-// It takes ownership of the in-memory ELF file.
-void CreateJITCodeEntryForAddress(uintptr_t address, std::vector<uint8_t> symfile);
+// Helper method to track life-time of JITCodeEntry.
+// It registers given code address as being described by the given entry.
+void IncrementJITCodeEntryRefcount(JITCodeEntry* entry, uintptr_t code_address)
+    REQUIRES(g_jit_debug_mutex);
 
-// Notify native debugger that JITed code has been removed.
-// Returns false if entry for the given address was not found.
-bool DeleteJITCodeEntryForAddress(uintptr_t address);
+// Helper method to track life-time of JITCodeEntry.
+// It de-registers given code address as being described by the given entry.
+void DecrementJITCodeEntryRefcount(JITCodeEntry* entry, uintptr_t code_address)
+    REQUIRES(g_jit_debug_mutex);
+
+// Find the registered JITCodeEntry for given code address.
+// There can be only one entry per address at any given time.
+JITCodeEntry* GetJITCodeEntry(uintptr_t code_address)
+    REQUIRES(g_jit_debug_mutex);
+
+// Returns approximate memory used by all JITCodeEntries.
+size_t GetJITCodeEntryMemUsage()
+    REQUIRES(g_jit_debug_mutex);
 
 }  // namespace art
 
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 659c55a..e41667a 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -549,7 +549,11 @@
   uintptr_t allocation = FromCodeToAllocation(code_ptr);
   // Notify native debugger that we are about to remove the code.
   // It does nothing if we are not using native debugger.
-  DeleteJITCodeEntryForAddress(reinterpret_cast<uintptr_t>(code_ptr));
+  MutexLock mu(Thread::Current(), g_jit_debug_mutex);
+  JITCodeEntry* entry = GetJITCodeEntry(reinterpret_cast<uintptr_t>(code_ptr));
+  if (entry != nullptr) {
+    DecrementJITCodeEntryRefcount(entry, reinterpret_cast<uintptr_t>(code_ptr));
+  }
   if (OatQuickMethodHeader::FromCodePointer(code_ptr)->IsOptimized()) {
     FreeData(GetRootTable(code_ptr));
   }  // else this is a JNI stub without any data.