diff options
author | David Srbecky <dsrbecky@google.com> | 2019-04-05 18:23:32 +0000 |
---|---|---|
committer | David Srbecky <dsrbecky@google.com> | 2019-04-12 12:55:50 +0000 |
commit | e5c4d62ba3fca09fed86ec605542b2be74a59851 (patch) | |
tree | da853107155df86b564454aad59b8ca9a5fcf598 | |
parent | 84ababefb7b7ec04027b741d7a6fe937cffee54b (diff) | |
download | system_core-e5c4d62ba3fca09fed86ec605542b2be74a59851.tar.gz system_core-e5c4d62ba3fca09fed86ec605542b2be74a59851.tar.bz2 system_core-e5c4d62ba3fca09fed86ec605542b2be74a59851.zip |
Revert "Check for data races when reading JIT/DEX entries."
This reverts commit 85b5fecec920208ec43b42488f08d4c2e5aaeda2.
Reason for revert: Breaks ART tests, reverting to investigate.
Exempt-From-Owner-Approval: Revert.
(cherry picked from commit b9cc4fbb268652744c812415cb2e5d1fbe04879a)
Bug: 130406806
Change-Id: I634e37060b97484d627fc544e3b406fd90aaa784
26 files changed, 659 insertions, 566 deletions
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp index 437450c05..82ba0a18e 100644 --- a/debuggerd/crash_dump.cpp +++ b/debuggerd/crash_dump.cpp @@ -48,6 +48,7 @@ #define ATRACE_TAG ATRACE_TAG_BIONIC #include <utils/Trace.h> +#include <unwindstack/DexFiles.h> #include <unwindstack/JitDebug.h> #include <unwindstack/Maps.h> #include <unwindstack/Memory.h> @@ -566,7 +567,7 @@ int main(int argc, char** argv) { // TODO: Use seccomp to lock ourselves down. unwindstack::UnwinderFromPid unwinder(256, vm_pid); - if (!unwinder.Init()) { + if (!unwinder.Init(unwindstack::Regs::CurrentArch())) { LOG(FATAL) << "Failed to init unwinder object."; } diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp index 5f7ebc34c..bbec612a8 100644 --- a/debuggerd/handler/debuggerd_fallback.cpp +++ b/debuggerd/handler/debuggerd_fallback.cpp @@ -42,6 +42,7 @@ #include <android-base/file.h> #include <android-base/unique_fd.h> #include <async_safe/log.h> +#include <unwindstack/DexFiles.h> #include <unwindstack/JitDebug.h> #include <unwindstack/Maps.h> #include <unwindstack/Memory.h> @@ -80,12 +81,12 @@ static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) { thread.pid = getpid(); thread.tid = gettid(); thread.thread_name = get_thread_name(gettid()); - thread.registers.reset( - unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext)); + unwindstack::ArchEnum arch = unwindstack::Regs::CurrentArch(); + thread.registers.reset(unwindstack::Regs::CreateFromUcontext(arch, ucontext)); // TODO: Create this once and store it in a global? unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid()); - if (unwinder.Init()) { + if (unwinder.Init(arch)) { dump_backtrace_thread(output_fd, &unwinder, thread); } else { async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Unable to init unwinder."); diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp index 4c1d63317..cc337ed30 100644 --- a/debuggerd/libdebuggerd/tombstone.cpp +++ b/debuggerd/libdebuggerd/tombstone.cpp @@ -45,6 +45,7 @@ #include <log/log.h> #include <log/logprint.h> #include <private/android_filesystem_config.h> +#include <unwindstack/DexFiles.h> #include <unwindstack/JitDebug.h> #include <unwindstack/Maps.h> #include <unwindstack/Memory.h> @@ -650,7 +651,7 @@ void engrave_tombstone_ucontext(int tombstone_fd, uint64_t abort_msg_address, si }; unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid); - if (!unwinder.Init()) { + if (!unwinder.Init(unwindstack::Regs::CurrentArch())) { LOG(FATAL) << "Failed to init unwinder object."; } diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp index ff19833a4..f5f9b2ada 100644 --- a/libbacktrace/UnwindStack.cpp +++ b/libbacktrace/UnwindStack.cpp @@ -32,6 +32,9 @@ #include <unwindstack/Regs.h> #include <unwindstack/RegsGetLocal.h> +#if !defined(NO_LIBDEXFILE_SUPPORT) +#include <unwindstack/DexFiles.h> +#endif #include <unwindstack/Unwinder.h> #include "BacktraceLog.h" @@ -47,6 +50,14 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map, regs, stack_map->process_memory()); unwinder.SetResolveNames(stack_map->ResolveNames()); stack_map->SetArch(regs->Arch()); + if (stack_map->GetJitDebug() != nullptr) { + unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch()); + } +#if !defined(NO_LIBDEXFILE_SUPPORT) + if (stack_map->GetDexFiles() != nullptr) { + unwinder.SetDexFiles(stack_map->GetDexFiles(), regs->Arch()); + } +#endif unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore()); if (error != nullptr) { switch (unwinder.LastErrorCode()) { diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp index 726fdfa04..4518891c3 100644 --- a/libbacktrace/UnwindStackMap.cpp +++ b/libbacktrace/UnwindStackMap.cpp @@ -43,6 +43,13 @@ bool UnwindStackMap::Build() { // Create the process memory object. process_memory_ = unwindstack::Memory::CreateProcessMemory(pid_); + // Create a JitDebug object for getting jit unwind information. + std::vector<std::string> search_libs_{"libart.so", "libartd.so"}; + jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_)); +#if !defined(NO_LIBDEXFILE_SUPPORT) + dex_files_.reset(new unwindstack::DexFiles(process_memory_, search_libs_)); +#endif + if (!stack_maps_->Parse()) { return false; } diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h index 9bb970915..e19b60565 100644 --- a/libbacktrace/UnwindStackMap.h +++ b/libbacktrace/UnwindStackMap.h @@ -27,6 +27,9 @@ #include <backtrace/Backtrace.h> #include <backtrace/BacktraceMap.h> +#if !defined(NO_LIBDEXFILE_SUPPORT) +#include <unwindstack/DexFiles.h> +#endif #include <unwindstack/Elf.h> #include <unwindstack/JitDebug.h> #include <unwindstack/Maps.h> @@ -50,6 +53,12 @@ class UnwindStackMap : public BacktraceMap { const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; } + unwindstack::JitDebug* GetJitDebug() { return jit_debug_.get(); } + +#if !defined(NO_LIBDEXFILE_SUPPORT) + unwindstack::DexFiles* GetDexFiles() { return dex_files_.get(); } +#endif + void SetArch(unwindstack::ArchEnum arch) { arch_ = arch; } protected: @@ -57,6 +66,11 @@ class UnwindStackMap : public BacktraceMap { std::unique_ptr<unwindstack::Maps> stack_maps_; std::shared_ptr<unwindstack::Memory> process_memory_; + std::unique_ptr<unwindstack::JitDebug> jit_debug_; +#if !defined(NO_LIBDEXFILE_SUPPORT) + std::unique_ptr<unwindstack::DexFiles> dex_files_; +#endif + unwindstack::ArchEnum arch_ = unwindstack::ARCH_UNKNOWN; }; diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index ce2510821..b7650a178 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -49,6 +49,7 @@ cc_library { srcs: [ "ArmExidx.cpp", "DexFile.cpp", + "DexFiles.cpp", "DwarfCfa.cpp", "DwarfEhFrameWithHdr.cpp", "DwarfMemory.cpp", @@ -91,6 +92,7 @@ cc_library { cflags: ["-DNO_LIBDEXFILE_SUPPORT"], exclude_srcs: [ "DexFile.cpp", + "DexFiles.cpp", ], exclude_shared_libs: [ "libdexfile_support", @@ -100,6 +102,7 @@ cc_library { cflags: ["-DNO_LIBDEXFILE_SUPPORT"], exclude_srcs: [ "DexFile.cpp", + "DexFiles.cpp", ], exclude_shared_libs: [ "libdexfile_support", diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp index d8d5a24de..eaf867fd0 100644 --- a/libunwindstack/DexFile.cpp +++ b/libunwindstack/DexFile.cpp @@ -35,31 +35,22 @@ namespace unwindstack { std::unique_ptr<DexFile> DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info) { if (!info->name.empty()) { - std::unique_ptr<DexFile> dex_file_from_file = + std::unique_ptr<DexFile> dex_file = DexFileFromFile::Create(dex_file_offset_in_memory - info->start + info->offset, info->name); - if (dex_file_from_file) { - dex_file_from_file->addr_ = dex_file_offset_in_memory; - return dex_file_from_file; + if (dex_file) { + return dex_file; } } - std::unique_ptr<DexFile> dex_file_from_memory = - DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name); - if (dex_file_from_memory) { - dex_file_from_memory->addr_ = dex_file_offset_in_memory; - return dex_file_from_memory; - } - return nullptr; + return DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name); } -bool DexFile::GetFunctionName(uint64_t dex_pc, std::string* method_name, uint64_t* method_offset) { - uint64_t dex_offset = dex_pc - addr_; +bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name, + uint64_t* method_offset) { art_api::dex::MethodInfo method_info = GetMethodInfoForOffset(dex_offset, false); if (method_info.offset == 0) { return false; } - if (method_name != nullptr) { - *method_name = method_info.name; - } + *method_name = method_info.name; *method_offset = dex_offset - method_info.offset; return true; } diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h index 1448a4b56..ca658e688 100644 --- a/libunwindstack/DexFile.h +++ b/libunwindstack/DexFile.h @@ -29,22 +29,17 @@ namespace unwindstack { -class Memory; -struct MapInfo; - class DexFile : protected art_api::dex::DexFile { public: virtual ~DexFile() = default; - bool GetFunctionName(uint64_t dex_pc, std::string* method_name, uint64_t* method_offset); + bool GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset); static std::unique_ptr<DexFile> Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info); protected: DexFile(art_api::dex::DexFile&& art_dex_file) : art_api::dex::DexFile(std::move(art_dex_file)) {} - - uint64_t addr_ = 0; }; class DexFileFromFile : public DexFile { diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp new file mode 100644 index 000000000..63a77e50f --- /dev/null +++ b/libunwindstack/DexFiles.cpp @@ -0,0 +1,179 @@ +/* + * 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. + */ + +#include <stdint.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <memory> + +#include <unwindstack/DexFiles.h> +#include <unwindstack/MapInfo.h> +#include <unwindstack/Maps.h> +#include <unwindstack/Memory.h> + +#include "DexFile.h" + +namespace unwindstack { + +struct DEXFileEntry32 { + uint32_t next; + uint32_t prev; + uint32_t dex_file; +}; + +struct DEXFileEntry64 { + uint64_t next; + uint64_t prev; + uint64_t dex_file; +}; + +DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : Global(memory) {} + +DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs) + : Global(memory, search_libs) {} + +DexFiles::~DexFiles() {} + +void DexFiles::ProcessArch() { + switch (arch()) { + case ARCH_ARM: + case ARCH_MIPS: + case ARCH_X86: + read_entry_ptr_func_ = &DexFiles::ReadEntryPtr32; + read_entry_func_ = &DexFiles::ReadEntry32; + break; + + case ARCH_ARM64: + case ARCH_MIPS64: + case ARCH_X86_64: + read_entry_ptr_func_ = &DexFiles::ReadEntryPtr64; + read_entry_func_ = &DexFiles::ReadEntry64; + break; + + case ARCH_UNKNOWN: + abort(); + } +} + +uint64_t DexFiles::ReadEntryPtr32(uint64_t addr) { + uint32_t entry; + const uint32_t field_offset = 12; // offset of first_entry_ in the descriptor struct. + if (!memory_->ReadFully(addr + field_offset, &entry, sizeof(entry))) { + return 0; + } + return entry; +} + +uint64_t DexFiles::ReadEntryPtr64(uint64_t addr) { + uint64_t entry; + const uint32_t field_offset = 16; // offset of first_entry_ in the descriptor struct. + if (!memory_->ReadFully(addr + field_offset, &entry, sizeof(entry))) { + return 0; + } + return entry; +} + +bool DexFiles::ReadEntry32() { + DEXFileEntry32 entry; + if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) { + entry_addr_ = 0; + return false; + } + + addrs_.push_back(entry.dex_file); + entry_addr_ = entry.next; + return true; +} + +bool DexFiles::ReadEntry64() { + DEXFileEntry64 entry; + if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) { + entry_addr_ = 0; + return false; + } + + addrs_.push_back(entry.dex_file); + entry_addr_ = entry.next; + return true; +} + +bool DexFiles::ReadVariableData(uint64_t ptr_offset) { + entry_addr_ = (this->*read_entry_ptr_func_)(ptr_offset); + return entry_addr_ != 0; +} + +void DexFiles::Init(Maps* maps) { + if (initialized_) { + return; + } + initialized_ = true; + entry_addr_ = 0; + + FindAndReadVariable(maps, "__dex_debug_descriptor"); +} + +DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) { + // Lock while processing the data. + DexFile* dex_file; + auto entry = files_.find(dex_file_offset); + if (entry == files_.end()) { + std::unique_ptr<DexFile> new_dex_file = DexFile::Create(dex_file_offset, memory_.get(), info); + dex_file = new_dex_file.get(); + files_[dex_file_offset] = std::move(new_dex_file); + } else { + dex_file = entry->second.get(); + } + return dex_file; +} + +bool DexFiles::GetAddr(size_t index, uint64_t* addr) { + if (index < addrs_.size()) { + *addr = addrs_[index]; + return true; + } + if (entry_addr_ != 0 && (this->*read_entry_func_)()) { + *addr = addrs_.back(); + return true; + } + return false; +} + +void DexFiles::GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc, + std::string* method_name, uint64_t* method_offset) { + std::lock_guard<std::mutex> guard(lock_); + if (!initialized_) { + Init(maps); + } + + size_t index = 0; + uint64_t addr; + while (GetAddr(index++, &addr)) { + if (addr < info->start || addr >= info->end) { + continue; + } + + DexFile* dex_file = GetDexFile(addr, info); + if (dex_file != nullptr && + dex_file->GetMethodInformation(dex_pc - addr, method_name, method_offset)) { + break; + } + } +} + +} // namespace unwindstack diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp index 5b402ed13..4b93abb43 100644 --- a/libunwindstack/Elf.cpp +++ b/libunwindstack/Elf.cpp @@ -243,24 +243,6 @@ bool Elf::IsValidPc(uint64_t pc) { return false; } -bool Elf::GetTextRange(uint64_t* addr, uint64_t* size) { - if (!valid_) { - return false; - } - - if (interface_->GetTextRange(addr, size)) { - *addr += load_bias_; - return true; - } - - if (gnu_debugdata_interface_ != nullptr && gnu_debugdata_interface_->GetTextRange(addr, size)) { - *addr += load_bias_; - return true; - } - - return false; -} - ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) { if (!IsValidElf(memory)) { return nullptr; diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp index 32c637f85..dee8eb36a 100644 --- a/libunwindstack/ElfInterface.cpp +++ b/libunwindstack/ElfInterface.cpp @@ -69,15 +69,6 @@ bool ElfInterface::IsValidPc(uint64_t pc) { return false; } -bool ElfInterface::GetTextRange(uint64_t* addr, uint64_t* size) { - if (text_size_ != 0) { - *addr = text_addr_; - *size = text_size_; - return true; - } - return false; -} - Memory* ElfInterface::CreateGnuDebugdataMemory() { if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) { return nullptr; @@ -339,26 +330,29 @@ void ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) { } symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize, str_shdr.sh_offset, str_shdr.sh_size)); - } else if (shdr.sh_type == SHT_PROGBITS || shdr.sh_type == SHT_NOBITS) { + } else if (shdr.sh_type == SHT_PROGBITS && sec_size != 0) { // Look for the .debug_frame and .gnu_debugdata. if (shdr.sh_name < sec_size) { std::string name; if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) { + uint64_t* offset_ptr = nullptr; + uint64_t* size_ptr = nullptr; if (name == ".debug_frame") { - debug_frame_offset_ = shdr.sh_offset; - debug_frame_size_ = shdr.sh_size; + offset_ptr = &debug_frame_offset_; + size_ptr = &debug_frame_size_; } else if (name == ".gnu_debugdata") { - gnu_debugdata_offset_ = shdr.sh_offset; - gnu_debugdata_size_ = shdr.sh_size; + offset_ptr = &gnu_debugdata_offset_; + size_ptr = &gnu_debugdata_size_; } else if (name == ".eh_frame") { - eh_frame_offset_ = shdr.sh_offset; - eh_frame_size_ = shdr.sh_size; + offset_ptr = &eh_frame_offset_; + size_ptr = &eh_frame_size_; } else if (eh_frame_hdr_offset_ == 0 && name == ".eh_frame_hdr") { - eh_frame_hdr_offset_ = shdr.sh_offset; - eh_frame_hdr_size_ = shdr.sh_size; - } else if (name == ".text") { - text_addr_ = shdr.sh_addr; - text_size_ = shdr.sh_size; + offset_ptr = &eh_frame_hdr_offset_; + size_ptr = &eh_frame_hdr_size_; + } + if (offset_ptr != nullptr) { + *offset_ptr = shdr.sh_offset; + *size_ptr = shdr.sh_size; } } } diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp index 71665a135..20bc4b902 100644 --- a/libunwindstack/JitDebug.cpp +++ b/libunwindstack/JitDebug.cpp @@ -16,13 +16,8 @@ #include <stdint.h> #include <sys/mman.h> -#include <cstddef> -#include <atomic> -#include <deque> -#include <map> #include <memory> -#include <unordered_set> #include <vector> #include <unwindstack/Elf.h> @@ -30,334 +25,197 @@ #include <unwindstack/Maps.h> #include <unwindstack/Memory.h> -#if !defined(NO_LIBDEXFILE_SUPPORT) -#include <DexFile.h> -#endif - // This implements the JIT Compilation Interface. // See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html namespace unwindstack { -// 32-bit platforms may differ in alignment of uint64_t. -struct Uint64_P { - uint64_t value; +struct JITCodeEntry32Pack { + uint32_t next; + uint32_t prev; + uint32_t symfile_addr; + uint64_t symfile_size; } __attribute__((packed)); -struct Uint64_A { - uint64_t value; -} __attribute__((aligned(8))); - -// Wrapper around other memory object which protects us against data races. -// It will check seqlock after every read, and fail if the seqlock changed. -// This ensues that the read memory has not been partially modified. -struct JitMemory : public Memory { - size_t Read(uint64_t addr, void* dst, size_t size) override; - - Memory* parent_ = nullptr; - uint64_t seqlock_addr_ = 0; - uint32_t expected_seqlock_ = 0; - bool failed_due_to_race_ = false; -}; -template <typename Symfile> -struct JitCacheEntry { - // PC memory range described by this entry. - uint64_t addr_ = 0; - uint64_t size_ = 0; - std::unique_ptr<Symfile> symfile_; +struct JITCodeEntry32Pad { + uint32_t next; + uint32_t prev; + uint32_t symfile_addr; + uint32_t pad; + uint64_t symfile_size; +}; - bool Init(Maps* maps, JitMemory* memory, uint64_t addr, uint64_t size); +struct JITCodeEntry64 { + uint64_t next; + uint64_t prev; + uint64_t symfile_addr; + uint64_t symfile_size; }; -template <typename Symfile, typename PointerT, typename Uint64_T> -class JitDebugImpl : public JitDebug<Symfile>, public Global { - public: - static constexpr const char* kDescriptorExtMagic = "Android1"; - static constexpr int kMaxRaceRetries = 16; - - struct JITCodeEntry { - PointerT next; - PointerT prev; - PointerT symfile_addr; - Uint64_T symfile_size; - }; - - struct JITDescriptor { - uint32_t version; - uint32_t action_flag; - PointerT relevant_entry; - PointerT first_entry; - }; - - // Android-specific extensions. - struct JITDescriptorExt { - JITDescriptor desc; - uint8_t magic[8]; - uint32_t flags; - uint32_t sizeof_descriptor; - uint32_t sizeof_entry; - uint32_t action_seqlock; - uint64_t action_timestamp; - }; - - JitDebugImpl(ArchEnum arch, std::shared_ptr<Memory>& memory, - std::vector<std::string>& search_libs) - : Global(memory, search_libs) { - SetArch(arch); - } +struct JITDescriptorHeader { + uint32_t version; + uint32_t action_flag; +}; - Symfile* Get(Maps* maps, uint64_t pc) override; - virtual bool ReadVariableData(uint64_t offset); - virtual void ProcessArch() {} - bool Update(Maps* maps); - bool Read(Maps* maps, JitMemory* memory); +struct JITDescriptor32 { + JITDescriptorHeader header; + uint32_t relevant_entry; + uint32_t first_entry; +}; - bool initialized_ = false; - uint64_t descriptor_addr_ = 0; // Non-zero if we have found (non-empty) descriptor. - uint64_t seqlock_addr_ = 0; // Re-read entries if the value at this address changes. - uint32_t last_seqlock_ = ~0u; // The value of seqlock when we last read the entries. +struct JITDescriptor64 { + JITDescriptorHeader header; + uint64_t relevant_entry; + uint64_t first_entry; +}; - std::deque<JitCacheEntry<Symfile>> entries_; +JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : Global(memory) {} - std::mutex lock_; -}; +JitDebug::JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs) + : Global(memory, search_libs) {} -template <typename Symfile> -std::unique_ptr<JitDebug<Symfile>> JitDebug<Symfile>::Create(ArchEnum arch, - std::shared_ptr<Memory>& memory, - std::vector<std::string> search_libs) { - typedef JitDebugImpl<Symfile, uint32_t, Uint64_P> JitDebugImpl32P; - typedef JitDebugImpl<Symfile, uint32_t, Uint64_A> JitDebugImpl32A; - typedef JitDebugImpl<Symfile, uint64_t, Uint64_A> JitDebugImpl64A; - switch (arch) { - case ARCH_X86: - static_assert(sizeof(typename JitDebugImpl32P::JITCodeEntry) == 20, "layout"); - static_assert(sizeof(typename JitDebugImpl32P::JITDescriptor) == 16, "layout"); - static_assert(sizeof(typename JitDebugImpl32P::JITDescriptorExt) == 48, "layout"); - return std::unique_ptr<JitDebug>(new JitDebugImpl32P(arch, memory, search_libs)); - break; - case ARCH_ARM: - case ARCH_MIPS: - static_assert(sizeof(typename JitDebugImpl32A::JITCodeEntry) == 24, "layout"); - static_assert(sizeof(typename JitDebugImpl32A::JITDescriptor) == 16, "layout"); - static_assert(sizeof(typename JitDebugImpl32A::JITDescriptorExt) == 48, "layout"); - return std::unique_ptr<JitDebug>(new JitDebugImpl32A(arch, memory, search_libs)); - break; - case ARCH_ARM64: - case ARCH_X86_64: - case ARCH_MIPS64: - static_assert(sizeof(typename JitDebugImpl64A::JITCodeEntry) == 32, "layout"); - static_assert(sizeof(typename JitDebugImpl64A::JITDescriptor) == 24, "layout"); - static_assert(sizeof(typename JitDebugImpl64A::JITDescriptorExt) == 56, "layout"); - return std::unique_ptr<JitDebug>(new JitDebugImpl64A(arch, memory, search_libs)); - break; - default: - abort(); +JitDebug::~JitDebug() { + for (auto* elf : elf_list_) { + delete elf; } } -size_t JitMemory::Read(uint64_t addr, void* dst, size_t size) { - if (!parent_->ReadFully(addr, dst, size)) { +uint64_t JitDebug::ReadDescriptor32(uint64_t addr) { + JITDescriptor32 desc; + if (!memory_->ReadFully(addr, &desc, sizeof(desc))) { return 0; } - // This is required for memory synchronization if the we are working with local memory. - // For other types of memory (e.g. remote) this is no-op and has no significant effect. - std::atomic_thread_fence(std::memory_order_acquire); - uint32_t seen_seqlock; - if (!parent_->Read32(seqlock_addr_, &seen_seqlock)) { - return 0; - } - if (seen_seqlock != expected_seqlock_) { - failed_due_to_race_ = true; + + if (desc.header.version != 1 || desc.first_entry == 0) { + // Either unknown version, or no jit entries. return 0; } - return size; + + return desc.first_entry; } -template <typename Symfile, typename PointerT, typename Uint64_T> -bool JitDebugImpl<Symfile, PointerT, Uint64_T>::ReadVariableData(uint64_t addr) { - JITDescriptor desc; - if (!this->memory_->ReadFully(addr, &desc, sizeof(desc))) { - return false; - } - if (desc.version != 1) { - return false; - } - if (desc.first_entry == 0) { - return false; // There could be multiple descriptors. Ignore empty ones. - } - descriptor_addr_ = addr; - JITDescriptorExt desc_ext; - if (this->memory_->ReadFully(addr, &desc_ext, sizeof(desc_ext)) && - memcmp(desc_ext.magic, kDescriptorExtMagic, 8) == 0) { - seqlock_addr_ = descriptor_addr_ + offsetof(JITDescriptorExt, action_seqlock); - } else { - // In the absence of Android-specific fields, use the head pointer instead. - seqlock_addr_ = descriptor_addr_ + offsetof(JITDescriptor, first_entry); +uint64_t JitDebug::ReadDescriptor64(uint64_t addr) { + JITDescriptor64 desc; + if (!memory_->ReadFully(addr, &desc, sizeof(desc))) { + return 0; } - return true; -} -template <typename Symfile> -static const char* GetDescriptorName(); + if (desc.header.version != 1 || desc.first_entry == 0) { + // Either unknown version, or no jit entries. + return 0; + } -template <> -const char* GetDescriptorName<Elf>() { - return "__jit_debug_descriptor"; + return desc.first_entry; } -template <typename Symfile, typename PointerT, typename Uint64_T> -Symfile* JitDebugImpl<Symfile, PointerT, Uint64_T>::Get(Maps* maps, uint64_t pc) { - std::lock_guard<std::mutex> guard(lock_); - if (!initialized_) { - FindAndReadVariable(maps, GetDescriptorName<Symfile>()); - initialized_ = true; +uint64_t JitDebug::ReadEntry32Pack(uint64_t* start, uint64_t* size) { + JITCodeEntry32Pack code; + if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) { + return 0; } - if (descriptor_addr_ == 0) { - return nullptr; - } + *start = code.symfile_addr; + *size = code.symfile_size; + return code.next; +} - if (!Update(maps)) { - return nullptr; +uint64_t JitDebug::ReadEntry32Pad(uint64_t* start, uint64_t* size) { + JITCodeEntry32Pad code; + if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) { + return 0; } - Symfile* fallback = nullptr; - for (auto& entry : entries_) { - // Skip entries which are obviously not relevant (if we know the PC range). - if (entry.size_ == 0 || (entry.addr_ <= pc && (pc - entry.addr_) < entry.size_)) { - // Double check the entry contains the PC in case there are overlapping entries. - // This is might happen for native-code due to GC and for DEX due to data sharing. - std::string method_name; - uint64_t method_offset; - if (entry.symfile_->GetFunctionName(pc, &method_name, &method_offset)) { - return entry.symfile_.get(); - } - fallback = entry.symfile_.get(); // Tests don't have any symbols. - } - } - return fallback; // Not found. + *start = code.symfile_addr; + *size = code.symfile_size; + return code.next; } -// Update JIT entries if needed. It will retry if there are data races. -template <typename Symfile, typename PointerT, typename Uint64_T> -bool JitDebugImpl<Symfile, PointerT, Uint64_T>::Update(Maps* maps) { - // We might need to retry the whole read in the presence of data races. - for (int i = 0; i < kMaxRaceRetries; i++) { - // Read the seqlock (counter which is incremented before and after any modification). - uint32_t seqlock = 0; - if (!this->memory_->Read32(seqlock_addr_, &seqlock)) { - return false; // Failed to read seqlock. - } - - // Check if anything changed since the last time we checked. - if (last_seqlock_ != seqlock) { - // Create memory wrapper to allow us to read the entries safely even in a live process. - JitMemory safe_memory; - safe_memory.parent_ = this->memory_.get(); - safe_memory.seqlock_addr_ = seqlock_addr_; - safe_memory.expected_seqlock_ = seqlock; - std::atomic_thread_fence(std::memory_order_acquire); - - // Add all entries to our cache. - if (!Read(maps, &safe_memory)) { - if (safe_memory.failed_due_to_race_) { - sleep(0); - continue; // Try again (there was a data race). - } else { - return false; // Proper failure (we could not read the data). - } - } - last_seqlock_ = seqlock; - } - return true; +uint64_t JitDebug::ReadEntry64(uint64_t* start, uint64_t* size) { + JITCodeEntry64 code; + if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) { + return 0; } - return false; // Too many retries. + + *start = code.symfile_addr; + *size = code.symfile_size; + return code.next; } -// Read all JIT entries. It might randomly fail due to data races. -template <typename Symfile, typename PointerT, typename Uint64_T> -bool JitDebugImpl<Symfile, PointerT, Uint64_T>::Read(Maps* maps, JitMemory* memory) { - std::unordered_set<uint64_t> seen_entry_addr; +void JitDebug::ProcessArch() { + switch (arch()) { + case ARCH_X86: + read_descriptor_func_ = &JitDebug::ReadDescriptor32; + read_entry_func_ = &JitDebug::ReadEntry32Pack; + break; - // Read and verify the descriptor (must be after we have read the initial seqlock). - JITDescriptor desc; - if (!(memory->ReadFully(descriptor_addr_, &desc, sizeof(desc)))) { - return false; - } + case ARCH_ARM: + case ARCH_MIPS: + read_descriptor_func_ = &JitDebug::ReadDescriptor32; + read_entry_func_ = &JitDebug::ReadEntry32Pad; + break; - entries_.clear(); - JITCodeEntry entry; - for (uint64_t entry_addr = desc.first_entry; entry_addr != 0; entry_addr = entry.next) { - // Check for infinite loops in the lined list. - if (!seen_entry_addr.emplace(entry_addr).second) { - return true; // TODO: Fail when seening infinite loop. - } + case ARCH_ARM64: + case ARCH_X86_64: + case ARCH_MIPS64: + read_descriptor_func_ = &JitDebug::ReadDescriptor64; + read_entry_func_ = &JitDebug::ReadEntry64; + break; + case ARCH_UNKNOWN: + abort(); + } +} - // Read the entry (while checking for data races). - if (!memory->ReadFully(entry_addr, &entry, sizeof(entry))) { - return false; - } +bool JitDebug::ReadVariableData(uint64_t ptr) { + entry_addr_ = (this->*read_descriptor_func_)(ptr); + return entry_addr_ != 0; +} - // Copy and load the symfile. - entries_.emplace_back(JitCacheEntry<Symfile>()); - if (!entries_.back().Init(maps, memory, entry.symfile_addr, entry.symfile_size.value)) { - return false; - } +void JitDebug::Init(Maps* maps) { + if (initialized_) { + return; } + // Regardless of what happens below, consider the init finished. + initialized_ = true; - return true; + FindAndReadVariable(maps, "__jit_debug_descriptor"); } -// Copy and load ELF file. -template <> -bool JitCacheEntry<Elf>::Init(Maps*, JitMemory* memory, uint64_t addr, uint64_t size) { - // Make a copy of the in-memory symbol file (while checking for data races). - std::unique_ptr<MemoryBuffer> buffer(new MemoryBuffer()); - buffer->Resize(size); - if (!memory->ReadFully(addr, buffer->GetPtr(0), buffer->Size())) { - return false; +Elf* JitDebug::GetElf(Maps* maps, uint64_t pc) { + // Use a single lock, this object should be used so infrequently that + // a fine grain lock is unnecessary. + std::lock_guard<std::mutex> guard(lock_); + if (!initialized_) { + Init(maps); } - // Load and validate the ELF file. - symfile_.reset(new Elf(buffer.release())); - symfile_->Init(); - if (!symfile_->valid()) { - return false; + // Search the existing elf object first. + for (Elf* elf : elf_list_) { + if (elf->IsValidPc(pc)) { + return elf; + } } - symfile_->GetTextRange(&addr_, &size_); - return true; -} + while (entry_addr_ != 0) { + uint64_t start; + uint64_t size; + entry_addr_ = (this->*read_entry_func_)(&start, &size); -template std::unique_ptr<JitDebug<Elf>> JitDebug<Elf>::Create(ArchEnum, std::shared_ptr<Memory>&, - std::vector<std::string>); - -#if !defined(NO_LIBDEXFILE_SUPPORT) - -template <> -const char* GetDescriptorName<DexFile>() { - return "__dex_debug_descriptor"; -} + Elf* elf = new Elf(new MemoryRange(memory_, start, size, 0)); + elf->Init(); + if (!elf->valid()) { + // The data is not formatted in a way we understand, do not attempt + // to process any other entries. + entry_addr_ = 0; + delete elf; + return nullptr; + } + elf_list_.push_back(elf); -// Copy and load DEX file. -template <> -bool JitCacheEntry<DexFile>::Init(Maps* maps, JitMemory* memory, uint64_t addr, uint64_t) { - MapInfo* info = maps->Find(addr); - if (info == nullptr) { - return false; - } - symfile_ = DexFile::Create(addr, memory, info); - if (symfile_ == nullptr) { - return false; + if (elf->IsValidPc(pc)) { + return elf; + } } - return true; + return nullptr; } -template std::unique_ptr<JitDebug<DexFile>> JitDebug<DexFile>::Create(ArchEnum, - std::shared_ptr<Memory>&, - std::vector<std::string>); - -#endif - } // namespace unwindstack diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp index 73c5a0475..3f2e1c1b8 100644 --- a/libunwindstack/Unwinder.cpp +++ b/libunwindstack/Unwinder.cpp @@ -36,38 +36,11 @@ #include <unwindstack/Unwinder.h> #if !defined(NO_LIBDEXFILE_SUPPORT) -#include <DexFile.h> +#include <unwindstack/DexFiles.h> #endif namespace unwindstack { -Unwinder::Unwinder(size_t max_frames, Maps* maps, Regs* regs, - std::shared_ptr<Memory> process_memory) - : max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) { - frames_.reserve(max_frames); - if (regs != nullptr) { - ArchEnum arch = regs_->Arch(); - - jit_debug_ = JitDebug<Elf>::Create(arch, process_memory_); -#if !defined(NO_LIBDEXFILE_SUPPORT) - dex_files_ = JitDebug<DexFile>::Create(arch, process_memory_); -#endif - } -} - -void Unwinder::SetRegs(Regs* regs) { - regs_ = regs; - - if (jit_debug_ == nullptr) { - ArchEnum arch = regs_->Arch(); - - jit_debug_ = JitDebug<Elf>::Create(arch, process_memory_); -#if !defined(NO_LIBDEXFILE_SUPPORT) - dex_files_ = JitDebug<DexFile>::Create(arch, process_memory_); -#endif - } -} - // Inject extra 'virtual' frame that represents the dex pc data. // The dex pc is a magic register defined in the Mterp interpreter, // and thus it will be restored/observed in the frame after it. @@ -111,7 +84,8 @@ void Unwinder::FillInDexFrame() { return; } - dex_files_->GetFunctionName(maps_, dex_pc, &frame->function_name, &frame->function_offset); + dex_files_->GetMethodInformation(maps_, info, dex_pc, &frame->function_name, + &frame->function_offset); #endif } @@ -211,7 +185,7 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip, // using the jit debug information. if (!elf->valid() && jit_debug_ != nullptr) { uint64_t adjusted_jit_pc = regs_->pc() - pc_adjustment; - Elf* jit_elf = jit_debug_->Get(maps_, adjusted_jit_pc); + Elf* jit_elf = jit_debug_->GetElf(maps_, adjusted_jit_pc); if (jit_elf != nullptr) { // The jit debug information requires a non relative adjusted pc. step_pc = adjusted_jit_pc; @@ -356,7 +330,19 @@ std::string Unwinder::FormatFrame(size_t frame_num) { return FormatFrame(frames_[frame_num]); } -bool UnwinderFromPid::Init() { +void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) { + jit_debug->SetArch(arch); + jit_debug_ = jit_debug; +} + +#if !defined(NO_LIBDEXFILE_SUPPORT) +void Unwinder::SetDexFiles(DexFiles* dex_files, ArchEnum arch) { + dex_files->SetArch(arch); + dex_files_ = dex_files; +} +#endif + +bool UnwinderFromPid::Init(ArchEnum arch) { if (pid_ == getpid()) { maps_ptr_.reset(new LocalMaps()); } else { @@ -369,6 +355,15 @@ bool UnwinderFromPid::Init() { process_memory_ = Memory::CreateProcessMemoryCached(pid_); + jit_debug_ptr_.reset(new JitDebug(process_memory_)); + jit_debug_ = jit_debug_ptr_.get(); + SetJitDebug(jit_debug_, arch); +#if !defined(NO_LIBDEXFILE_SUPPORT) + dex_files_ptr_.reset(new DexFiles(process_memory_)); + dex_files_ = dex_files_ptr_.get(); + SetDexFiles(dex_files_, arch); +#endif + return true; } diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h new file mode 100644 index 000000000..67a9640d6 --- /dev/null +++ b/libunwindstack/include/unwindstack/DexFiles.h @@ -0,0 +1,79 @@ +/* + * 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 _LIBUNWINDSTACK_DEX_FILES_H +#define _LIBUNWINDSTACK_DEX_FILES_H + +#include <stdint.h> + +#include <memory> +#include <mutex> +#include <string> +#include <unordered_map> +#include <vector> + +#include <unwindstack/Global.h> +#include <unwindstack/Memory.h> + +namespace unwindstack { + +// Forward declarations. +class DexFile; +class Maps; +struct MapInfo; +enum ArchEnum : uint8_t; + +class DexFiles : public Global { + public: + explicit DexFiles(std::shared_ptr<Memory>& memory); + DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs); + virtual ~DexFiles(); + + DexFile* GetDexFile(uint64_t dex_file_offset, MapInfo* info); + + void GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc, std::string* method_name, + uint64_t* method_offset); + + private: + void Init(Maps* maps); + + bool GetAddr(size_t index, uint64_t* addr); + + uint64_t ReadEntryPtr32(uint64_t addr); + + uint64_t ReadEntryPtr64(uint64_t addr); + + bool ReadEntry32(); + + bool ReadEntry64(); + + bool ReadVariableData(uint64_t ptr_offset) override; + + void ProcessArch() override; + + std::mutex lock_; + bool initialized_ = false; + std::unordered_map<uint64_t, std::unique_ptr<DexFile>> files_; + + uint64_t entry_addr_ = 0; + uint64_t (DexFiles::*read_entry_ptr_func_)(uint64_t) = nullptr; + bool (DexFiles::*read_entry_func_)() = nullptr; + std::vector<uint64_t> addrs_; +}; + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_DEX_FILES_H diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h index 25a665d81..ac94f101c 100644 --- a/libunwindstack/include/unwindstack/Elf.h +++ b/libunwindstack/include/unwindstack/Elf.h @@ -78,8 +78,6 @@ class Elf { bool IsValidPc(uint64_t pc); - bool GetTextRange(uint64_t* addr, uint64_t* size); - void GetLastError(ErrorData* data); ErrorCode GetLastErrorCode(); uint64_t GetLastErrorAddress(); diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h index 945c27740..dbd917d67 100644 --- a/libunwindstack/include/unwindstack/ElfInterface.h +++ b/libunwindstack/include/unwindstack/ElfInterface.h @@ -68,8 +68,6 @@ class ElfInterface { virtual bool IsValidPc(uint64_t pc); - bool GetTextRange(uint64_t* addr, uint64_t* size); - Memory* CreateGnuDebugdataMemory(); Memory* memory() { return memory_; } @@ -158,9 +156,6 @@ class ElfInterface { uint64_t gnu_build_id_offset_ = 0; uint64_t gnu_build_id_size_ = 0; - uint64_t text_addr_ = 0; - uint64_t text_size_ = 0; - uint8_t soname_type_ = SONAME_UNKNOWN; std::string soname_; diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h index 0c3ded984..8b7b4b590 100644 --- a/libunwindstack/include/unwindstack/JitDebug.h +++ b/libunwindstack/include/unwindstack/JitDebug.h @@ -19,7 +19,6 @@ #include <stdint.h> -#include <map> #include <memory> #include <mutex> #include <string> @@ -31,24 +30,40 @@ namespace unwindstack { // Forward declarations. +class Elf; class Maps; enum ArchEnum : uint8_t; -template <typename Symfile> -class JitDebug { +class JitDebug : public Global { public: - static std::unique_ptr<JitDebug> Create(ArchEnum arch, std::shared_ptr<Memory>& memory, - std::vector<std::string> search_libs = {}); - virtual ~JitDebug() {} - - // Find symbol file for given pc. - virtual Symfile* Get(Maps* maps, uint64_t pc) = 0; - - // Find symbol for given pc. - bool GetFunctionName(Maps* maps, uint64_t pc, std::string* name, uint64_t* offset) { - Symfile* file = Get(maps, pc); - return file != nullptr && file->GetFunctionName(pc, name, offset); - } + explicit JitDebug(std::shared_ptr<Memory>& memory); + JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs); + virtual ~JitDebug(); + + Elf* GetElf(Maps* maps, uint64_t pc); + + private: + void Init(Maps* maps); + + uint64_t (JitDebug::*read_descriptor_func_)(uint64_t) = nullptr; + uint64_t (JitDebug::*read_entry_func_)(uint64_t*, uint64_t*) = nullptr; + + uint64_t ReadDescriptor32(uint64_t); + uint64_t ReadDescriptor64(uint64_t); + + uint64_t ReadEntry32Pack(uint64_t* start, uint64_t* size); + uint64_t ReadEntry32Pad(uint64_t* start, uint64_t* size); + uint64_t ReadEntry64(uint64_t* start, uint64_t* size); + + bool ReadVariableData(uint64_t ptr_offset) override; + + void ProcessArch() override; + + uint64_t entry_addr_ = 0; + bool initialized_ = false; + std::vector<Elf*> elf_list_; + + std::mutex lock_; }; } // namespace unwindstack diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h index a04d7c304..8b01654a9 100644 --- a/libunwindstack/include/unwindstack/Unwinder.h +++ b/libunwindstack/include/unwindstack/Unwinder.h @@ -24,6 +24,7 @@ #include <string> #include <vector> +#include <unwindstack/DexFiles.h> #include <unwindstack/Error.h> #include <unwindstack/JitDebug.h> #include <unwindstack/Maps.h> @@ -33,7 +34,6 @@ namespace unwindstack { // Forward declarations. -class DexFile; class Elf; enum ArchEnum : uint8_t; @@ -63,14 +63,14 @@ struct FrameData { class Unwinder { public: - Unwinder(size_t max_frames, Maps* maps, Regs* regs, std::shared_ptr<Memory> process_memory); + Unwinder(size_t max_frames, Maps* maps, Regs* regs, std::shared_ptr<Memory> process_memory) + : max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) { + frames_.reserve(max_frames); + } Unwinder(size_t max_frames, Maps* maps, std::shared_ptr<Memory> process_memory) - : Unwinder(max_frames, maps, nullptr, process_memory) {} - - Unwinder(const Unwinder&) = delete; - Unwinder& operator=(const Unwinder&) = delete; - Unwinder(Unwinder&&) = default; - Unwinder& operator=(Unwinder&&) = default; + : max_frames_(max_frames), maps_(maps), process_memory_(process_memory) { + frames_.reserve(max_frames); + } virtual ~Unwinder() = default; @@ -90,7 +90,9 @@ class Unwinder { std::string FormatFrame(size_t frame_num); std::string FormatFrame(const FrameData& frame); - void SetRegs(Regs* regs); + void SetJitDebug(JitDebug* jit_debug, ArchEnum arch); + + void SetRegs(Regs* regs) { regs_ = regs; } Maps* GetMaps() { return maps_; } std::shared_ptr<Memory>& GetProcessMemory() { return process_memory_; } @@ -105,6 +107,10 @@ class Unwinder { void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; } +#if !defined(NO_LIBDEXFILE_SUPPORT) + void SetDexFiles(DexFiles* dex_files, ArchEnum arch); +#endif + ErrorCode LastErrorCode() { return last_error_.code; } uint64_t LastErrorAddress() { return last_error_.address; } @@ -120,9 +126,9 @@ class Unwinder { Regs* regs_; std::vector<FrameData> frames_; std::shared_ptr<Memory> process_memory_; - std::unique_ptr<JitDebug<Elf>> jit_debug_; + JitDebug* jit_debug_ = nullptr; #if !defined(NO_LIBDEXFILE_SUPPORT) - std::unique_ptr<JitDebug<DexFile>> dex_files_; + DexFiles* dex_files_ = nullptr; #endif bool resolve_names_ = true; bool embedded_soname_ = true; @@ -135,11 +141,15 @@ class UnwinderFromPid : public Unwinder { UnwinderFromPid(size_t max_frames, pid_t pid) : Unwinder(max_frames), pid_(pid) {} virtual ~UnwinderFromPid() = default; - bool Init(); + bool Init(ArchEnum arch); private: pid_t pid_; std::unique_ptr<Maps> maps_ptr_; + std::unique_ptr<JitDebug> jit_debug_ptr_; +#if !defined(NO_LIBDEXFILE_SUPPORT) + std::unique_ptr<DexFiles> dex_files_ptr_; +#endif }; } // namespace unwindstack diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp index df7b31dfd..0149a42c7 100644 --- a/libunwindstack/tests/DexFileTest.cpp +++ b/libunwindstack/tests/DexFileTest.cpp @@ -177,11 +177,11 @@ TEST(DexFileTest, get_method) { std::string method; uint64_t method_offset; - ASSERT_TRUE(dex_file->GetFunctionName(0x4102, &method, &method_offset)); + ASSERT_TRUE(dex_file->GetMethodInformation(0x102, &method, &method_offset)); EXPECT_EQ("Main.<init>", method); EXPECT_EQ(2U, method_offset); - ASSERT_TRUE(dex_file->GetFunctionName(0x4118, &method, &method_offset)); + ASSERT_TRUE(dex_file->GetMethodInformation(0x118, &method, &method_offset)); EXPECT_EQ("Main.main", method); EXPECT_EQ(0U, method_offset); } @@ -195,9 +195,9 @@ TEST(DexFileTest, get_method_empty) { std::string method; uint64_t method_offset; - EXPECT_FALSE(dex_file->GetFunctionName(0x100000, &method, &method_offset)); + EXPECT_FALSE(dex_file->GetMethodInformation(0x100000, &method, &method_offset)); - EXPECT_FALSE(dex_file->GetFunctionName(0x98, &method, &method_offset)); + EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset)); } } // namespace unwindstack diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp index 655dcc8e3..1ea9e5c03 100644 --- a/libunwindstack/tests/DexFilesTest.cpp +++ b/libunwindstack/tests/DexFilesTest.cpp @@ -22,8 +22,8 @@ #include <gtest/gtest.h> +#include <unwindstack/DexFiles.h> #include <unwindstack/Elf.h> -#include <unwindstack/JitDebug.h> #include <unwindstack/MapInfo.h> #include <unwindstack/Maps.h> #include <unwindstack/Memory.h> @@ -32,10 +32,6 @@ #include "ElfFake.h" #include "MemoryFake.h" -#if !defined(NO_LIBDEXFILE_SUPPORT) -#include <DexFile.h> -#endif - namespace unwindstack { class DexFilesTest : public ::testing::Test { @@ -52,7 +48,8 @@ class DexFilesTest : public ::testing::Test { } void Init(ArchEnum arch) { - dex_files_ = JitDebug<DexFile>::Create(arch, process_memory_); + dex_files_.reset(new DexFiles(process_memory_)); + dex_files_->SetArch(arch); maps_.reset( new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf\n" @@ -89,11 +86,10 @@ class DexFilesTest : public ::testing::Test { Init(ARCH_ARM); } - void WriteDescriptor32(uint64_t addr, uint32_t entry); - void WriteDescriptor64(uint64_t addr, uint64_t entry); - void WriteEntry32Pack(uint64_t addr, uint32_t next, uint32_t prev, uint32_t dex); - void WriteEntry32Pad(uint64_t addr, uint32_t next, uint32_t prev, uint32_t dex); - void WriteEntry64(uint64_t addr, uint64_t next, uint64_t prev, uint64_t dex); + void WriteDescriptor32(uint64_t addr, uint32_t head); + void WriteDescriptor64(uint64_t addr, uint64_t head); + void WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev, uint32_t dex_file); + void WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev, uint64_t dex_file); void WriteDex(uint64_t dex_file); static constexpr size_t kMapGlobalNonReadable = 2; @@ -105,70 +101,40 @@ class DexFilesTest : public ::testing::Test { std::shared_ptr<Memory> process_memory_; MemoryFake* memory_; - std::unique_ptr<JitDebug<DexFile>> dex_files_; + std::unique_ptr<DexFiles> dex_files_; std::unique_ptr<BufferMaps> maps_; }; -void DexFilesTest::WriteDescriptor32(uint64_t addr, uint32_t entry) { - // Format of the 32 bit JITDescriptor structure: - // uint32_t version - memory_->SetData32(addr, 1); - // uint32_t action_flag - memory_->SetData32(addr + 4, 0); - // uint32_t relevant_entry - memory_->SetData32(addr + 8, 0); - // uint32_t first_entry - memory_->SetData32(addr + 12, entry); -} - -void DexFilesTest::WriteDescriptor64(uint64_t addr, uint64_t entry) { - // Format of the 64 bit JITDescriptor structure: - // uint32_t version - memory_->SetData32(addr, 1); - // uint32_t action_flag - memory_->SetData32(addr + 4, 0); - // uint64_t relevant_entry - memory_->SetData64(addr + 8, 0); - // uint64_t first_entry - memory_->SetData64(addr + 16, entry); +void DexFilesTest::WriteDescriptor32(uint64_t addr, uint32_t head) { + // void* first_entry_ + memory_->SetData32(addr + 12, head); } -void DexFilesTest::WriteEntry32Pack(uint64_t addr, uint32_t next, uint32_t prev, uint32_t dex) { - // Format of the 32 bit JITCodeEntry structure: - // uint32_t next - memory_->SetData32(addr, next); - // uint32_t prev - memory_->SetData32(addr + 4, prev); - // uint32_t dex - memory_->SetData32(addr + 8, dex); - // uint64_t symfile_size - memory_->SetData64(addr + 12, sizeof(kDexData) * sizeof(uint32_t)); +void DexFilesTest::WriteDescriptor64(uint64_t addr, uint64_t head) { + // void* first_entry_ + memory_->SetData64(addr + 16, head); } -void DexFilesTest::WriteEntry32Pad(uint64_t addr, uint32_t next, uint32_t prev, uint32_t dex) { - // Format of the 32 bit JITCodeEntry structure: +void DexFilesTest::WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev, + uint32_t dex_file) { + // Format of the 32 bit DEXFileEntry structure: // uint32_t next - memory_->SetData32(addr, next); + memory_->SetData32(entry_addr, next); // uint32_t prev - memory_->SetData32(addr + 4, prev); - // uint32_t dex - memory_->SetData32(addr + 8, dex); - // uint32_t pad - memory_->SetData32(addr + 12, 0); - // uint64_t symfile_size - memory_->SetData64(addr + 16, sizeof(kDexData) * sizeof(uint32_t)); + memory_->SetData32(entry_addr + 4, prev); + // uint32_t dex_file + memory_->SetData32(entry_addr + 8, dex_file); } -void DexFilesTest::WriteEntry64(uint64_t addr, uint64_t next, uint64_t prev, uint64_t dex) { - // Format of the 64 bit JITCodeEntry structure: +void DexFilesTest::WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev, + uint64_t dex_file) { + // Format of the 64 bit DEXFileEntry structure: // uint64_t next - memory_->SetData64(addr, next); + memory_->SetData64(entry_addr, next); // uint64_t prev - memory_->SetData64(addr + 8, prev); - // uint64_t dex - memory_->SetData64(addr + 16, dex); - // uint64_t symfile_size - memory_->SetData64(addr + 24, sizeof(kDexData) * sizeof(uint32_t)); + memory_->SetData64(entry_addr + 8, prev); + // uint64_t dex_file + memory_->SetData64(entry_addr + 16, dex_file); } void DexFilesTest::WriteDex(uint64_t dex_file) { @@ -178,8 +144,9 @@ void DexFilesTest::WriteDex(uint64_t dex_file) { TEST_F(DexFilesTest, get_method_information_invalid) { std::string method_name = "nothing"; uint64_t method_offset = 0x124; + MapInfo* info = maps_->Get(kMapDexFileEntries); - dex_files_->GetFunctionName(maps_.get(), 0, &method_name, &method_offset); + dex_files_->GetMethodInformation(maps_.get(), info, 0, &method_name, &method_offset); EXPECT_EQ("nothing", method_name); EXPECT_EQ(0x124U, method_offset); } @@ -187,12 +154,13 @@ TEST_F(DexFilesTest, get_method_information_invalid) { TEST_F(DexFilesTest, get_method_information_32) { std::string method_name = "nothing"; uint64_t method_offset = 0x124; + MapInfo* info = maps_->Get(kMapDexFiles); WriteDescriptor32(0xf800, 0x200000); - WriteEntry32Pad(0x200000, 0, 0, 0x300000); + WriteEntry32(0x200000, 0, 0, 0x300000); WriteDex(0x300000); - dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset); + dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset); EXPECT_EQ("Main.<init>", method_name); EXPECT_EQ(0U, method_offset); } @@ -202,12 +170,13 @@ TEST_F(DexFilesTest, get_method_information_64) { std::string method_name = "nothing"; uint64_t method_offset = 0x124; + MapInfo* info = maps_->Get(kMapDexFiles); WriteDescriptor64(0xf800, 0x200000); WriteEntry64(0x200000, 0, 0, 0x301000); WriteDex(0x301000); - dex_files_->GetFunctionName(maps_.get(), 0x301102, &method_name, &method_offset); + dex_files_->GetMethodInformation(maps_.get(), info, 0x301102, &method_name, &method_offset); EXPECT_EQ("Main.<init>", method_name); EXPECT_EQ(2U, method_offset); } @@ -215,14 +184,14 @@ TEST_F(DexFilesTest, get_method_information_64) { TEST_F(DexFilesTest, get_method_information_not_first_entry_32) { std::string method_name = "nothing"; uint64_t method_offset = 0x124; + MapInfo* info = maps_->Get(kMapDexFiles); WriteDescriptor32(0xf800, 0x200000); - WriteEntry32Pad(0x200000, 0x200100, 0, 0x100000); - WriteDex(0x100000); - WriteEntry32Pad(0x200100, 0, 0x200000, 0x300000); + WriteEntry32(0x200000, 0x200100, 0, 0x100000); + WriteEntry32(0x200100, 0, 0x200000, 0x300000); WriteDex(0x300000); - dex_files_->GetFunctionName(maps_.get(), 0x300104, &method_name, &method_offset); + dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset); EXPECT_EQ("Main.<init>", method_name); EXPECT_EQ(4U, method_offset); } @@ -232,14 +201,14 @@ TEST_F(DexFilesTest, get_method_information_not_first_entry_64) { std::string method_name = "nothing"; uint64_t method_offset = 0x124; + MapInfo* info = maps_->Get(kMapDexFiles); WriteDescriptor64(0xf800, 0x200000); WriteEntry64(0x200000, 0x200100, 0, 0x100000); - WriteDex(0x100000); WriteEntry64(0x200100, 0, 0x200000, 0x300000); WriteDex(0x300000); - dex_files_->GetFunctionName(maps_.get(), 0x300106, &method_name, &method_offset); + dex_files_->GetMethodInformation(maps_.get(), info, 0x300106, &method_name, &method_offset); EXPECT_EQ("Main.<init>", method_name); EXPECT_EQ(6U, method_offset); } @@ -247,18 +216,19 @@ TEST_F(DexFilesTest, get_method_information_not_first_entry_64) { TEST_F(DexFilesTest, get_method_information_cached) { std::string method_name = "nothing"; uint64_t method_offset = 0x124; + MapInfo* info = maps_->Get(kMapDexFiles); WriteDescriptor32(0xf800, 0x200000); - WriteEntry32Pad(0x200000, 0, 0, 0x300000); + WriteEntry32(0x200000, 0, 0, 0x300000); WriteDex(0x300000); - dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset); + dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset); EXPECT_EQ("Main.<init>", method_name); EXPECT_EQ(0U, method_offset); // Clear all memory and make sure that data is acquired from the cache. memory_->Clear(); - dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset); + dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset); EXPECT_EQ("Main.<init>", method_name); EXPECT_EQ(0U, method_offset); } @@ -266,24 +236,26 @@ TEST_F(DexFilesTest, get_method_information_cached) { TEST_F(DexFilesTest, get_method_information_search_libs) { std::string method_name = "nothing"; uint64_t method_offset = 0x124; + MapInfo* info = maps_->Get(kMapDexFiles); WriteDescriptor32(0xf800, 0x200000); - WriteEntry32Pad(0x200000, 0x200100, 0, 0x100000); - WriteDex(0x100000); - WriteEntry32Pad(0x200100, 0, 0x200000, 0x300000); + WriteEntry32(0x200000, 0x200100, 0, 0x100000); + WriteEntry32(0x200100, 0, 0x200000, 0x300000); WriteDex(0x300000); // Only search a given named list of libs. std::vector<std::string> libs{"libart.so"}; - dex_files_ = JitDebug<DexFile>::Create(ARCH_ARM, process_memory_, libs); + dex_files_.reset(new DexFiles(process_memory_, libs)); + dex_files_->SetArch(ARCH_ARM); - dex_files_->GetFunctionName(maps_.get(), 0x300104, &method_name, &method_offset); + dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset); EXPECT_EQ("nothing", method_name); EXPECT_EQ(0x124U, method_offset); MapInfo* map_info = maps_->Get(kMapGlobal); map_info->name = "/system/lib/libart.so"; - dex_files_ = JitDebug<DexFile>::Create(ARCH_ARM, process_memory_, libs); + dex_files_.reset(new DexFiles(process_memory_, libs)); + dex_files_->SetArch(ARCH_ARM); // Set the rw map to the same name or this will not scan this entry. map_info = maps_->Get(kMapGlobalRw); map_info->name = "/system/lib/libart.so"; @@ -291,7 +263,7 @@ TEST_F(DexFilesTest, get_method_information_search_libs) { // DexFiles object. libs.clear(); - dex_files_->GetFunctionName(maps_.get(), 0x300104, &method_name, &method_offset); + dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset); EXPECT_EQ("Main.<init>", method_name); EXPECT_EQ(4U, method_offset); } @@ -299,24 +271,26 @@ TEST_F(DexFilesTest, get_method_information_search_libs) { TEST_F(DexFilesTest, get_method_information_global_skip_zero_32) { std::string method_name = "nothing"; uint64_t method_offset = 0x124; + MapInfo* info = maps_->Get(kMapDexFiles); // First global variable found, but value is zero. WriteDescriptor32(0xa800, 0); WriteDescriptor32(0xf800, 0x200000); - WriteEntry32Pad(0x200000, 0, 0, 0x300000); + WriteEntry32(0x200000, 0, 0, 0x300000); WriteDex(0x300000); - dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset); + dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset); EXPECT_EQ("Main.<init>", method_name); EXPECT_EQ(0U, method_offset); // Verify that second is ignored when first is set to non-zero - dex_files_ = JitDebug<DexFile>::Create(ARCH_ARM, process_memory_); + dex_files_.reset(new DexFiles(process_memory_)); + dex_files_->SetArch(ARCH_ARM); method_name = "fail"; method_offset = 0x123; WriteDescriptor32(0xa800, 0x100000); - dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset); + dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset); EXPECT_EQ("fail", method_name); EXPECT_EQ(0x123U, method_offset); } @@ -326,6 +300,7 @@ TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) { std::string method_name = "nothing"; uint64_t method_offset = 0x124; + MapInfo* info = maps_->Get(kMapDexFiles); // First global variable found, but value is zero. WriteDescriptor64(0xa800, 0); @@ -334,16 +309,17 @@ TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) { WriteEntry64(0x200000, 0, 0, 0x300000); WriteDex(0x300000); - dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset); + dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset); EXPECT_EQ("Main.<init>", method_name); EXPECT_EQ(0U, method_offset); // Verify that second is ignored when first is set to non-zero - dex_files_ = JitDebug<DexFile>::Create(ARCH_ARM64, process_memory_); + dex_files_.reset(new DexFiles(process_memory_)); + dex_files_->SetArch(ARCH_ARM64); method_name = "fail"; method_offset = 0x123; WriteDescriptor64(0xa800, 0x100000); - dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset); + dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset); EXPECT_EQ("fail", method_name); EXPECT_EQ(0x123U, method_offset); } diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp index 438194a9f..b1ca111cb 100644 --- a/libunwindstack/tests/JitDebugTest.cpp +++ b/libunwindstack/tests/JitDebugTest.cpp @@ -46,7 +46,8 @@ class JitDebugTest : public ::testing::Test { } void Init(ArchEnum arch) { - jit_debug_ = JitDebug<Elf>::Create(arch, process_memory_); + jit_debug_.reset(new JitDebug(process_memory_)); + jit_debug_->SetArch(arch); maps_.reset( new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf1\n" @@ -61,12 +62,6 @@ class JitDebugTest : public ::testing::Test { "200000-210000 rw-p 0002000 00:00 0 /fake/elf4\n")); ASSERT_TRUE(maps_->Parse()); - // Ensure all memory of the ELF file is initialized, - // otherwise reads within it may fail. - for (uint64_t addr = 0x4000; addr < 0x6000; addr += 8) { - memory_->SetData64(addr, 0); - } - MapInfo* map_info = maps_->Get(3); ASSERT_TRUE(map_info != nullptr); CreateFakeElf(map_info); @@ -99,7 +94,7 @@ class JitDebugTest : public ::testing::Test { ehdr.e_shstrndx = 1; ehdr.e_shoff = sh_offset; ehdr.e_shentsize = sizeof(ShdrType); - ehdr.e_shnum = 4; + ehdr.e_shnum = 3; memory_->SetMemory(offset, &ehdr, sizeof(ehdr)); ShdrType shdr; @@ -115,7 +110,6 @@ class JitDebugTest : public ::testing::Test { shdr.sh_size = 0x100; memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr)); memory_->SetMemory(offset + 0x500, ".debug_frame"); - memory_->SetMemory(offset + 0x550, ".text"); sh_offset += sizeof(shdr); memset(&shdr, 0, sizeof(shdr)); @@ -126,15 +120,6 @@ class JitDebugTest : public ::testing::Test { shdr.sh_size = 0x200; memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr)); - sh_offset += sizeof(shdr); - memset(&shdr, 0, sizeof(shdr)); - shdr.sh_type = SHT_NOBITS; - shdr.sh_name = 0x50; - shdr.sh_addr = pc; - shdr.sh_offset = 0; - shdr.sh_size = size; - memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr)); - // Now add a single cie/fde. uint64_t dwarf_offset = offset + 0x600; if (class_type == ELFCLASS32) { @@ -183,7 +168,7 @@ class JitDebugTest : public ::testing::Test { std::shared_ptr<Memory> process_memory_; MemoryFake* memory_; - std::unique_ptr<JitDebug<Elf>> jit_debug_; + std::unique_ptr<JitDebug> jit_debug_; std::unique_ptr<BufferMaps> maps_; }; @@ -253,20 +238,20 @@ void JitDebugTest::WriteEntry64(uint64_t addr, uint64_t prev, uint64_t next, uin } TEST_F(JitDebugTest, get_elf_invalid) { - Elf* elf = jit_debug_->Get(maps_.get(), 0x1500); + Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500); ASSERT_TRUE(elf == nullptr); } TEST_F(JitDebugTest, get_elf_no_global_variable) { maps_.reset(new BufferMaps("")); - Elf* elf = jit_debug_->Get(maps_.get(), 0x1500); + Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500); ASSERT_TRUE(elf == nullptr); } TEST_F(JitDebugTest, get_elf_no_valid_descriptor_in_memory) { CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200); - Elf* elf = jit_debug_->Get(maps_.get(), 0x1500); + Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500); ASSERT_TRUE(elf == nullptr); } @@ -275,7 +260,7 @@ TEST_F(JitDebugTest, get_elf_no_valid_code_entry) { WriteDescriptor32(0xf800, 0x200000); - Elf* elf = jit_debug_->Get(maps_.get(), 0x1500); + Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500); ASSERT_TRUE(elf == nullptr); } @@ -284,7 +269,7 @@ TEST_F(JitDebugTest, get_elf_invalid_descriptor_first_entry) { WriteDescriptor32(0xf800, 0); - Elf* elf = jit_debug_->Get(maps_.get(), 0x1500); + Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500); ASSERT_TRUE(elf == nullptr); } @@ -295,7 +280,7 @@ TEST_F(JitDebugTest, get_elf_invalid_descriptor_version) { // Set the version to an invalid value. memory_->SetData32(0xf800, 2); - Elf* elf = jit_debug_->Get(maps_.get(), 0x1500); + Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500); ASSERT_TRUE(elf == nullptr); } @@ -305,18 +290,12 @@ TEST_F(JitDebugTest, get_elf_32) { WriteDescriptor32(0xf800, 0x200000); WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000); - Elf* elf = jit_debug_->Get(maps_.get(), 0x1500); + Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500); ASSERT_TRUE(elf != nullptr); - uint64_t text_addr; - uint64_t text_size; - ASSERT_TRUE(elf->GetTextRange(&text_addr, &text_size)); - ASSERT_EQ(text_addr, 0x1500u); - ASSERT_EQ(text_size, 0x200u); // Clear the memory and verify all of the data is cached. memory_->Clear(); - WriteDescriptor32(0xf800, 0x200000); - Elf* elf2 = jit_debug_->Get(maps_.get(), 0x1500); + Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500); ASSERT_TRUE(elf2 != nullptr); EXPECT_EQ(elf, elf2); } @@ -330,15 +309,16 @@ TEST_F(JitDebugTest, get_multiple_jit_debug_descriptors_valid) { WriteDescriptor32(0x12800, 0x201000); WriteEntry32Pad(0x201000, 0, 0, 0x5000, 0x1000); - ASSERT_TRUE(jit_debug_->Get(maps_.get(), 0x1500) != nullptr); - ASSERT_TRUE(jit_debug_->Get(maps_.get(), 0x2000) == nullptr); + ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr); + ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x2000) == nullptr); // Now clear the descriptor entry for the first one. WriteDescriptor32(0xf800, 0); - jit_debug_ = JitDebug<Elf>::Create(ARCH_ARM, process_memory_); + jit_debug_.reset(new JitDebug(process_memory_)); + jit_debug_->SetArch(ARCH_ARM); - ASSERT_TRUE(jit_debug_->Get(maps_.get(), 0x1500) == nullptr); - ASSERT_TRUE(jit_debug_->Get(maps_.get(), 0x2000) != nullptr); + ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) == nullptr); + ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x2000) != nullptr); } TEST_F(JitDebugTest, get_elf_x86) { @@ -349,14 +329,13 @@ TEST_F(JitDebugTest, get_elf_x86) { WriteDescriptor32(0xf800, 0x200000); WriteEntry32Pack(0x200000, 0, 0, 0x4000, 0x1000); - jit_debug_ = JitDebug<Elf>::Create(ARCH_X86, process_memory_); - Elf* elf = jit_debug_->Get(maps_.get(), 0x1500); + jit_debug_->SetArch(ARCH_X86); + Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500); ASSERT_TRUE(elf != nullptr); // Clear the memory and verify all of the data is cached. memory_->Clear(); - WriteDescriptor32(0xf800, 0x200000); - Elf* elf2 = jit_debug_->Get(maps_.get(), 0x1500); + Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500); ASSERT_TRUE(elf2 != nullptr); EXPECT_EQ(elf, elf2); } @@ -369,13 +348,12 @@ TEST_F(JitDebugTest, get_elf_64) { WriteDescriptor64(0xf800, 0x200000); WriteEntry64(0x200000, 0, 0, 0x4000, 0x1000); - Elf* elf = jit_debug_->Get(maps_.get(), 0x1500); + Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500); ASSERT_TRUE(elf != nullptr); // Clear the memory and verify all of the data is cached. memory_->Clear(); - WriteDescriptor64(0xf800, 0x200000); - Elf* elf2 = jit_debug_->Get(maps_.get(), 0x1500); + Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500); ASSERT_TRUE(elf2 != nullptr); EXPECT_EQ(elf, elf2); } @@ -388,21 +366,20 @@ TEST_F(JitDebugTest, get_elf_multiple_entries) { WriteEntry32Pad(0x200000, 0, 0x200100, 0x4000, 0x1000); WriteEntry32Pad(0x200100, 0x200100, 0, 0x5000, 0x1000); - Elf* elf_2 = jit_debug_->Get(maps_.get(), 0x2400); + Elf* elf_2 = jit_debug_->GetElf(maps_.get(), 0x2400); ASSERT_TRUE(elf_2 != nullptr); - Elf* elf_1 = jit_debug_->Get(maps_.get(), 0x1600); + Elf* elf_1 = jit_debug_->GetElf(maps_.get(), 0x1600); ASSERT_TRUE(elf_1 != nullptr); // Clear the memory and verify all of the data is cached. memory_->Clear(); - WriteDescriptor32(0xf800, 0x200000); - EXPECT_EQ(elf_1, jit_debug_->Get(maps_.get(), 0x1500)); - EXPECT_EQ(elf_1, jit_debug_->Get(maps_.get(), 0x16ff)); - EXPECT_EQ(elf_2, jit_debug_->Get(maps_.get(), 0x2300)); - EXPECT_EQ(elf_2, jit_debug_->Get(maps_.get(), 0x26ff)); - EXPECT_EQ(nullptr, jit_debug_->Get(maps_.get(), 0x1700)); - EXPECT_EQ(nullptr, jit_debug_->Get(maps_.get(), 0x2700)); + EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x1500)); + EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x16ff)); + EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x2300)); + EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x26ff)); + EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x1700)); + EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x2700)); } TEST_F(JitDebugTest, get_elf_search_libs) { @@ -413,19 +390,21 @@ TEST_F(JitDebugTest, get_elf_search_libs) { // Only search a given named list of libs. std::vector<std::string> libs{"libart.so"}; - jit_debug_ = JitDebug<Elf>::Create(ARCH_ARM, process_memory_, libs); - EXPECT_TRUE(jit_debug_->Get(maps_.get(), 0x1500) == nullptr); + jit_debug_.reset(new JitDebug(process_memory_, libs)); + jit_debug_->SetArch(ARCH_ARM); + EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) == nullptr); // Change the name of the map that includes the value and verify this works. MapInfo* map_info = maps_->Get(5); map_info->name = "/system/lib/libart.so"; map_info = maps_->Get(6); map_info->name = "/system/lib/libart.so"; - jit_debug_ = JitDebug<Elf>::Create(ARCH_ARM, process_memory_); + jit_debug_.reset(new JitDebug(process_memory_, libs)); // Make sure that clearing our copy of the libs doesn't affect the // JitDebug object. libs.clear(); - EXPECT_TRUE(jit_debug_->Get(maps_.get(), 0x1500) != nullptr); + jit_debug_->SetArch(ARCH_ARM); + EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr); } } // namespace unwindstack diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp index e3c646a74..02ba9c8fa 100644 --- a/libunwindstack/tests/UnwindOfflineTest.cpp +++ b/libunwindstack/tests/UnwindOfflineTest.cpp @@ -307,7 +307,9 @@ TEST_F(UnwindOfflineTest, jit_debug_x86) { } process_memory_.reset(memory); + JitDebug jit_debug(process_memory_); Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_); + unwinder.SetJitDebug(&jit_debug, regs_->Arch()); unwinder.Unwind(); std::string frame_info(DumpFrames(unwinder)); @@ -607,7 +609,9 @@ TEST_F(UnwindOfflineTest, jit_debug_arm) { } process_memory_.reset(memory); + JitDebug jit_debug(process_memory_); Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_); + unwinder.SetJitDebug(&jit_debug, regs_->Arch()); unwinder.Unwind(); std::string frame_info(DumpFrames(unwinder)); @@ -928,7 +932,9 @@ static void OfflineUnwind(void* data) { LeakType* leak_data = reinterpret_cast<LeakType*>(data); std::unique_ptr<Regs> regs_copy(leak_data->regs->Clone()); + JitDebug jit_debug(leak_data->process_memory); Unwinder unwinder(128, leak_data->maps, regs_copy.get(), leak_data->process_memory); + unwinder.SetJitDebug(&jit_debug, regs_copy->Arch()); unwinder.Unwind(); ASSERT_EQ(76U, unwinder.NumFrames()); } @@ -1049,7 +1055,9 @@ TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) { } process_memory_.reset(memory); + JitDebug jit_debug(process_memory_); Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_); + unwinder.SetJitDebug(&jit_debug, regs_->Arch()); unwinder.Unwind(); std::string frame_info(DumpFrames(unwinder)); diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp index 5e7e6bf9f..4e3801511 100644 --- a/libunwindstack/tests/UnwindTest.cpp +++ b/libunwindstack/tests/UnwindTest.cpp @@ -170,7 +170,7 @@ extern "C" void InnerFunction(TestTypeEnum test_type) { unwinder.reset(new Unwinder(512, maps.get(), regs.get(), process_memory)); } else { UnwinderFromPid* unwinder_from_pid = new UnwinderFromPid(512, getpid()); - ASSERT_TRUE(unwinder_from_pid->Init()); + ASSERT_TRUE(unwinder_from_pid->Init(regs->Arch())); unwinder_from_pid->SetRegs(regs.get()); unwinder.reset(unwinder_from_pid); } @@ -283,7 +283,7 @@ TEST_F(UnwindTest, unwind_from_pid_remote) { ASSERT_TRUE(regs.get() != nullptr); UnwinderFromPid unwinder(512, pid); - ASSERT_TRUE(unwinder.Init()); + ASSERT_TRUE(unwinder.Init(regs->Arch())); unwinder.SetRegs(regs.get()); VerifyUnwind(&unwinder, kFunctionOrder); @@ -335,7 +335,7 @@ static void RemoteUnwindFromPid(void* data) { ASSERT_TRUE(regs.get() != nullptr); UnwinderFromPid unwinder(512, *pid); - ASSERT_TRUE(unwinder.Init()); + ASSERT_TRUE(unwinder.Init(regs->Arch())); unwinder.SetRegs(regs.get()); VerifyUnwind(&unwinder, kFunctionOrder); diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp index cad95f8c5..1812e5037 100644 --- a/libunwindstack/tools/unwind.cpp +++ b/libunwindstack/tools/unwind.cpp @@ -26,6 +26,7 @@ #include <sys/types.h> #include <unistd.h> +#include <unwindstack/DexFiles.h> #include <unwindstack/Elf.h> #include <unwindstack/JitDebug.h> #include <unwindstack/Maps.h> @@ -89,7 +90,7 @@ void DoUnwind(pid_t pid) { printf("\n"); unwindstack::UnwinderFromPid unwinder(1024, pid); - if (!unwinder.Init()) { + if (!unwinder.Init(regs->Arch())) { printf("Failed to init unwinder object.\n"); return; } diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp index 86f3163e3..4f67d679f 100644 --- a/libunwindstack/tools/unwind_for_offline.cpp +++ b/libunwindstack/tools/unwind_for_offline.cpp @@ -248,7 +248,7 @@ int SaveData(pid_t pid) { // Do an unwind so we know how much of the stack to save, and what // elf files are involved. unwindstack::UnwinderFromPid unwinder(1024, pid); - if (!unwinder.Init()) { + if (!unwinder.Init(regs->Arch())) { printf("Unable to init unwinder object.\n"); return 1; } |