diff options
author | 2015-12-18 15:04:48 +0000 | |
---|---|---|
committer | 2016-01-13 15:39:12 +0000 | |
commit | 5cc349f3dd578e974f78314c50b6a0267c23e591 (patch) | |
tree | 7d8bf706fd6aba6f298bfe212f75db0f66e94b81 | |
parent | a38e418fb2d9b817309c54b54ca85039907c2bbb (diff) |
Report DWARF debug information for JITed code.
Change-Id: Ia5b2133c54386932c76c22774cf3d2ae61e0925f
-rw-r--r-- | compiler/dwarf/method_debug_info.h | 4 | ||||
-rw-r--r-- | compiler/elf_builder.h | 12 | ||||
-rw-r--r-- | compiler/elf_writer_debug.cc | 66 | ||||
-rw-r--r-- | compiler/elf_writer_debug.h | 2 | ||||
-rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 38 | ||||
-rw-r--r-- | runtime/base/mutex.h | 1 | ||||
-rw-r--r-- | runtime/jit/debugger_interface.cc | 63 | ||||
-rw-r--r-- | runtime/jit/debugger_interface.h | 17 | ||||
-rw-r--r-- | runtime/jit/jit_code_cache.cc | 4 |
9 files changed, 184 insertions, 23 deletions
diff --git a/compiler/dwarf/method_debug_info.h b/compiler/dwarf/method_debug_info.h index a391e4d08a..e8ba9148e8 100644 --- a/compiler/dwarf/method_debug_info.h +++ b/compiler/dwarf/method_debug_info.h @@ -30,8 +30,8 @@ struct MethodDebugInfo { uint32_t access_flags_; const DexFile::CodeItem* code_item_; bool deduped_; - uint32_t low_pc_; - uint32_t high_pc_; + uintptr_t low_pc_; + uintptr_t high_pc_; CompiledMethod* compiled_method_; }; diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index bb07cc2913..a7461a5525 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -148,6 +148,12 @@ class ElfBuilder FINAL { } } + // Returns true if the section was written to disk. + // (Used to check whether we have .text when writing JIT debug info) + bool Exists() const { + return finished_; + } + // Get the location of this section in virtual memory. Elf_Addr GetAddress() const { CHECK(started_); @@ -247,16 +253,18 @@ class ElfBuilder FINAL { } // Buffer symbol for this section. It will be written later. + // If the symbol's section is null, it will be considered absolute (SHN_ABS). + // (we use this in JIT to reference code which is stored outside the debug ELF file) void Add(Elf_Word name, const Section* section, Elf_Addr addr, bool is_relative, Elf_Word size, uint8_t binding, uint8_t type, uint8_t other = 0) { - CHECK(section != nullptr); Elf_Sym sym = Elf_Sym(); sym.st_name = name; sym.st_value = addr + (is_relative ? section->GetAddress() : 0); sym.st_size = size; sym.st_other = other; - sym.st_shndx = section->GetSectionIndex(); + sym.st_shndx = (section != nullptr ? section->GetSectionIndex() + : static_cast<Elf_Word>(SHN_ABS)); sym.st_info = (binding << 4) + (type & 0xf); symbols_.push_back(sym); } diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc index 2bc8c89f73..315a8334b1 100644 --- a/compiler/elf_writer_debug.cc +++ b/compiler/elf_writer_debug.cc @@ -22,16 +22,17 @@ #include "base/casts.h" #include "base/stl_util.h" #include "compiled_method.h" -#include "driver/compiler_driver.h" #include "dex_file-inl.h" +#include "driver/compiler_driver.h" #include "dwarf/dedup_vector.h" #include "dwarf/headers.h" #include "dwarf/method_debug_info.h" #include "dwarf/register.h" #include "elf_builder.h" +#include "linker/vector_output_stream.h" #include "oat_writer.h" -#include "utils.h" #include "stack_map.h" +#include "utils.h" namespace art { namespace dwarf { @@ -234,7 +235,9 @@ void WriteCFISection(ElfBuilder<ElfTypes>* builder, { cfi_section->Start(); const bool is64bit = Is64BitInstructionSet(builder->GetIsa()); - const Elf_Addr text_address = builder->GetText()->GetAddress(); + const Elf_Addr text_address = builder->GetText()->Exists() + ? builder->GetText()->GetAddress() + : 0; const Elf_Addr cfi_address = cfi_section->GetAddress(); const Elf_Addr cie_address = cfi_address; Elf_Addr buffer_address = cfi_address; @@ -305,8 +308,8 @@ namespace { struct CompilationUnit { std::vector<const MethodDebugInfo*> methods_; size_t debug_line_offset_ = 0; - uint32_t low_pc_ = 0xFFFFFFFFU; - uint32_t high_pc_ = 0; + uintptr_t low_pc_ = std::numeric_limits<uintptr_t>::max(); + uintptr_t high_pc_ = 0; }; typedef std::vector<DexFile::LocalInfo> LocalInfos; @@ -439,14 +442,17 @@ class DebugInfoWriter { void Write(const CompilationUnit& compilation_unit) { CHECK(!compilation_unit.methods_.empty()); - const Elf_Addr text_address = owner_->builder_->GetText()->GetAddress(); + const Elf_Addr text_address = owner_->builder_->GetText()->Exists() + ? owner_->builder_->GetText()->GetAddress() + : 0; + const uintptr_t cu_size = compilation_unit.high_pc_ - compilation_unit.low_pc_; info_.StartTag(DW_TAG_compile_unit); info_.WriteStrp(DW_AT_producer, owner_->WriteString("Android dex2oat")); info_.WriteData1(DW_AT_language, DW_LANG_Java); info_.WriteStrp(DW_AT_comp_dir, owner_->WriteString("$JAVA_SRC_ROOT")); info_.WriteAddr(DW_AT_low_pc, text_address + compilation_unit.low_pc_); - info_.WriteUdata(DW_AT_high_pc, compilation_unit.high_pc_ - compilation_unit.low_pc_); + info_.WriteUdata(DW_AT_high_pc, dchecked_integral_cast<uint32_t>(cu_size)); info_.WriteSecOffset(DW_AT_stmt_list, compilation_unit.debug_line_offset_); const char* last_dex_class_desc = nullptr; @@ -476,7 +482,7 @@ class DebugInfoWriter { info_.StartTag(DW_TAG_subprogram); WriteName(dex->GetMethodName(dex_method)); info_.WriteAddr(DW_AT_low_pc, text_address + mi->low_pc_); - info_.WriteUdata(DW_AT_high_pc, mi->high_pc_ - mi->low_pc_); + info_.WriteUdata(DW_AT_high_pc, dchecked_integral_cast<uint32_t>(mi->high_pc_-mi->low_pc_)); uint8_t frame_base[] = { DW_OP_call_frame_cfa }; info_.WriteExprLoc(DW_AT_frame_base, &frame_base, sizeof(frame_base)); WriteLazyType(dex->GetReturnTypeDescriptor(dex_proto)); @@ -924,7 +930,9 @@ class DebugLineWriter { // Returns the number of bytes written. size_t WriteCompilationUnit(CompilationUnit& compilation_unit) { const bool is64bit = Is64BitInstructionSet(builder_->GetIsa()); - const Elf_Addr text_address = builder_->GetText()->GetAddress(); + const Elf_Addr text_address = builder_->GetText()->Exists() + ? builder_->GetText()->GetAddress() + : 0; compilation_unit.debug_line_offset_ = builder_->GetDebugLine()->GetSize(); @@ -1173,11 +1181,13 @@ void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, name += " [DEDUPED]"; } + const auto* text = builder->GetText()->Exists() ? builder->GetText() : nullptr; + const bool is_relative = (text != nullptr); uint32_t low_pc = info.low_pc_; // Add in code delta, e.g., thumb bit 0 for Thumb2 code. low_pc += info.compiled_method_->CodeDelta(); - symtab->Add(strtab->Write(name), builder->GetText(), low_pc, - true, info.high_pc_ - info.low_pc_, STB_GLOBAL, STT_FUNC); + symtab->Add(strtab->Write(name), text, low_pc, + is_relative, info.high_pc_ - info.low_pc_, STB_GLOBAL, STT_FUNC); // Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2 // instructions, so that disassembler tools can correctly disassemble. @@ -1185,8 +1195,8 @@ void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, // requires it to match function symbol. Just address 0 does not work. if (info.compiled_method_->GetInstructionSet() == kThumb2) { if (!generated_mapping_symbol || !kGenerateSingleArmMappingSymbol) { - symtab->Add(strtab->Write("$t"), builder->GetText(), info.low_pc_ & ~1, - true, 0, STB_LOCAL, STT_NOTYPE); + symtab->Add(strtab->Write("$t"), text, info.low_pc_ & ~1, + is_relative, 0, STB_LOCAL, STT_NOTYPE); generated_mapping_symbol = true; } } @@ -1214,6 +1224,36 @@ void WriteDebugInfo(ElfBuilder<ElfTypes>* builder, } } +template <typename ElfTypes> +static ArrayRef<const uint8_t> WriteDebugElfFileInternal( + const dwarf::MethodDebugInfo& method_info) { + const InstructionSet isa = method_info.compiled_method_->GetInstructionSet(); + std::vector<uint8_t> buffer; + buffer.reserve(KB); + VectorOutputStream out("Debug ELF file", &buffer); + std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out)); + builder->Start(); + WriteDebugInfo(builder.get(), + ArrayRef<const MethodDebugInfo>(&method_info, 1), + DW_DEBUG_FRAME_FORMAT); + builder->End(); + CHECK(builder->Good()); + // Make a copy of the buffer. We want to shrink it anyway. + uint8_t* result = new uint8_t[buffer.size()]; + CHECK(result != nullptr); + memcpy(result, buffer.data(), buffer.size()); + return ArrayRef<const uint8_t>(result, buffer.size()); +} + +ArrayRef<const uint8_t> WriteDebugElfFile(const dwarf::MethodDebugInfo& method_info) { + const InstructionSet isa = method_info.compiled_method_->GetInstructionSet(); + if (Is64BitInstructionSet(isa)) { + return WriteDebugElfFileInternal<ElfTypes64>(method_info); + } else { + return WriteDebugElfFileInternal<ElfTypes32>(method_info); + } +} + // Explicit instantiations template void WriteDebugInfo<ElfTypes32>( ElfBuilder<ElfTypes32>* builder, diff --git a/compiler/elf_writer_debug.h b/compiler/elf_writer_debug.h index 7ec0be185a..94e09d7ccf 100644 --- a/compiler/elf_writer_debug.h +++ b/compiler/elf_writer_debug.h @@ -30,6 +30,8 @@ void WriteDebugInfo(ElfBuilder<ElfTypes>* builder, const ArrayRef<const MethodDebugInfo>& method_infos, CFIFormat cfi_format); +ArrayRef<const uint8_t> WriteDebugElfFile(const dwarf::MethodDebugInfo& method_info); + } // namespace dwarf } // namespace art diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 3eb72744ee..82f1e84454 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -17,6 +17,7 @@ #include "optimizing_compiler.h" #include <fstream> +#include <memory> #include <stdint.h> #ifdef ART_ENABLE_CODEGEN_arm64 @@ -52,6 +53,8 @@ #include "driver/compiler_driver-inl.h" #include "driver/compiler_options.h" #include "driver/dex_compilation_unit.h" +#include "dwarf/method_debug_info.h" +#include "elf_writer_debug.h" #include "elf_writer_quick.h" #include "graph_checker.h" #include "graph_visualizer.h" @@ -60,6 +63,7 @@ #include "inliner.h" #include "instruction_simplifier.h" #include "intrinsics.h" +#include "jit/debugger_interface.h" #include "jit/jit_code_cache.h" #include "licm.h" #include "jni/quick/jni_compiler.h" @@ -68,6 +72,7 @@ #include "prepare_for_register_allocation.h" #include "reference_type_propagation.h" #include "register_allocator.h" +#include "oat_quick_method_header.h" #include "sharpening.h" #include "side_effects_analysis.h" #include "ssa_builder.h" @@ -968,6 +973,39 @@ bool OptimizingCompiler::JitCompile(Thread* self, return false; } + if (GetCompilerDriver()->GetCompilerOptions().GetGenerateDebugInfo()) { + const auto* method_header = reinterpret_cast<const OatQuickMethodHeader*>(code); + const uintptr_t code_address = reinterpret_cast<uintptr_t>(method_header->GetCode()); + CompiledMethod compiled_method( + GetCompilerDriver(), + codegen->GetInstructionSet(), + ArrayRef<const uint8_t>(code_allocator.GetMemory()), + codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(), + codegen->GetCoreSpillMask(), + codegen->GetFpuSpillMask(), + ArrayRef<const SrcMapElem>(), + ArrayRef<const uint8_t>(), // mapping_table. + ArrayRef<const uint8_t>(stack_map_data, stack_map_size), + ArrayRef<const uint8_t>(), // native_gc_map. + ArrayRef<const uint8_t>(*codegen->GetAssembler()->cfi().data()), + ArrayRef<const LinkerPatch>()); + dwarf::MethodDebugInfo method_debug_info { + dex_file, + class_def_idx, + method_idx, + access_flags, + code_item, + false, // deduped. + code_address, + code_address + code_allocator.GetSize(), + &compiled_method + }; + ArrayRef<const uint8_t> elf_file = dwarf::WriteDebugElfFile(method_debug_info); + CreateJITCodeEntryForAddress(code_address, + std::unique_ptr<const uint8_t[]>(elf_file.data()), + elf_file.size()); + } + return true; } diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 263f50de51..f674a6f3c8 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -75,6 +75,7 @@ enum LockLevel { kReferenceQueueWeakReferencesLock, kReferenceQueueClearedReferencesLock, kReferenceProcessorLock, + kJitDebugInterfaceLock, kJitCodeCacheLock, kAllocSpaceLock, kBumpPointerSpaceBlockLock, diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc index 3c2898b8ac..f08a1a9d90 100644 --- a/runtime/jit/debugger_interface.cc +++ b/runtime/jit/debugger_interface.cc @@ -16,6 +16,13 @@ #include "debugger_interface.h" +#include "base/logging.h" +#include "base/mutex.h" +#include "thread-inl.h" +#include "thread.h" + +#include <unordered_map> + namespace art { // ------------------------------------------------------------------- @@ -57,13 +64,19 @@ extern "C" { JITDescriptor __jit_debug_descriptor = { 1, JIT_NOACTION, nullptr, nullptr }; } -JITCodeEntry* CreateJITCodeEntry(const uint8_t *symfile_addr, uintptr_t symfile_size) { +static Mutex g_jit_debug_mutex("JIT debug interface lock", kJitDebugInterfaceLock); + +static JITCodeEntry* CreateJITCodeEntryInternal( + std::unique_ptr<const uint8_t[]> symfile_addr, + uintptr_t symfile_size) + REQUIRES(g_jit_debug_mutex) { + DCHECK(symfile_addr.get() != nullptr); + JITCodeEntry* entry = new JITCodeEntry; - entry->symfile_addr_ = symfile_addr; + entry->symfile_addr_ = symfile_addr.release(); entry->symfile_size_ = symfile_size; entry->prev_ = nullptr; - // TODO: Do we need a lock here? entry->next_ = __jit_debug_descriptor.first_entry_; if (entry->next_ != nullptr) { entry->next_->prev_ = entry; @@ -76,8 +89,7 @@ JITCodeEntry* CreateJITCodeEntry(const uint8_t *symfile_addr, uintptr_t symfile_ return entry; } -void DeleteJITCodeEntry(JITCodeEntry* entry) { - // TODO: Do we need a lock here? +static void DeleteJITCodeEntryInternal(JITCodeEntry* entry) REQUIRES(g_jit_debug_mutex) { if (entry->prev_ != nullptr) { entry->prev_->next_ = entry->next_; } else { @@ -91,7 +103,48 @@ void DeleteJITCodeEntry(JITCodeEntry* entry) { __jit_debug_descriptor.relevant_entry_ = entry; __jit_debug_descriptor.action_flag_ = JIT_UNREGISTER_FN; __jit_debug_register_code(); + delete[] entry->symfile_addr_; delete entry; } +JITCodeEntry* CreateJITCodeEntry(std::unique_ptr<const uint8_t[]> symfile_addr, + uintptr_t symfile_size) { + Thread* self = Thread::Current(); + MutexLock mu(self, g_jit_debug_mutex); + return CreateJITCodeEntryInternal(std::move(symfile_addr), symfile_size); +} + +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::unique_ptr<const uint8_t[]> symfile_addr, + uintptr_t symfile_size) { + 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_addr), symfile_size); + 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; + } + DeleteJITCodeEntryInternal(it->second); + g_jit_code_entries.erase(it); + return true; +} + } // namespace art diff --git a/runtime/jit/debugger_interface.h b/runtime/jit/debugger_interface.h index a784ef5990..74469a98d0 100644 --- a/runtime/jit/debugger_interface.h +++ b/runtime/jit/debugger_interface.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_JIT_DEBUGGER_INTERFACE_H_ #include <inttypes.h> +#include <memory> namespace art { @@ -26,11 +27,25 @@ extern "C" { } // Notify native debugger about new JITed code by passing in-memory ELF. -JITCodeEntry* CreateJITCodeEntry(const uint8_t *symfile_addr, uintptr_t symfile_size); +// It takes ownership of the in-memory ELF file. +JITCodeEntry* CreateJITCodeEntry(std::unique_ptr<const uint8_t[]> symfile_addr, + uintptr_t symfile_size); // Notify native debugger that JITed code has been removed. +// It also releases the associated in-memory ELF file. void DeleteJITCodeEntry(JITCodeEntry* entry); +// 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::unique_ptr<const uint8_t[]> symfile_addr, + uintptr_t symfile_size); + +// 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); + } // namespace art #endif // ART_RUNTIME_JIT_DEBUGGER_INTERFACE_H_ diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index c260ca4629..1ac57b1d84 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -21,6 +21,7 @@ #include "art_method-inl.h" #include "base/stl_util.h" #include "base/time_utils.h" +#include "debugger_interface.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/bitmap-inl.h" #include "jit/profiling_info.h" @@ -215,6 +216,9 @@ void JitCodeCache::FreeCode(const void* code_ptr, ArtMethod* method ATTRIBUTE_UN uintptr_t allocation = FromCodeToAllocation(code_ptr); const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); const uint8_t* data = method_header->GetNativeGcMap(); + // 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)); if (data != nullptr) { mspace_free(data_mspace_, const_cast<uint8_t*>(data)); } |