diff options
| author | 2018-12-19 18:45:35 +0000 | |
|---|---|---|
| committer | 2018-12-19 18:45:35 +0000 | |
| commit | 30a8c4b28c8199418dd0a844992604274a7bc9cc (patch) | |
| tree | 41c74f53ceb4720805d3e9cde8b9aa4ee88e9649 | |
| parent | 2b979929d701ae26b80be6aa42ea4781f141c1d6 (diff) | |
| parent | 0b21e41260bea6c078c18eba745d43e549230fd0 (diff) | |
Merge "Pack JIT mini-debug-infos together."
| -rw-r--r-- | compiler/debug/dwarf/headers.h | 28 | ||||
| -rw-r--r-- | compiler/debug/elf_debug_frame_writer.h | 3 | ||||
| -rw-r--r-- | compiler/debug/elf_debug_reader.h | 104 | ||||
| -rw-r--r-- | compiler/debug/elf_debug_writer.cc | 140 | ||||
| -rw-r--r-- | compiler/debug/elf_debug_writer.h | 7 | ||||
| -rw-r--r-- | compiler/jit/jit_compiler.cc | 5 | ||||
| -rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 14 | ||||
| -rw-r--r-- | runtime/jit/debugger_interface.cc | 138 | ||||
| -rw-r--r-- | runtime/jit/debugger_interface.h | 28 |
9 files changed, 422 insertions, 45 deletions
diff --git a/compiler/debug/dwarf/headers.h b/compiler/debug/dwarf/headers.h index 28f108423e..4a27178146 100644 --- a/compiler/debug/dwarf/headers.h +++ b/compiler/debug/dwarf/headers.h @@ -107,7 +107,9 @@ void WriteFDE(bool is64bit, } else { DCHECK(format == DW_DEBUG_FRAME_FORMAT); // Relocate code_address if it has absolute value. - patch_locations->push_back(buffer_address + buffer->size() - section_address); + if (patch_locations != nullptr) { + patch_locations->push_back(buffer_address + buffer->size() - section_address); + } } if (is64bit) { writer.PushUint64(code_address); @@ -122,6 +124,30 @@ void WriteFDE(bool is64bit, writer.UpdateUint32(fde_header_start, writer.data()->size() - fde_header_start - 4); } +// Read singe FDE entry from 'data' (which is advanced). +template<typename Addr> +bool ReadFDE(const uint8_t** data, Addr* addr, Addr* size, ArrayRef<const uint8_t>* opcodes) { + struct Header { + uint32_t length; + int32_t cie_pointer; + Addr addr; + Addr size; + uint8_t augmentaion; + uint8_t opcodes[]; + } PACKED(1); + const Header* header = reinterpret_cast<const Header*>(*data); + const size_t length = 4 + header->length; + *data += length; + if (header->cie_pointer == -1) { + return false; // Not an FDE entry. + } + DCHECK_EQ(header->cie_pointer, 0); // Expects single CIE. Assumes DW_DEBUG_FRAME_FORMAT. + *addr = header->addr; + *size = header->size; + *opcodes = ArrayRef<const uint8_t>(header->opcodes, length - offsetof(Header, opcodes)); + return true; +} + // Write compilation unit (CU) to .debug_info section. template<typename Vector> void WriteDebugInfoCU(uint32_t debug_abbrev_offset, diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h index 27b70c8caa..e0116c6f41 100644 --- a/compiler/debug/elf_debug_frame_writer.h +++ b/compiler/debug/elf_debug_frame_writer.h @@ -182,7 +182,7 @@ void WriteCFISection(linker::ElfBuilder<ElfTypes>* builder, std::vector<const MethodDebugInfo*> sorted_method_infos; sorted_method_infos.reserve(method_infos.size()); for (size_t i = 0; i < method_infos.size(); i++) { - if (!method_infos[i].cfi.empty() && !method_infos[i].deduped) { + if (!method_infos[i].deduped) { sorted_method_infos.push_back(&method_infos[i]); } } @@ -222,7 +222,6 @@ void WriteCFISection(linker::ElfBuilder<ElfTypes>* builder, buffer.clear(); for (const MethodDebugInfo* mi : sorted_method_infos) { DCHECK(!mi->deduped); - DCHECK(!mi->cfi.empty()); const Elf_Addr code_address = mi->code_address + (mi->is_code_address_text_relative ? builder->GetText()->GetAddress() : 0); if (format == dwarf::DW_EH_FRAME_FORMAT) { diff --git a/compiler/debug/elf_debug_reader.h b/compiler/debug/elf_debug_reader.h new file mode 100644 index 0000000000..91b1b3ea81 --- /dev/null +++ b/compiler/debug/elf_debug_reader.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DEBUG_ELF_DEBUG_READER_H_ +#define ART_COMPILER_DEBUG_ELF_DEBUG_READER_H_ + +#include "base/array_ref.h" +#include "debug/dwarf/headers.h" +#include "elf.h" +#include "xz_utils.h" + +namespace art { +namespace debug { + +// Trivial ELF file reader. +// +// It is the bare minimum needed to read mini-debug-info symbols for unwinding. +// We use it to merge JIT mini-debug-infos together or to prune them after GC. +// The consumed ELF file comes from ART JIT. +template <typename ElfTypes, typename VisitSym, typename VisitFde> +static void ReadElfSymbols(const uint8_t* elf, VisitSym visit_sym, VisitFde visit_fde) { + // Note that the input buffer might be misaligned. + typedef typename ElfTypes::Ehdr ALIGNED(1) Elf_Ehdr; + typedef typename ElfTypes::Shdr ALIGNED(1) Elf_Shdr; + typedef typename ElfTypes::Sym ALIGNED(1) Elf_Sym; + typedef typename ElfTypes::Addr ALIGNED(1) Elf_Addr; + + // Read and check the elf header. + const Elf_Ehdr* header = reinterpret_cast<const Elf_Ehdr*>(elf); + CHECK(header->checkMagic()); + + // Find sections that we are interested in. + const Elf_Shdr* sections = reinterpret_cast<const Elf_Shdr*>(elf + header->e_shoff); + const Elf_Shdr* strtab = nullptr; + const Elf_Shdr* symtab = nullptr; + const Elf_Shdr* debug_frame = nullptr; + const Elf_Shdr* gnu_debugdata = nullptr; + for (size_t i = 1 /* skip null section */; i < header->e_shnum; i++) { + const Elf_Shdr* section = sections + i; + const char* name = reinterpret_cast<const char*>( + elf + sections[header->e_shstrndx].sh_offset + section->sh_name); + if (strcmp(name, ".strtab") == 0) { + strtab = section; + } else if (strcmp(name, ".symtab") == 0) { + symtab = section; + } else if (strcmp(name, ".debug_frame") == 0) { + debug_frame = section; + } else if (strcmp(name, ".gnu_debugdata") == 0) { + gnu_debugdata = section; + } + } + + // Visit symbols. + if (symtab != nullptr && strtab != nullptr) { + const Elf_Sym* symbols = reinterpret_cast<const Elf_Sym*>(elf + symtab->sh_offset); + DCHECK_EQ(symtab->sh_entsize, sizeof(Elf_Sym)); + size_t count = symtab->sh_size / sizeof(Elf_Sym); + for (size_t i = 1 /* skip null symbol */; i < count; i++) { + Elf_Sym symbol = symbols[i]; + if (symbol.getBinding() != STB_LOCAL) { // Ignore local symbols (e.g. "$t"). + const uint8_t* name = elf + strtab->sh_offset + symbol.st_name; + visit_sym(symbol, reinterpret_cast<const char*>(name)); + } + } + } + + // Visit CFI (unwind) data. + if (debug_frame != nullptr) { + const uint8_t* data = elf + debug_frame->sh_offset; + const uint8_t* end = data + debug_frame->sh_size; + while (data < end) { + Elf_Addr addr, size; + ArrayRef<const uint8_t> opcodes; + if (dwarf::ReadFDE<Elf_Addr>(&data, &addr, &size, &opcodes)) { + visit_fde(addr, size, opcodes); + } + } + } + + // Process embedded compressed ELF file. + if (gnu_debugdata != nullptr) { + ArrayRef<const uint8_t> compressed(elf + gnu_debugdata->sh_offset, gnu_debugdata->sh_size); + std::vector<uint8_t> decompressed; + XzDecompress(compressed, &decompressed); + ReadElfSymbols<ElfTypes>(decompressed.data(), visit_sym, visit_fde); + } +} + +} // namespace debug +} // namespace art +#endif // ART_COMPILER_DEBUG_ELF_DEBUG_READER_H_ diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc index 1ecb1d8ed9..56d773f508 100644 --- a/compiler/debug/elf_debug_writer.cc +++ b/compiler/debug/elf_debug_writer.cc @@ -21,12 +21,14 @@ #include <vector> #include "base/array_ref.h" +#include "base/stl_util.h" #include "debug/dwarf/dwarf_constants.h" #include "debug/elf_compilation_unit.h" #include "debug/elf_debug_frame_writer.h" #include "debug/elf_debug_info_writer.h" #include "debug/elf_debug_line_writer.h" #include "debug/elf_debug_loc_writer.h" +#include "debug/elf_debug_reader.h" #include "debug/elf_symtab_writer.h" #include "debug/method_debug_info.h" #include "debug/xz_utils.h" @@ -203,9 +205,147 @@ std::vector<uint8_t> MakeElfFileForJIT( } builder->End(); CHECK(builder->Good()); + // Verify the ELF file by reading it back using the trivial reader. + if (kIsDebugBuild) { + using Elf_Sym = typename ElfTypes::Sym; + using Elf_Addr = typename ElfTypes::Addr; + size_t num_syms = 0; + size_t num_cfis = 0; + ReadElfSymbols<ElfTypes>( + buffer.data(), + [&](Elf_Sym sym, const char*) { + DCHECK_EQ(sym.st_value, method_info.code_address + CompiledMethod::CodeDelta(isa)); + DCHECK_EQ(sym.st_size, method_info.code_size); + num_syms++; + }, + [&](Elf_Addr addr, Elf_Addr size, ArrayRef<const uint8_t> opcodes) { + DCHECK_EQ(addr, method_info.code_address); + DCHECK_EQ(size, method_info.code_size); + DCHECK_GE(opcodes.size(), method_info.cfi.size()); + DCHECK_EQ(memcmp(opcodes.data(), method_info.cfi.data(), method_info.cfi.size()), 0); + num_cfis++; + }); + DCHECK_EQ(num_syms, 1u); + DCHECK_EQ(num_cfis, 1u); + } return buffer; } +// Combine several mini-debug-info ELF files into one, while filtering some symbols. +std::vector<uint8_t> PackElfFileForJIT( + InstructionSet isa, + const InstructionSetFeatures* features, + std::vector<const uint8_t*>& added_elf_files, + std::vector<const void*>& removed_symbols, + /*out*/ size_t* num_symbols) { + using ElfTypes = ElfRuntimeTypes; + using Elf_Addr = typename ElfTypes::Addr; + using Elf_Sym = typename ElfTypes::Sym; + CHECK_EQ(sizeof(Elf_Addr), static_cast<size_t>(GetInstructionSetPointerSize(isa))); + const bool is64bit = Is64BitInstructionSet(isa); + auto is_removed_symbol = [&removed_symbols](Elf_Addr addr) { + const void* code_ptr = reinterpret_cast<const void*>(addr); + return std::binary_search(removed_symbols.begin(), removed_symbols.end(), code_ptr); + }; + uint64_t min_address = std::numeric_limits<uint64_t>::max(); + uint64_t max_address = 0; + + // Produce the inner ELF file. + // It will contain the symbols (.symtab) and unwind information (.debug_frame). + std::vector<uint8_t> inner_elf_file; + { + inner_elf_file.reserve(1 * KB); // Approximate size of ELF file with a single symbol. + linker::VectorOutputStream out("Mini-debug-info ELF file for JIT", &inner_elf_file); + std::unique_ptr<linker::ElfBuilder<ElfTypes>> builder( + new linker::ElfBuilder<ElfTypes>(isa, features, &out)); + builder->Start(/*write_program_headers=*/ false); + auto* text = builder->GetText(); + auto* strtab = builder->GetStrTab(); + auto* symtab = builder->GetSymTab(); + auto* debug_frame = builder->GetDebugFrame(); + std::deque<Elf_Sym> symbols; + std::vector<uint8_t> debug_frame_buffer; + WriteCIE(isa, dwarf::DW_DEBUG_FRAME_FORMAT, &debug_frame_buffer); + + // Write symbols names. All other data is buffered. + strtab->Start(); + strtab->Write(""); // strtab should start with empty string. + for (const uint8_t* added_elf_file : added_elf_files) { + ReadElfSymbols<ElfTypes>( + added_elf_file, + [&](Elf_Sym sym, const char* name) { + if (is_removed_symbol(sym.st_value)) { + return; + } + sym.st_name = strtab->Write(name); + symbols.push_back(sym); + min_address = std::min<uint64_t>(min_address, sym.st_value); + max_address = std::max<uint64_t>(max_address, sym.st_value + sym.st_size); + }, + [&](Elf_Addr addr, Elf_Addr size, ArrayRef<const uint8_t> opcodes) { + if (is_removed_symbol(addr)) { + return; + } + WriteFDE(is64bit, + /*section_address=*/ 0, + /*cie_address=*/ 0, + addr, + size, + opcodes, + dwarf::DW_DEBUG_FRAME_FORMAT, + debug_frame_buffer.size(), + &debug_frame_buffer, + /*patch_locations=*/ nullptr); + }); + } + strtab->End(); + + // Create .text covering the code range. Needed for gdb to find the symbols. + if (max_address > min_address) { + text->AllocateVirtualMemory(min_address, max_address - min_address); + } + + // Add the symbols. + *num_symbols = symbols.size(); + for (; !symbols.empty(); symbols.pop_front()) { + symtab->Add(symbols.front(), text); + } + symtab->WriteCachedSection(); + + // Add the CFI/unwind section. + debug_frame->Start(); + debug_frame->WriteFully(debug_frame_buffer.data(), debug_frame_buffer.size()); + debug_frame->End(); + + builder->End(); + CHECK(builder->Good()); + } + + // Produce the outer ELF file. + // It contains only the inner ELF file compressed as .gnu_debugdata section. + // This extra wrapping is not necessary but the compression saves space. + std::vector<uint8_t> outer_elf_file; + { + std::vector<uint8_t> gnu_debugdata; + gnu_debugdata.reserve(inner_elf_file.size() / 4); + XzCompress(ArrayRef<const uint8_t>(inner_elf_file), &gnu_debugdata); + + outer_elf_file.reserve(KB + gnu_debugdata.size()); + linker::VectorOutputStream out("Mini-debug-info ELF file for JIT", &outer_elf_file); + std::unique_ptr<linker::ElfBuilder<ElfTypes>> builder( + new linker::ElfBuilder<ElfTypes>(isa, features, &out)); + builder->Start(/*write_program_headers=*/ false); + if (max_address > min_address) { + builder->GetText()->AllocateVirtualMemory(min_address, max_address - min_address); + } + builder->WriteSection(".gnu_debugdata", &gnu_debugdata); + builder->End(); + CHECK(builder->Good()); + } + + return outer_elf_file; +} + std::vector<uint8_t> WriteDebugElfFileForClasses( InstructionSet isa, const InstructionSetFeatures* features, diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h index 8ad0c4219a..85ab356b0c 100644 --- a/compiler/debug/elf_debug_writer.h +++ b/compiler/debug/elf_debug_writer.h @@ -56,6 +56,13 @@ std::vector<uint8_t> MakeElfFileForJIT( bool mini_debug_info, const MethodDebugInfo& method_info); +std::vector<uint8_t> PackElfFileForJIT( + InstructionSet isa, + const InstructionSetFeatures* features, + std::vector<const uint8_t*>& added_elf_files, + std::vector<const void*>& removed_symbols, + /*out*/ size_t* num_symbols); + std::vector<uint8_t> WriteDebugElfFileForClasses( InstructionSet isa, const InstructionSetFeatures* features, diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index e57bbfa1e1..93575d7c75 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -146,7 +146,10 @@ extern "C" void jit_types_loaded(void* handle, mirror::Class** types, size_t cou // (which would have been otherwise used as identifier to remove it later). AddNativeDebugInfoForJit(Thread::Current(), /*code_ptr=*/ nullptr, - elf_file); + elf_file, + debug::PackElfFileForJIT, + compiler_options.GetInstructionSet(), + compiler_options.GetInstructionSetFeatures()); } } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index c9b4d36bc4..4936a6d3c0 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -1456,8 +1456,8 @@ bool OptimizingCompiler::JitCompile(Thread* self, return true; } -void OptimizingCompiler::GenerateJitDebugInfo( - ArtMethod* method, const debug::MethodDebugInfo& info) { +void OptimizingCompiler::GenerateJitDebugInfo(ArtMethod* method ATTRIBUTE_UNUSED, + const debug::MethodDebugInfo& info) { const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); DCHECK(compiler_options.GenerateAnyDebugInfo()); @@ -1472,12 +1472,10 @@ void OptimizingCompiler::GenerateJitDebugInfo( info); 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(GetJitMiniDebugInfoMemUsage()); + elf_file, + debug::PackElfFileForJIT, + compiler_options.GetInstructionSet(), + compiler_options.GetInstructionSetFeatures()); } } // namespace art diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc index 853c0cab96..7aa6ddfb00 100644 --- a/runtime/jit/debugger_interface.cc +++ b/runtime/jit/debugger_interface.cc @@ -19,14 +19,17 @@ #include <android-base/logging.h> #include "base/array_ref.h" +#include "base/logging.h" #include "base/mutex.h" #include "base/time_utils.h" +#include "base/utils.h" #include "dex/dex_file.h" #include "thread-current-inl.h" #include "thread.h" #include <atomic> #include <cstddef> +#include <deque> #include <map> // @@ -77,6 +80,10 @@ // namespace art { + +static Mutex g_jit_debug_lock("JIT native debug entries", kNativeDebugInterfaceLock); +static Mutex g_dex_debug_lock("DEX native debug entries", kNativeDebugInterfaceLock); + extern "C" { enum JITAction { JIT_NOACTION = 0, @@ -127,14 +134,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 GUARDED_BY(*Locks::native_debug_interface_lock_) {}; + JITDescriptor __jit_debug_descriptor GUARDED_BY(g_jit_debug_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 GUARDED_BY(*Locks::native_debug_interface_lock_) {}; + JITDescriptor __dex_debug_descriptor GUARDED_BY(g_dex_debug_lock) {}; } // Mark the descriptor as "locked", so native tools know the data is being modified. @@ -157,8 +164,7 @@ static JITCodeEntry* CreateJITCodeEntryInternal( JITDescriptor& descriptor, void (*register_code_ptr)(), ArrayRef<const uint8_t> symfile, - bool copy_symfile) - REQUIRES(Locks::native_debug_interface_lock_) { + bool copy_symfile) { // 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()]; @@ -199,8 +205,7 @@ static void DeleteJITCodeEntryInternal( JITDescriptor& descriptor, void (*register_code_ptr)(), JITCodeEntry* entry, - bool free_symfile) - REQUIRES(Locks::native_debug_interface_lock_) { + bool free_symfile) { CHECK(entry != nullptr); const uint8_t* symfile = entry->symfile_addr_; @@ -238,11 +243,10 @@ static void DeleteJITCodeEntryInternal( } } -static std::map<const DexFile*, JITCodeEntry*> g_dex_debug_entries - GUARDED_BY(*Locks::native_debug_interface_lock_); +static std::map<const DexFile*, JITCodeEntry*> g_dex_debug_entries GUARDED_BY(g_dex_debug_lock); void AddNativeDebugInfoForDex(Thread* self, const DexFile* dexfile) { - MutexLock mu(self, *Locks::native_debug_interface_lock_); + MutexLock mu(self, g_dex_debug_lock); DCHECK(dexfile != nullptr); // This is just defensive check. The class linker should not register the dex file twice. if (g_dex_debug_entries.count(dexfile) == 0) { @@ -256,7 +260,7 @@ void AddNativeDebugInfoForDex(Thread* self, const DexFile* dexfile) { } void RemoveNativeDebugInfoForDex(Thread* self, const DexFile* dexfile) { - MutexLock mu(self, *Locks::native_debug_interface_lock_); + MutexLock mu(self, g_dex_debug_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. @@ -270,46 +274,134 @@ void RemoveNativeDebugInfoForDex(Thread* self, const DexFile* dexfile) { } // Mapping from handle to entry. Used to manage life-time of the entries. -static std::map<const void*, JITCodeEntry*> g_jit_debug_entries - GUARDED_BY(*Locks::native_debug_interface_lock_); +static std::map<const void*, JITCodeEntry*> g_jit_debug_entries GUARDED_BY(g_jit_debug_lock); + +// Number of entries added since last packing. Used to pack entries in bulk. +static size_t g_jit_num_unpacked_entries GUARDED_BY(g_jit_debug_lock) = 0; + +// We postpone removal so that it is done in bulk. +static std::deque<const void*> g_jit_removed_entries GUARDED_BY(g_jit_debug_lock); + +// Split the JIT code cache into groups of fixed size and create singe JITCodeEntry for each group. +// The start address of method's code determines which group it belongs to. The end is irrelevant. +// As a consequnce, newly added mini debug infos will be merged and old ones (GCed) will be pruned. +static void MaybePackJitMiniDebugInfo(PackElfFileForJITFunction pack, + InstructionSet isa, + const InstructionSetFeatures* features) + REQUIRES(g_jit_debug_lock) { + // Size of memory range covered by each JITCodeEntry. + // The number of methods per entry is variable (depending on how many fit in that range). + constexpr uint32_t kGroupSize = 64 * KB; + // Even if there are no removed entries, we want to pack new entries on regular basis. + constexpr uint32_t kPackFrequency = 64; + + std::deque<const void*>& removed_entries = g_jit_removed_entries; + std::sort(removed_entries.begin(), removed_entries.end()); + if (removed_entries.empty() && g_jit_num_unpacked_entries < kPackFrequency) { + return; // Nothing to do. + } + + std::vector<const uint8_t*> added_elf_files; + std::vector<const void*> removed_symbols; + auto added_it = g_jit_debug_entries.begin(); + auto removed_it = removed_entries.begin(); + while (added_it != g_jit_debug_entries.end()) { + // Collect all entries that have been added or removed within our memory range. + const void* group_ptr = AlignDown(added_it->first, kGroupSize); + added_elf_files.clear(); + auto added_begin = added_it; + while (added_it != g_jit_debug_entries.end() && + AlignDown(added_it->first, kGroupSize) == group_ptr) { + added_elf_files.push_back((added_it++)->second->symfile_addr_); + } + removed_symbols.clear(); + while (removed_it != removed_entries.end() && + AlignDown(*removed_it, kGroupSize) == group_ptr) { + removed_symbols.push_back(*(removed_it++)); + } + + // Create new singe JITCodeEntry that covers this memory range. + if (added_elf_files.size() == 1 && removed_symbols.size() == 0) { + continue; // Nothing changed in this memory range. + } + uint64_t start_time = MilliTime(); + size_t symbols; + std::vector<uint8_t> packed = pack(isa, features, added_elf_files, removed_symbols, &symbols); + VLOG(jit) + << "JIT mini-debug-info packed" + << " for " << group_ptr + << " in " << MilliTime() - start_time << "ms" + << " files=" << added_elf_files.size() + << " removed=" << removed_symbols.size() + << " symbols=" << symbols + << " size=" << PrettySize(packed.size()); + + // Replace the old entries with the new one (with their lifetime temporally overlapping). + JITCodeEntry* packed_entry = CreateJITCodeEntryInternal( + __jit_debug_descriptor, + __jit_debug_register_code_ptr, + ArrayRef<const uint8_t>(packed), + /*copy_symfile=*/ true); + for (auto it = added_begin; it != added_it; ++it) { + DeleteJITCodeEntryInternal(__jit_debug_descriptor, + __jit_debug_register_code_ptr, + /*entry=*/ it->second, + /*free_symfile=*/ true); + } + g_jit_debug_entries.erase(added_begin, added_it); + g_jit_debug_entries.emplace(group_ptr, packed_entry); + } + CHECK(added_it == g_jit_debug_entries.end()); + CHECK(removed_it == removed_entries.end()); + removed_entries.clear(); + g_jit_num_unpacked_entries = 0; +} void AddNativeDebugInfoForJit(Thread* self, const void* code_ptr, - const std::vector<uint8_t>& symfile) { - MutexLock mu(self, *Locks::native_debug_interface_lock_); + const std::vector<uint8_t>& symfile, + PackElfFileForJITFunction pack, + InstructionSet isa, + const InstructionSetFeatures* features) { + MutexLock mu(self, g_jit_debug_lock); DCHECK_NE(symfile.size(), 0u); + MaybePackJitMiniDebugInfo(pack, isa, features); + JITCodeEntry* entry = CreateJITCodeEntryInternal( __jit_debug_descriptor, __jit_debug_register_code_ptr, ArrayRef<const uint8_t>(symfile), /*copy_symfile=*/ true); + VLOG(jit) + << "JIT mini-debug-info added" + << " for " << code_ptr + << " size=" << PrettySize(symfile.size()); + // 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). 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; + // Count how many entries we have added since the last mini-debug-info packing. + // We avoid g_jit_debug_entries.size() here because it can shrink during packing. + g_jit_num_unpacked_entries++; } } 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); + MutexLock mu(self, g_jit_debug_lock); // 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 != g_jit_debug_entries.end()) { - DeleteJITCodeEntryInternal(__jit_debug_descriptor, - __jit_debug_register_code_ptr, - it->second, - /*free_symfile=*/ true); - g_jit_debug_entries.erase(it); + if (!g_jit_debug_entries.empty()) { + g_jit_removed_entries.push_back(code_ptr); } } size_t GetJitMiniDebugInfoMemUsage() { - MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_); + MutexLock mu(Thread::Current(), g_jit_debug_lock); size_t size = 0; for (auto entry : g_jit_debug_entries) { size += sizeof(JITCodeEntry) + entry.second->symfile_size_ + /*map entry*/ 4 * sizeof(void*); diff --git a/runtime/jit/debugger_interface.h b/runtime/jit/debugger_interface.h index 4b0d011295..17beb4baca 100644 --- a/runtime/jit/debugger_interface.h +++ b/runtime/jit/debugger_interface.h @@ -20,6 +20,7 @@ #include <inttypes.h> #include <vector> +#include "arch/instruction_set_features.h" #include "base/locks.h" namespace art { @@ -27,28 +28,35 @@ namespace art { class DexFile; class Thread; +// This method is declared in the compiler library. +// We need to pass it by pointer to be able to call it from runtime. +typedef std::vector<uint8_t> PackElfFileForJITFunction( + InstructionSet isa, + const InstructionSetFeatures* features, + std::vector<const uint8_t*>& added_elf_files, + std::vector<const void*>& removed_symbols, + /*out*/ size_t* num_symbols); + // Notify native tools (e.g. libunwind) that DEX file has been opened. -void AddNativeDebugInfoForDex(Thread* self, const DexFile* dexfile) - REQUIRES(!Locks::native_debug_interface_lock_); +void AddNativeDebugInfoForDex(Thread* self, const DexFile* dexfile); // Notify native tools (e.g. libunwind) that DEX file has been closed. -void RemoveNativeDebugInfoForDex(Thread* self, const DexFile* dexfile) - REQUIRES(!Locks::native_debug_interface_lock_); +void RemoveNativeDebugInfoForDex(Thread* self, const DexFile* dexfile); // 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(Thread* self, const void* code_ptr, - const std::vector<uint8_t>& symfile) - REQUIRES(!Locks::native_debug_interface_lock_); + const std::vector<uint8_t>& symfile, + PackElfFileForJITFunction pack, + InstructionSet isa, + const InstructionSetFeatures* features); // 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_); +void RemoveNativeDebugInfoForJit(Thread* self, const void* code_ptr); // Returns approximate memory used by debug info for JIT code. -size_t GetJitMiniDebugInfoMemUsage() - REQUIRES(!Locks::native_debug_interface_lock_); +size_t GetJitMiniDebugInfoMemUsage(); } // namespace art |