Create list of open dex files for libbacktrace.

This fixes unwinds after recent changes (oob apks; cdex data sharing).

Bug: 72520014
Test: m test-art-host-gtest
Change-Id: Ie2a02657b2afbe899acd2e61f0a57d207e688b99
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 2c62095..17b94d3 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -76,7 +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);
+    MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_);
     CreateJITCodeEntry(std::move(elf_file));
   }
 }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 47ef194..b3f23a0 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -1410,7 +1410,7 @@
       GetCompilerDriver()->GetInstructionSetFeatures(),
       mini_debug_info,
       ArrayRef<const debug::MethodDebugInfo>(&info, 1));
-  MutexLock mu(Thread::Current(), g_jit_debug_mutex);
+  MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_);
   JITCodeEntry* entry = CreateJITCodeEntry(elf_file);
   IncrementJITCodeEntryRefcount(entry, info.code_address);
 
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 9f17ad0..a4c32dd 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -74,6 +74,7 @@
 ReaderWriterMutex* Locks::jni_globals_lock_ = nullptr;
 Mutex* Locks::jni_weak_globals_lock_ = nullptr;
 ReaderWriterMutex* Locks::dex_lock_ = nullptr;
+Mutex* Locks::native_debug_interface_lock_ = nullptr;
 std::vector<BaseMutex*> Locks::expected_mutexes_on_weak_ref_access_;
 Atomic<const BaseMutex*> Locks::expected_mutexes_on_weak_ref_access_guard_;
 
@@ -1073,6 +1074,7 @@
     DCHECK(unexpected_signal_lock_ != nullptr);
     DCHECK(user_code_suspension_lock_ != nullptr);
     DCHECK(dex_lock_ != nullptr);
