Add timestamps to JIT/DEX native debug info.
This a forward-looking change intended to allow simpleperf to
reliably correlate samples and native debug information.
I have added the timestamps to both JIT and DEX, and refactored
the code in the process to avoid code duplication.
Test: testrunner.py -t 137
Change-Id: I45fa4310305aff540e036db9af15a86c5b8b7aff
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 17b94d3..ac5c6fb 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -77,7 +77,9 @@
std::vector<uint8_t> elf_file = debug::WriteDebugElfFileForClasses(
kRuntimeISA, jit_compiler->GetCompilerDriver()->GetInstructionSetFeatures(), types_array);
MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_);
- CreateJITCodeEntry(std::move(elf_file));
+ // We never free debug info for types, so we don't need to provide a handle
+ // (which would have been otherwise used as identifier to remove it later).
+ AddNativeDebugInfoForJit(nullptr /* handle */, elf_file);
}
}
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index b3f23a0..e42dfc1 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -1411,13 +1411,12 @@
mini_debug_info,
ArrayRef<const debug::MethodDebugInfo>(&info, 1));
MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_);
- JITCodeEntry* entry = CreateJITCodeEntry(elf_file);
- IncrementJITCodeEntryRefcount(entry, info.code_address);
+ AddNativeDebugInfoForJit(reinterpret_cast<const void*>(info.code_address), elf_file);
VLOG(jit)
<< "JIT mini-debug-info added for " << ArtMethod::PrettyMethod(method)
<< " size=" << PrettySize(elf_file.size())
- << " total_size=" << PrettySize(GetJITCodeEntryMemUsage());
+ << " total_size=" << PrettySize(GetJitNativeDebugInfoMemUsage());
}
} // namespace art
diff --git a/runtime/Android.bp b/runtime/Android.bp
index e011c2e..4db9b74 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -18,7 +18,10 @@
// we use gold as the linker (arm, x86, x86_64). The symbol is used by the debuggers to detect when
// new jit code is generated. We don't want it to be called when a different function with the same
// (empty) body is called.
-JIT_DEBUG_REGISTER_CODE_LDFLAGS = ["-Wl,--keep-unique,__jit_debug_register_code"]
+JIT_DEBUG_REGISTER_CODE_LDFLAGS = [
+ "-Wl,--keep-unique,__jit_debug_register_code",
+ "-Wl,--keep-unique,__dex_debug_register_code"
+]
cc_defaults {
name: "libart_defaults",
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index a4c32dd..8a24daa 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -1218,6 +1218,10 @@
DCHECK(jni_function_table_lock_ == nullptr);
jni_function_table_lock_ = new Mutex("JNI function table lock", current_lock_level);
+ 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(kAbortLock);
DCHECK(abort_lock_ == nullptr);
abort_lock_ = new Mutex("abort lock", current_lock_level, true);
@@ -1230,10 +1234,6 @@
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 bf27b7f..4f7001a 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -58,11 +58,11 @@
// [1] http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163
enum LockLevel {
kLoggingLock = 0,
- kNativeDebugInterfaceLock,
kSwapMutexesLock,
kUnexpectedSignalLock,
kThreadSuspendCountLock,
kAbortLock,
+ kNativeDebugInterfaceLock,
kSignalHandlingLock,
kJdwpAdbStateLock,
kJdwpSocketLock,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index c4b1bf8..f7bd45a 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3433,7 +3433,8 @@
data.weak_root = dex_cache_jweak;
data.dex_file = dex_cache->GetDexFile();
data.class_table = ClassTableForClassLoader(class_loader);
- RegisterDexFileForNative(self, data.dex_file->Begin());
+ AddNativeDebugInfoForDex(self, ArrayRef<const uint8_t>(data.dex_file->Begin(),
+ data.dex_file->Size()));
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/elf_file_impl.h b/runtime/elf_file_impl.h
index 04c2243..3143df5 100644
--- a/runtime/elf_file_impl.h
+++ b/runtime/elf_file_impl.h
@@ -28,10 +28,6 @@
namespace art {
-extern "C" {
- struct JITCodeEntry;
-}
-
template <typename ElfTypes>
class ElfFileImpl {
public:
diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc
index d60f70a..505e626 100644
--- a/runtime/jit/debugger_interface.cc
+++ b/runtime/jit/debugger_interface.cc
@@ -18,18 +18,35 @@
#include <android-base/logging.h>
+#include "base/array_ref.h"
#include "base/mutex.h"
+#include "base/time_utils.h"
#include "thread-current-inl.h"
#include "thread.h"
#include <unordered_map>
-namespace art {
+//
+// Debug interface for native tools (gdb, lldb, libunwind, simpleperf).
+//
+// See http://sourceware.org/gdb/onlinedocs/gdb/Declarations.html
+//
+// There are two ways for native tools to access the debug data safely:
+//
+// 1) Synchronously, by setting a breakpoint in the __*_debug_register_code
+// method, which is called after every modification of the linked list.
+// GDB does this, but it is complex to set up and it stops the process.
+//
+// 2) Asynchronously, by monitoring the action_counter_, which is incremented
+// on every modification of the linked list and kept at -1 during updates.
+// Therefore, if the tool process reads the counter both before and after
+// iterating over the linked list, and the counters match and are not -1,
+// the tool process can be sure the list was not modified during the read.
+// Obviously, it can also cache the data and use the counter to determine
+// if the cache is up to date, or to intelligently update it if needed.
+//
-// -------------------------------------------------------------------
-// Binary GDB JIT Interface as described in
-// http://sourceware.org/gdb/onlinedocs/gdb/Declarations.html
-// -------------------------------------------------------------------
+namespace art {
extern "C" {
typedef enum {
JIT_NOACTION = 0,
@@ -40,168 +57,193 @@
struct JITCodeEntry {
JITCodeEntry* next_;
JITCodeEntry* prev_;
- const uint8_t *symfile_addr_;
- uint64_t symfile_size_;
- uint32_t ref_count; // ART internal field.
+ const uint8_t* symfile_addr_;
+ uint64_t symfile_size_; // Beware of the offset (12 on x86; but 16 on ARM32).
+
+ // Android-specific fields:
+ uint64_t register_timestamp_; // CLOCK_MONOTONIC time of entry registration.
};
struct JITDescriptor {
- uint32_t version_;
- uint32_t action_flag_;
- JITCodeEntry* relevant_entry_;
- JITCodeEntry* first_entry_;
+ uint32_t version_ = 1; // NB: GDB supports only version 1.
+ uint32_t action_flag_ = JIT_NOACTION; // One of the JITAction enum values.
+ JITCodeEntry* relevant_entry_ = nullptr; // The entry affected by the action.
+ JITCodeEntry* first_entry_ = nullptr; // Head of link list of all entries.
+
+ // Android-specific fields:
+ uint8_t magic_[8] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', '1'};
+ uint32_t flags_ = 0; // Reserved for future use. Must be 0.
+ uint32_t sizeof_descriptor = sizeof(JITDescriptor);
+ uint32_t sizeof_entry = sizeof(JITCodeEntry);
+ std::atomic_int32_t action_counter_; // Number of actions, or -1 if locked.
+ // It can overflow from INT32_MAX to 0.
+ uint64_t action_timestamp_ = 1; // CLOCK_MONOTONIC time of last action.
};
- // GDB will place breakpoint into this function.
- // To prevent GCC from inlining or removing it we place noinline attribute
- // and inline assembler statement inside.
- void __attribute__((noinline)) __jit_debug_register_code();
+ // Check that std::atomic_int32_t has the same layout as int32_t.
+ static_assert(alignof(std::atomic_int32_t) == alignof(int32_t), "Weird alignment");
+ static_assert(sizeof(std::atomic_int32_t) == sizeof(int32_t), "Weird size");
+
+ // GDB may set breakpoint here. We must ensure it is not removed or deduplicated.
void __attribute__((noinline)) __jit_debug_register_code() {
__asm__("");
}
- // Call __jit_debug_register_code indirectly via global variable.
- // This gives the debugger an easy way to inject custom code to handle the events.
+ // Alternatively, native tools may overwrite this field to execute custom handler.
void (*__jit_debug_register_code_ptr)() = __jit_debug_register_code;
- // GDB will inspect contents of this descriptor.
- // Static initialization is necessary to prevent GDB from seeing
- // uninitialized descriptor.
- JITDescriptor __jit_debug_descriptor = { 1, JIT_NOACTION, nullptr, nullptr };
+ // The root data structure describing of all JITed methods.
+ JITDescriptor __jit_debug_descriptor {};
- // 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;
-}
-
-static size_t g_jit_debug_mem_usage
- GUARDED_BY(Locks::native_debug_interface_lock_) = 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);
+ // The following globals mirror the ones above, but are used to register dex files.
+ void __attribute__((noinline)) __dex_debug_register_code() {
+ __asm__("");
}
+ void (*__dex_debug_register_code_ptr)() = __dex_debug_register_code;
+ JITDescriptor __dex_debug_descriptor {};
}
-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);
- }
+// Mark the descriptor as "locked", so native tools know the data is unstable.
+// Returns the old value of the counter.
+static int32_t LockActionCounter(JITDescriptor& descriptor) {
+ return descriptor.action_counter_.exchange(-1);
}
-JITCodeEntry* CreateJITCodeEntry(const std::vector<uint8_t>& symfile) {
- DCHECK_NE(symfile.size(), 0u);
+// Mark the descriptor as "unlocked", so native tools know the data is safe to read.
+// It will also increment the value so that the tools know the data has changed.
+static void UnlockActionCounter(JITDescriptor& descriptor, int32_t old_value) {
+ int32_t new_value = (old_value + 1) & 0x7FFFFFFF; // Handle overflow to avoid -1.
+ descriptor.action_counter_.store(new_value);
+}
- // Make a copy of the buffer. We want to shrink it anyway.
- uint8_t* symfile_copy = new uint8_t[symfile.size()];
- CHECK(symfile_copy != nullptr);
- memcpy(symfile_copy, symfile.data(), symfile.size());
+static JITCodeEntry* CreateJITCodeEntryInternal(
+ JITDescriptor& descriptor,
+ void (*register_code_ptr)(),
+ const ArrayRef<const uint8_t>& symfile)
+ REQUIRES(Locks::native_debug_interface_lock_) {
+ int32_t old_action_counter = LockActionCounter(descriptor);
JITCodeEntry* entry = new JITCodeEntry;
CHECK(entry != nullptr);
- entry->symfile_addr_ = symfile_copy;
+ entry->symfile_addr_ = symfile.data();
entry->symfile_size_ = symfile.size();
entry->prev_ = nullptr;
- entry->ref_count = 0;
- entry->next_ = __jit_debug_descriptor.first_entry_;
+ entry->next_ = descriptor.first_entry_;
+ entry->register_timestamp_ = NanoTime();
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_descriptor_timestamp++;
- (*__jit_debug_register_code_ptr)();
+ descriptor.first_entry_ = entry;
+ descriptor.relevant_entry_ = entry;
+ descriptor.action_flag_ = JIT_REGISTER_FN;
+ descriptor.action_timestamp_ = entry->register_timestamp_;
+ UnlockActionCounter(descriptor, old_action_counter);
+
+ (*register_code_ptr)();
return entry;
}
-void DeleteJITCodeEntry(JITCodeEntry* entry) {
+static void DeleteJITCodeEntryInternal(
+ JITDescriptor& descriptor,
+ void (*register_code_ptr)(),
+ JITCodeEntry* entry)
+ REQUIRES(Locks::native_debug_interface_lock_) {
+ CHECK(entry != nullptr);
+ int32_t old_action_counter = LockActionCounter(descriptor);
+
if (entry->prev_ != nullptr) {
entry->prev_->next_ = entry->next_;
} else {
- __jit_debug_descriptor.first_entry_ = entry->next_;
+ descriptor.first_entry_ = entry->next_;
}
-
if (entry->next_ != nullptr) {
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_descriptor_timestamp++;
- (*__jit_debug_register_code_ptr)();
- delete[] entry->symfile_addr_;
+ descriptor.relevant_entry_ = entry;
+ descriptor.action_flag_ = JIT_UNREGISTER_FN;
+ descriptor.action_timestamp_ = NanoTime();
+ UnlockActionCounter(descriptor, old_action_counter);
+
+ (*register_code_ptr)();
delete entry;
}
-// 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
+static std::unordered_map<const void*, JITCodeEntry*> __dex_debug_entries
GUARDED_BY(Locks::native_debug_interface_lock_);
-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 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);
+void AddNativeDebugInfoForDex(Thread* current_thread, ArrayRef<const uint8_t> dexfile) {
+ MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
+ DCHECK(dexfile.data() != nullptr);
+ // This is just defensive check. The class linker should not register the dex file twice.
+ if (__dex_debug_entries.count(dexfile.data()) == 0) {
+ JITCodeEntry* entry = CreateJITCodeEntryInternal(__dex_debug_descriptor,
+ __dex_debug_register_code_ptr,
+ dexfile);
+ __dex_debug_entries.emplace(dexfile.data(), entry);
}
- 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;
+void RemoveNativeDebugInfoForDex(Thread* current_thread, ArrayRef<const uint8_t> dexfile) {
+ MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
+ auto it = __dex_debug_entries.find(dexfile.data());
+ // We register dex files in the class linker and free them in DexFile_closeDexFile, but
+ // there might be cases where we load the dex file without using it in the class linker.
+ if (it != __dex_debug_entries.end()) {
+ DeleteJITCodeEntryInternal(__dex_debug_descriptor,
+ __dex_debug_register_code_ptr,
+ it->second);
+ __dex_debug_entries.erase(it);
+ }
}
-size_t GetJITCodeEntryMemUsage() {
- return g_jit_debug_mem_usage + g_jit_code_entries.size() * 2 * sizeof(void*);
+static size_t __jit_debug_mem_usage
+ GUARDED_BY(Locks::native_debug_interface_lock_) = 0;
+
+// Mapping from handle to entry. Used to manage life-time of the entries.
+static std::unordered_map<const void*, JITCodeEntry*> __jit_debug_entries
+ GUARDED_BY(Locks::native_debug_interface_lock_);
+
+void AddNativeDebugInfoForJit(const void* handle, const std::vector<uint8_t>& symfile) {
+ DCHECK_NE(symfile.size(), 0u);
+
+ // Make a copy of the buffer to shrink it and to pass ownership to JITCodeEntry.
+ uint8_t* copy = new uint8_t[symfile.size()];
+ CHECK(copy != nullptr);
+ memcpy(copy, symfile.data(), symfile.size());
+
+ JITCodeEntry* entry = CreateJITCodeEntryInternal(
+ __jit_debug_descriptor,
+ __jit_debug_register_code_ptr,
+ ArrayRef<const uint8_t>(copy, symfile.size()));
+ __jit_debug_mem_usage += sizeof(JITCodeEntry) + entry->symfile_size_;
+
+ // We don't provide handle for type debug info, which means we cannot free it later.
+ // (this only happens when --generate-debug-info flag is enabled for the purpose
+ // of being debugged with gdb; it does not happen for debuggable apps by default).
+ bool ok = handle == nullptr || __jit_debug_entries.emplace(handle, entry).second;
+ DCHECK(ok) << "Native debug entry already exists for " << std::hex << handle;
+}
+
+void RemoveNativeDebugInfoForJit(const void* handle) {
+ auto it = __jit_debug_entries.find(handle);
+ // We generate JIT native debug info only if the right runtime flags are enabled,
+ // but we try to remove it unconditionally whenever code is freed from JIT cache.
+ if (it != __jit_debug_entries.end()) {
+ JITCodeEntry* entry = it->second;
+ const uint8_t* symfile_addr = entry->symfile_addr_;
+ uint64_t symfile_size = entry->symfile_size_;
+ DeleteJITCodeEntryInternal(__jit_debug_descriptor,
+ __jit_debug_register_code_ptr,
+ entry);
+ __jit_debug_entries.erase(it);
+ __jit_debug_mem_usage -= sizeof(JITCodeEntry) + symfile_size;
+ delete[] symfile_addr;
+ }
+}
+
+size_t GetJitNativeDebugInfoMemUsage() {
+ return __jit_debug_mem_usage + __jit_debug_entries.size() * 2 * sizeof(void*);
}
} // namespace art
diff --git a/runtime/jit/debugger_interface.h b/runtime/jit/debugger_interface.h
index 8c4bb3f..3d25910 100644
--- a/runtime/jit/debugger_interface.h
+++ b/runtime/jit/debugger_interface.h
@@ -26,45 +26,26 @@
namespace art {
-extern "C" {
- struct JITCodeEntry;
-}
-
// 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);
+// It takes the lock itself. The parameter must point to dex data (not the DexFile* object).
+void AddNativeDebugInfoForDex(Thread* current_thread, ArrayRef<const uint8_t> dexfile);
// 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);
+// It takes the lock itself. The parameter must point to dex data (not the DexFile* object).
+void RemoveNativeDebugInfoForDex(Thread* current_thread, ArrayRef<const uint8_t> dexfile);
-// 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)
+// Notify native tools about new JITed code by passing in-memory ELF.
+// The handle is the object that is being described (needed to be able to remove the entry).
+// The method will make copy of the passed ELF file (to shrink it to the minimum size).
+void AddNativeDebugInfoForJit(const void* handle, const std::vector<uint8_t>& symfile)
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(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(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(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)
+// Notify native debugger that JITed code has been removed and free the debug info.
+void RemoveNativeDebugInfoForJit(const void* handle)
REQUIRES(Locks::native_debug_interface_lock_);
// Returns approximate memory used by all JITCodeEntries.
-size_t GetJITCodeEntryMemUsage()
+size_t GetJitNativeDebugInfoMemUsage()
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 c8c13cb..68a3647 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -550,10 +550,7 @@
// 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(), *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));
- }
+ RemoveNativeDebugInfoForJit(code_ptr);
if (OatQuickMethodHeader::FromCodePointer(code_ptr)->IsOptimized()) {
FreeData(GetRootTable(code_ptr));
} // else this is a JNI stub without any data.
@@ -1828,7 +1825,7 @@
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"
+ << "Current JIT mini-debug-info size: " << PrettySize(GetJitNativeDebugInfoMemUsage()) << "\n"
<< "Current JIT capacity: " << PrettySize(current_capacity_) << "\n"
<< "Current number of JIT JNI stub entries: " << jni_stubs_map_.size() << "\n"
<< "Current number of JIT code cache entries: " << method_code_map_.size() << "\n"
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index b49209c..b602602 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -332,7 +332,8 @@
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());
+ RemoveNativeDebugInfoForDex(soa.Self(), ArrayRef<const uint8_t>(dex_file->Begin(),
+ dex_file->Size()));
// 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)) {