diff options
| -rw-r--r-- | compiler/jit/jit_compiler.cc | 5 | ||||
| -rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 7 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 3 | ||||
| -rw-r--r-- | runtime/jit/debugger_interface.cc | 111 | ||||
| -rw-r--r-- | runtime/jit/debugger_interface.h | 38 | ||||
| -rw-r--r-- | runtime/jit/jit_code_cache.cc | 6 | ||||
| -rw-r--r-- | runtime/native/dalvik_system_DexFile.cc | 3 |
7 files changed, 95 insertions, 78 deletions
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index b9fd868f1c..9b8bb3e90e 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -142,10 +142,11 @@ extern "C" void jit_types_loaded(void* handle, mirror::Class** types, size_t cou const ArrayRef<mirror::Class*> types_array(types, count); std::vector<uint8_t> elf_file = debug::WriteDebugElfFileForClasses( kRuntimeISA, compiler_options.GetInstructionSetFeatures(), types_array); - MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_); // 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); + AddNativeDebugInfoForJit(Thread::Current(), + /*code_ptr=*/ nullptr, + elf_file); } } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 92aaa19121..c9b4d36bc4 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -1470,13 +1470,14 @@ void OptimizingCompiler::GenerateJitDebugInfo( compiler_options.GetInstructionSetFeatures(), mini_debug_info, info); - MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_); - AddNativeDebugInfoForJit(reinterpret_cast<const void*>(info.code_address), elf_file); + AddNativeDebugInfoForJit(Thread::Current(), + 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(GetJitNativeDebugInfoMemUsage()); + << " total_size=" << PrettySize(GetJitMiniDebugInfoMemUsage()); } } // namespace art diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index e31fe6315b..d33541c4d3 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3736,8 +3736,7 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, data.weak_root = dex_cache_jweak; data.dex_file = dex_cache->GetDexFile(); data.class_table = ClassTableForClassLoader(class_loader); - AddNativeDebugInfoForDex(self, ArrayRef<const uint8_t>(data.dex_file->Begin(), - data.dex_file->Size())); + AddNativeDebugInfoForDex(self, data.dex_file); 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 6cd719a55c..853c0cab96 100644 --- a/runtime/jit/debugger_interface.cc +++ b/runtime/jit/debugger_interface.cc @@ -21,12 +21,13 @@ #include "base/array_ref.h" #include "base/mutex.h" #include "base/time_utils.h" +#include "dex/dex_file.h" #include "thread-current-inl.h" #include "thread.h" #include <atomic> -#include <unordered_map> #include <cstddef> +#include <map> // // Debug interface for native tools (gdb, lldb, libunwind, simpleperf). @@ -126,14 +127,14 @@ extern "C" { void (*__jit_debug_register_code_ptr)() = __jit_debug_register_code; // The root data structure describing of all JITed methods. - JITDescriptor __jit_debug_descriptor {}; + JITDescriptor __jit_debug_descriptor GUARDED_BY(*Locks::native_debug_interface_lock_) {}; // 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 {}; + JITDescriptor __dex_debug_descriptor GUARDED_BY(*Locks::native_debug_interface_lock_) {}; } // Mark the descriptor as "locked", so native tools know the data is being modified. @@ -155,8 +156,17 @@ static void ActionSequnlock(JITDescriptor& descriptor) { static JITCodeEntry* CreateJITCodeEntryInternal( JITDescriptor& descriptor, void (*register_code_ptr)(), - const ArrayRef<const uint8_t>& symfile) + ArrayRef<const uint8_t> symfile, + bool copy_symfile) REQUIRES(Locks::native_debug_interface_lock_) { + // Make a copy of the buffer to shrink it and to pass ownership to JITCodeEntry. + if (copy_symfile) { + uint8_t* copy = new uint8_t[symfile.size()]; + CHECK(copy != nullptr); + memcpy(copy, symfile.data(), symfile.size()); + symfile = ArrayRef<const uint8_t>(copy, symfile.size()); + } + // Ensure the timestamp is monotonically increasing even in presence of low // granularity system timer. This ensures each entry has unique timestamp. uint64_t timestamp = std::max(descriptor.action_timestamp_ + 1, NanoTime()); @@ -188,9 +198,11 @@ static JITCodeEntry* CreateJITCodeEntryInternal( static void DeleteJITCodeEntryInternal( JITDescriptor& descriptor, void (*register_code_ptr)(), - JITCodeEntry* entry) + JITCodeEntry* entry, + bool free_symfile) REQUIRES(Locks::native_debug_interface_lock_) { CHECK(entry != nullptr); + const uint8_t* symfile = entry->symfile_addr_; // Ensure the timestamp is monotonically increasing even in presence of low // granularity system timer. This ensures each entry has unique timestamp. @@ -221,83 +233,88 @@ static void DeleteJITCodeEntryInternal( memset(entry, 0, sizeof(*entry)); delete entry; + if (free_symfile) { + delete[] symfile; + } } -static std::unordered_map<const void*, JITCodeEntry*> __dex_debug_entries - GUARDED_BY(Locks::native_debug_interface_lock_); +static std::map<const DexFile*, JITCodeEntry*> g_dex_debug_entries + GUARDED_BY(*Locks::native_debug_interface_lock_); -void AddNativeDebugInfoForDex(Thread* current_thread, ArrayRef<const uint8_t> dexfile) { - MutexLock mu(current_thread, *Locks::native_debug_interface_lock_); - DCHECK(dexfile.data() != nullptr); +void AddNativeDebugInfoForDex(Thread* self, const DexFile* dexfile) { + MutexLock mu(self, *Locks::native_debug_interface_lock_); + DCHECK(dexfile != nullptr); // This is just defensive check. The class linker should not register the dex file twice. - if (__dex_debug_entries.count(dexfile.data()) == 0) { + if (g_dex_debug_entries.count(dexfile) == 0) { + const ArrayRef<const uint8_t> symfile(dexfile->Begin(), dexfile->Size()); JITCodeEntry* entry = CreateJITCodeEntryInternal(__dex_debug_descriptor, __dex_debug_register_code_ptr, - dexfile); - __dex_debug_entries.emplace(dexfile.data(), entry); + symfile, + /*copy_symfile=*/ false); + g_dex_debug_entries.emplace(dexfile, entry); } } -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()); +void RemoveNativeDebugInfoForDex(Thread* self, const DexFile* dexfile) { + MutexLock mu(self, *Locks::native_debug_interface_lock_); + auto it = g_dex_debug_entries.find(dexfile); // 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()) { + if (it != g_dex_debug_entries.end()) { DeleteJITCodeEntryInternal(__dex_debug_descriptor, __dex_debug_register_code_ptr, - it->second); - __dex_debug_entries.erase(it); + /*entry=*/ it->second, + /*free_symfile=*/ false); + g_dex_debug_entries.erase(it); } } -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_); +static std::map<const void*, JITCodeEntry*> g_jit_debug_entries + GUARDED_BY(*Locks::native_debug_interface_lock_); -void AddNativeDebugInfoForJit(const void* handle, const std::vector<uint8_t>& symfile) { +void AddNativeDebugInfoForJit(Thread* self, + const void* code_ptr, + const std::vector<uint8_t>& symfile) { + MutexLock mu(self, *Locks::native_debug_interface_lock_); 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_; + ArrayRef<const uint8_t>(symfile), + /*copy_symfile=*/ true); - // We don't provide handle for type debug info, which means we cannot free it later. + // We don't provide code_ptr 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; + if (code_ptr != nullptr) { + bool ok = g_jit_debug_entries.emplace(code_ptr, entry).second; + DCHECK(ok) << "Native debug entry already exists for " << std::hex << code_ptr; + } } -void RemoveNativeDebugInfoForJit(const void* handle) { - auto it = __jit_debug_entries.find(handle); +void RemoveNativeDebugInfoForJit(Thread* self, const void* code_ptr) { + MutexLock mu(self, *Locks::native_debug_interface_lock_); + auto it = g_jit_debug_entries.find(code_ptr); // 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_; + if (it != g_jit_debug_entries.end()) { 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; + it->second, + /*free_symfile=*/ true); + g_jit_debug_entries.erase(it); } } -size_t GetJitNativeDebugInfoMemUsage() { - return __jit_debug_mem_usage + __jit_debug_entries.size() * 2 * sizeof(void*); +size_t GetJitMiniDebugInfoMemUsage() { + MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_); + size_t size = 0; + for (auto entry : g_jit_debug_entries) { + size += sizeof(JITCodeEntry) + entry.second->symfile_size_ + /*map entry*/ 4 * sizeof(void*); + } + return size; } } // namespace art diff --git a/runtime/jit/debugger_interface.h b/runtime/jit/debugger_interface.h index fb5e81b03c..4b0d011295 100644 --- a/runtime/jit/debugger_interface.h +++ b/runtime/jit/debugger_interface.h @@ -18,35 +18,37 @@ #define ART_RUNTIME_JIT_DEBUGGER_INTERFACE_H_ #include <inttypes.h> -#include <memory> #include <vector> -#include "base/array_ref.h" #include "base/locks.h" namespace art { +class DexFile; +class Thread; + // Notify native tools (e.g. libunwind) that DEX file has been opened. -// 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); +void AddNativeDebugInfoForDex(Thread* self, const DexFile* dexfile) + REQUIRES(!Locks::native_debug_interface_lock_); // Notify native tools (e.g. libunwind) that DEX file has been closed. -// 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); +void RemoveNativeDebugInfoForDex(Thread* self, const DexFile* dexfile) + REQUIRES(!Locks::native_debug_interface_lock_); -// 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). +// Notify native tools (e.g. libunwind) that JIT has compiled a new method. // 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 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 GetJitNativeDebugInfoMemUsage() - REQUIRES(Locks::native_debug_interface_lock_); +void AddNativeDebugInfoForJit(Thread* self, + const void* code_ptr, + const std::vector<uint8_t>& symfile) + REQUIRES(!Locks::native_debug_interface_lock_); + +// Notify native tools (e.g. libunwind) that JIT code has been garbage collected. +void RemoveNativeDebugInfoForJit(Thread* self, const void* code_ptr) + REQUIRES(!Locks::native_debug_interface_lock_); + +// Returns approximate memory used by debug info for JIT code. +size_t GetJitMiniDebugInfoMemUsage() + 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 749758a23d..d976fec6aa 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -765,8 +765,7 @@ void JitCodeCache::FreeCodeAndData(const void* code_ptr) { 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(), *Locks::native_debug_interface_lock_); - RemoveNativeDebugInfoForJit(code_ptr); + RemoveNativeDebugInfoForJit(Thread::Current(), code_ptr); if (OatQuickMethodHeader::FromCodePointer(code_ptr)->IsOptimized()) { FreeData(GetRootTable(code_ptr)); } // else this is a JNI stub without any data. @@ -2101,10 +2100,9 @@ void JitCodeCache::FreeData(uint8_t* data) { void JitCodeCache::Dump(std::ostream& os) { MutexLock mu(Thread::Current(), lock_); - 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(GetJitNativeDebugInfoMemUsage()) << "\n" + << "Current JIT mini-debug-info size: " << PrettySize(GetJitMiniDebugInfoMemUsage()) << "\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 203d200be3..1da91b089d 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -337,8 +337,7 @@ static jboolean DexFile_closeDexFile(JNIEnv* env, jclass, jobject cookie) { int32_t i = kDexFileIndexStart; // Oat file is at index 0. for (const DexFile* dex_file : dex_files) { if (dex_file != nullptr) { - RemoveNativeDebugInfoForDex(soa.Self(), ArrayRef<const uint8_t>(dex_file->Begin(), - dex_file->Size())); + RemoveNativeDebugInfoForDex(soa.Self(), dex_file); // 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)) { |