+    DCHECK(native_debug_interface_lock_ != nullptr);
   } else {
     // Create global locks in level order from highest lock level to lowest.
     LockLevel current_lock_level = kInstrumentEntrypointsLock;
@@ -1228,6 +1230,10 @@
     DCHECK(unexpected_signal_lock_ == nullptr);
     unexpected_signal_lock_ = new Mutex("unexpected signal lock", current_lock_level, true);
 
+    UPDATE_CURRENT_LOCK_LEVEL(kNativeDebugInterfaceLock);
+    DCHECK(native_debug_interface_lock_ == nullptr);
+    native_debug_interface_lock_ = new Mutex("Native debug interface lock", current_lock_level);
+
     UPDATE_CURRENT_LOCK_LEVEL(kLoggingLock);
     DCHECK(logging_lock_ == nullptr);
     logging_lock_ = new Mutex("logging lock", current_lock_level, true);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index d541b79..075122b 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -58,6 +58,7 @@
 // [1] http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163
 enum LockLevel {
   kLoggingLock = 0,
+  kNativeDebugInterfaceLock,
   kSwapMutexesLock,
   kUnexpectedSignalLock,
   kThreadSuspendCountLock,
@@ -745,8 +746,11 @@
   // One unexpected signal at a time lock.
   static Mutex* unexpected_signal_lock_ ACQUIRED_AFTER(thread_suspend_count_lock_);
 
+  // Guards the magic global variables used by native tools (e.g. libunwind).
+  static Mutex* native_debug_interface_lock_ ACQUIRED_AFTER(unexpected_signal_lock_);
+
   // Have an exclusive logging thread.
-  static Mutex* logging_lock_ ACQUIRED_AFTER(unexpected_signal_lock_);
+  static Mutex* logging_lock_ ACQUIRED_AFTER(native_debug_interface_lock_);
 
   // List of mutexes that we expect a thread may hold when accessing weak refs. This is used to
   // avoid a deadlock in the empty checkpoint while weak ref access is disabled (b/34964016). If we
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 32d3040..c9ebed2 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -73,6 +73,7 @@
 #include "intern_table.h"
 #include "interpreter/interpreter.h"
 #include "java_vm_ext.h"
+#include "jit/debugger_interface.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
 #include "jit/profile_compilation_info.h"
@@ -3432,6 +3433,7 @@
   data.weak_root = dex_cache_jweak;
   data.dex_file = dex_cache->GetDexFile();
   data.class_table = ClassTableForClassLoader(class_loader);
+  RegisterDexFileForNative(self, data.dex_file->Begin());
   DCHECK(data.class_table != nullptr);
   // Make sure to hold the dex cache live in the class table. This case happens for the boot class
   // path dex caches without an image.
diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc
index 0e295e2..d60f70a 100644
--- a/runtime/jit/debugger_interface.cc
+++ b/runtime/jit/debugger_interface.cc
@@ -68,11 +68,65 @@
   // Static initialization is necessary to prevent GDB from seeing
   // uninitialized descriptor.
   JITDescriptor __jit_debug_descriptor = { 1, JIT_NOACTION, nullptr, nullptr };
+
+  // Incremented whenever __jit_debug_descriptor is modified.
+  uint32_t __jit_debug_descriptor_timestamp = 0;
+
+  struct DEXFileEntry {
+    DEXFileEntry* next_;
+    DEXFileEntry* prev_;
+    const void* dexfile_;
+  };
+
+  DEXFileEntry* __art_debug_dexfiles = nullptr;
+
+  // Incremented whenever __art_debug_dexfiles is modified.
+  uint32_t __art_debug_dexfiles_timestamp = 0;
 }
 
-Mutex g_jit_debug_mutex("JIT debug interface lock", kJitDebugInterfaceLock);
+static size_t g_jit_debug_mem_usage
+    GUARDED_BY(Locks::native_debug_interface_lock_) = 0;
 
-static size_t g_jit_debug_mem_usage = 0;
+static std::unordered_map<const void*, DEXFileEntry*> g_dexfile_entries
+    GUARDED_BY(Locks::native_debug_interface_lock_);
+
+void RegisterDexFileForNative(Thread* current_thread, const void* dexfile_header) {
+  MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
+  if (g_dexfile_entries.count(dexfile_header) == 0) {
+    DEXFileEntry* entry = new DEXFileEntry();
+    CHECK(entry != nullptr);
+    entry->dexfile_ = dexfile_header;
+    entry->prev_ = nullptr;
+    entry->next_ = __art_debug_dexfiles;
+    if (entry->next_ != nullptr) {
+      entry->next_->prev_ = entry;
+    }
+    __art_debug_dexfiles = entry;
+    __art_debug_dexfiles_timestamp++;
+    g_dexfile_entries.emplace(dexfile_header, entry);
+  }
+}
+
+void DeregisterDexFileForNative(Thread* current_thread, const void* dexfile_header) {
+  MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
+  auto it = g_dexfile_entries.find(dexfile_header);
+  // We register dex files in the class linker and free them in DexFile_closeDexFile,
+  // but might be cases where we load the dex file without using it in the class linker.
+  if (it != g_dexfile_entries.end()) {
+    DEXFileEntry* entry = it->second;
+    if (entry->prev_ != nullptr) {
+      entry->prev_->next_ = entry->next_;
+    } else {
+      __art_debug_dexfiles = entry->next_;
+    }
+    if (entry->next_ != nullptr) {
+      entry->next_->prev_ = entry->prev_;
+    }
+    __art_debug_dexfiles_timestamp++;
+    delete entry;
+    g_dexfile_entries.erase(it);
+  }
+}
 
 JITCodeEntry* CreateJITCodeEntry(const std::vector<uint8_t>& symfile) {
   DCHECK_NE(symfile.size(), 0u);
@@ -96,6 +150,7 @@
   __jit_debug_descriptor.first_entry_ = entry;
   __jit_debug_descriptor.relevant_entry_ = entry;
   __jit_debug_descriptor.action_flag_ = JIT_REGISTER_FN;
+  __jit_debug_descriptor_timestamp++;
   (*__jit_debug_register_code_ptr)();
   return entry;
 }
@@ -114,6 +169,7 @@
   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_descriptor_timestamp++;
   (*__jit_debug_register_code_ptr)();
   delete[] entry->symfile_addr_;
   delete entry;
@@ -121,7 +177,7 @@
 
 // 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);
+    GUARDED_BY(Locks::native_debug_interface_lock_);
 
 void IncrementJITCodeEntryRefcount(JITCodeEntry* entry, uintptr_t code_address) {
   DCHECK(entry != nullptr);
diff --git a/runtime/jit/debugger_interface.h b/runtime/jit/debugger_interface.h
index 9aec988..8c4bb3f 100644
--- a/runtime/jit/debugger_interface.h
+++ b/runtime/jit/debugger_interface.h
@@ -30,36 +30,42 @@
   struct JITCodeEntry;
 }
 
-extern Mutex g_jit_debug_mutex;
+// Notify native tools (e.g. libunwind) that DEX file has been opened.
+// The pointer needs to point the start of the dex data (not the DexFile* object).
+void RegisterDexFileForNative(Thread* current_thread, const void* dexfile_header);
+
+// Notify native tools (e.g. libunwind) that DEX file has been closed.
+// The pointer needs to point the start of the dex data (not the DexFile* object).
+void DeregisterDexFileForNative(Thread* current_thread, const void* dexfile_header);
 
 // Notify native debugger about new JITed code by passing in-memory ELF.
 // It takes ownership of the in-memory ELF file.
 JITCodeEntry* CreateJITCodeEntry(const std::vector<uint8_t>& symfile)
-    REQUIRES(g_jit_debug_mutex);
+    REQUIRES(Locks::native_debug_interface_lock_);
 
 // Notify native debugger that JITed code has been removed.
 // It also releases the associated in-memory ELF file.
 void DeleteJITCodeEntry(JITCodeEntry* entry)
-    REQUIRES(g_jit_debug_mutex);
+    REQUIRES(Locks::native_debug_interface_lock_);
 
 // 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);
+    REQUIRES(Locks::native_debug_interface_lock_);
 
 // 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);
+    REQUIRES(Locks::native_debug_interface_lock_);
 
 // 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);
+    REQUIRES(Locks::native_debug_interface_lock_);
 
 // Returns approximate memory used by all JITCodeEntries.
 size_t GetJITCodeEntryMemUsage()
-    REQUIRES(g_jit_debug_mutex);
+    REQUIRES(Locks::native_debug_interface_lock_);
 
 }  // namespace art
 
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 7f04477..c8c13cb 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -549,7 +549,7 @@
   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.
-  MutexLock mu(Thread::Current(), g_jit_debug_mutex);
+  MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_);
   JITCodeEntry* entry = GetJITCodeEntry(reinterpret_cast<uintptr_t>(code_ptr));
   if (entry != nullptr) {
     DecrementJITCodeEntryRefcount(entry, reinterpret_cast<uintptr_t>(code_ptr));
@@ -1825,7 +1825,7 @@
 
 void JitCodeCache::Dump(std::ostream& os) {
   MutexLock mu(Thread::Current(), lock_);
-  MutexLock mu2(Thread::Current(), g_jit_debug_mutex);
+  MutexLock mu2(Thread::Current(), *Locks::native_debug_interface_lock_);
   os << "Current JIT code cache size: " << PrettySize(used_memory_for_code_) << "\n"
      << "Current JIT data cache size: " << PrettySize(used_memory_for_data_) << "\n"
      << "Current JIT mini-debug-info size: " << PrettySize(GetJITCodeEntryMemUsage()) << "\n"
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 0f43087..6ea9a7a 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -30,6 +30,7 @@
 #include "dex/art_dex_file_loader.h"
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_loader.h"
+#include "jit/debugger_interface.h"
 #include "jni_internal.h"
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
@@ -331,6 +332,7 @@
     int32_t i = kDexFileIndexStart;  // Oat file is at index 0.
     for (const DexFile* dex_file : dex_files) {
       if (dex_file != nullptr) {
+        DeregisterDexFileForNative(soa.Self(), dex_file->Begin());
         // Only delete the dex file if the dex cache is not found to prevent runtime crashes if there
         // are calls to DexFile.close while the ART DexFile is still in use.
         if (!class_linker->IsDexFileRegistered(soa.Self(), *dex_file)) {