diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp index 2a555afc4..b0345a140 100644 --- a/libbacktrace/UnwindStack.cpp +++ b/libbacktrace/UnwindStack.cpp @@ -50,6 +50,7 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map, auto process_memory = stack_map->process_memory(); unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(), regs, stack_map->process_memory()); + unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch()); unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore()); if (num_ignore_frames >= unwinder.NumFrames()) { diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp index 836a774b9..93406dc62 100644 --- a/libbacktrace/UnwindStackMap.cpp +++ b/libbacktrace/UnwindStackMap.cpp @@ -18,6 +18,9 @@ #include #include +#include +#include + #include #include #include @@ -39,6 +42,10 @@ 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 search_libs_{"libart.so", "libartd.so"}; + jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_)); + if (!stack_maps_->Parse()) { return false; } diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h index 2f63655fd..12c590982 100644 --- a/libbacktrace/UnwindStackMap.h +++ b/libbacktrace/UnwindStackMap.h @@ -23,6 +23,7 @@ #include #include +#include #include class UnwindStackMap : public BacktraceMap { @@ -41,11 +42,14 @@ class UnwindStackMap : public BacktraceMap { const std::shared_ptr& process_memory() { return process_memory_; } + unwindstack::JitDebug* GetJitDebug() { return jit_debug_.get(); } + protected: uint64_t GetLoadBias(size_t index) override; std::unique_ptr stack_maps_; std::shared_ptr process_memory_; + std::unique_ptr jit_debug_; }; #endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 133f3b9aa..484bc7d55 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -55,6 +55,7 @@ cc_library { "Elf.cpp", "ElfInterface.cpp", "ElfInterfaceArm.cpp", + "JitDebug.cpp", "Log.cpp", "MapInfo.cpp", "Maps.cpp", @@ -128,6 +129,7 @@ cc_test { "tests/ElfInterfaceTest.cpp", "tests/ElfTest.cpp", "tests/ElfTestUtils.cpp", + "tests/JitDebugTest.cpp", "tests/LogFake.cpp", "tests/MapInfoGetElfTest.cpp", "tests/MapInfoGetLoadBiasTest.cpp", @@ -168,6 +170,7 @@ cc_test { data: [ "tests/files/elf32.xz", "tests/files/elf64.xz", + "tests/files/offline/jit_debug_x86_32/*", "tests/files/offline/gnu_debugdata_arm32/*", "tests/files/offline/straddle_arm32/*", "tests/files/offline/straddle_arm64/*", diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp index 5ec4a3d2e..220e549aa 100644 --- a/libunwindstack/Elf.cpp +++ b/libunwindstack/Elf.cpp @@ -103,6 +103,37 @@ bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offse addr, load_bias_, name, func_offset))); } +bool Elf::GetGlobalVariable(const std::string& name, uint64_t* memory_address) { + if (!valid_) { + return false; + } + + if (!interface_->GetGlobalVariable(name, memory_address) && + (gnu_debugdata_interface_ == nullptr || + !gnu_debugdata_interface_->GetGlobalVariable(name, memory_address))) { + return false; + } + + // Adjust by the load bias. + if (*memory_address < load_bias_) { + return false; + } + + *memory_address -= load_bias_; + + // If this winds up in the dynamic section, then we might need to adjust + // the address. + uint64_t dynamic_end = interface_->dynamic_vaddr() + interface_->dynamic_size(); + if (*memory_address >= interface_->dynamic_vaddr() && *memory_address < dynamic_end) { + if (interface_->dynamic_vaddr() > interface_->dynamic_offset()) { + *memory_address -= interface_->dynamic_vaddr() - interface_->dynamic_offset(); + } else { + *memory_address += interface_->dynamic_offset() - interface_->dynamic_vaddr(); + } + } + return true; +} + // The relative pc is always relative to the start of the map from which it comes. bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs, Memory* process_memory, bool* finished) { @@ -160,6 +191,23 @@ void Elf::GetInfo(Memory* memory, bool* valid, uint64_t* size) { } } +bool Elf::IsValidPc(uint64_t pc) { + if (!valid_ || pc < load_bias_) { + return false; + } + pc -= load_bias_; + + if (interface_->IsValidPc(pc)) { + return true; + } + + if (gnu_debugdata_interface_ != nullptr && gnu_debugdata_interface_->IsValidPc(pc)) { + 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 df1642e18..0e3ab2c88 100644 --- a/libunwindstack/ElfInterface.cpp +++ b/libunwindstack/ElfInterface.cpp @@ -43,6 +43,30 @@ ElfInterface::~ElfInterface() { } } +bool ElfInterface::IsValidPc(uint64_t pc) { + if (!pt_loads_.empty()) { + for (auto& entry : pt_loads_) { + uint64_t start = entry.second.table_offset; + uint64_t end = start + entry.second.table_size; + if (pc >= start && pc < end) { + return true; + } + } + return false; + } + + // No PT_LOAD data, look for a fde for this pc in the section data. + if (debug_frame_ != nullptr && debug_frame_->GetFdeFromPc(pc) != nullptr) { + return true; + } + + if (eh_frame_ != nullptr && eh_frame_->GetFdeFromPc(pc) != nullptr) { + return true; + } + + return false; +} + Memory* ElfInterface::CreateGnuDebugdataMemory() { if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) { return nullptr; @@ -225,6 +249,10 @@ bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) return false; } dynamic_offset_ = phdr.p_offset; + if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) { + return false; + } + dynamic_vaddr_ = phdr.p_vaddr; if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { return false; } @@ -386,6 +414,20 @@ bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias return false; } +template +bool ElfInterface::GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address) { + if (symbols_.empty()) { + return false; + } + + for (const auto symbol : symbols_) { + if (symbol->GetGlobal(memory_, name, memory_address)) { + return true; + } + } + return false; +} + bool ElfInterface::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory, bool* finished) { // Adjust the load bias to get the real relative pc. @@ -451,6 +493,9 @@ template bool ElfInterface::GetFunctionNameWithTemplate(uint64_t, uin template bool ElfInterface::GetFunctionNameWithTemplate(uint64_t, uint64_t, std::string*, uint64_t*); +template bool ElfInterface::GetGlobalVariableWithTemplate(const std::string&, uint64_t*); +template bool ElfInterface::GetGlobalVariableWithTemplate(const std::string&, uint64_t*); + template void ElfInterface::GetMaxSizeWithTemplate(Memory*, uint64_t*); template void ElfInterface::GetMaxSizeWithTemplate(Memory*, uint64_t*); diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp new file mode 100644 index 000000000..100843931 --- /dev/null +++ b/libunwindstack/JitDebug.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2017 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 +#include + +#include +#include + +#include +#include +#include +#include + +// This implements the JIT Compilation Interface. +// See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html + +namespace unwindstack { + +struct JITCodeEntry32Pack { + uint32_t next; + uint32_t prev; + uint32_t symfile_addr; + uint64_t symfile_size; +} __attribute__((packed)); + +struct JITCodeEntry32Pad { + uint32_t next; + uint32_t prev; + uint32_t symfile_addr; + uint32_t pad; + uint64_t symfile_size; +}; + +struct JITCodeEntry64 { + uint64_t next; + uint64_t prev; + uint64_t symfile_addr; + uint64_t symfile_size; +}; + +struct JITDescriptorHeader { + uint32_t version; + uint32_t action_flag; +}; + +struct JITDescriptor32 { + JITDescriptorHeader header; + uint32_t relevant_entry; + uint32_t first_entry; +}; + +struct JITDescriptor64 { + JITDescriptorHeader header; + uint64_t relevant_entry; + uint64_t first_entry; +}; + +JitDebug::JitDebug(std::shared_ptr& memory) : memory_(memory) {} + +JitDebug::JitDebug(std::shared_ptr& memory, std::vector& search_libs) + : memory_(memory), search_libs_(search_libs) {} + +JitDebug::~JitDebug() { + for (auto* elf : elf_list_) { + delete elf; + } +} + +uint64_t JitDebug::ReadDescriptor32(uint64_t addr) { + JITDescriptor32 desc; + if (!memory_->ReadFully(addr, &desc, sizeof(desc))) { + return 0; + } + + if (desc.header.version != 1 || desc.first_entry == 0) { + // Either unknown version, or no jit entries. + return 0; + } + + return desc.first_entry; +} + +uint64_t JitDebug::ReadDescriptor64(uint64_t addr) { + JITDescriptor64 desc; + if (!memory_->ReadFully(addr, &desc, sizeof(desc))) { + return 0; + } + + if (desc.header.version != 1 || desc.first_entry == 0) { + // Either unknown version, or no jit entries. + return 0; + } + + return desc.first_entry; +} + +uint64_t JitDebug::ReadEntry32Pack(uint64_t* start, uint64_t* size) { + JITCodeEntry32Pack code; + if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) { + return 0; + } + + *start = code.symfile_addr; + *size = code.symfile_size; + return code.next; +} + +uint64_t JitDebug::ReadEntry32Pad(uint64_t* start, uint64_t* size) { + JITCodeEntry32Pad code; + if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) { + return 0; + } + + *start = code.symfile_addr; + *size = code.symfile_size; + return code.next; +} + +uint64_t JitDebug::ReadEntry64(uint64_t* start, uint64_t* size) { + JITCodeEntry64 code; + if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) { + return 0; + } + + *start = code.symfile_addr; + *size = code.symfile_size; + return code.next; +} + +void JitDebug::SetArch(ArchEnum arch) { + switch (arch) { + case ARCH_X86: + read_descriptor_func_ = &JitDebug::ReadDescriptor32; + read_entry_func_ = &JitDebug::ReadEntry32Pack; + break; + + case ARCH_ARM: + case ARCH_MIPS: + read_descriptor_func_ = &JitDebug::ReadDescriptor32; + read_entry_func_ = &JitDebug::ReadEntry32Pad; + break; + + 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(); + } +} + +void JitDebug::Init(Maps* maps) { + if (initialized_) { + return; + } + // Regardless of what happens below, consider the init finished. + initialized_ = true; + + std::string descriptor_name("__jit_debug_descriptor"); + uint64_t descriptor_addr = 0; + for (MapInfo* info : *maps) { + if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) { + continue; + } + + if (!search_libs_.empty()) { + bool found = false; + const char* lib = basename(info->name.c_str()); + for (std::string& name : search_libs_) { + if (strcmp(name.c_str(), lib) == 0) { + found = true; + break; + } + } + if (!found) { + continue; + } + } + + Elf* elf = info->GetElf(memory_, true); + if (elf->GetGlobalVariable(descriptor_name, &descriptor_addr)) { + descriptor_addr += info->start; + break; + } + } + + if (descriptor_addr == 0) { + return; + } + + entry_addr_ = (this->*read_descriptor_func_)(descriptor_addr); +} + +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 guard(lock_); + if (!initialized_) { + Init(maps); + } + + // Search the existing elf object first. + for (Elf* elf : elf_list_) { + if (elf->IsValidPc(pc)) { + return elf; + } + } + + while (entry_addr_ != 0) { + uint64_t start; + uint64_t size; + entry_addr_ = (this->*read_entry_func_)(&start, &size); + + Elf* elf = new Elf(new MemoryRange(memory_, start, size, 0)); + elf->Init(true); + 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); + + if (elf->IsValidPc(pc)) { + return elf; + } + } + return nullptr; +} + +} // namespace unwindstack diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp index 285f8790a..d4ba680a8 100644 --- a/libunwindstack/Memory.cpp +++ b/libunwindstack/Memory.cpp @@ -345,4 +345,26 @@ size_t MemoryOffline::Read(uint64_t addr, void* dst, size_t size) { return memory_->Read(addr, dst, size); } +MemoryOfflineParts::~MemoryOfflineParts() { + for (auto memory : memories_) { + delete memory; + } +} + +size_t MemoryOfflineParts::Read(uint64_t addr, void* dst, size_t size) { + if (memories_.empty()) { + return 0; + } + + // Do a read on each memory object, no support for reading across the + // different memory objects. + for (MemoryOffline* memory : memories_) { + size_t bytes = memory->Read(addr, dst, size); + if (bytes != 0) { + return bytes; + } + } + return 0; +} + } // namespace unwindstack diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp index 34f29bd95..d05c3e29d 100644 --- a/libunwindstack/RegsArm.cpp +++ b/libunwindstack/RegsArm.cpp @@ -37,10 +37,6 @@ ArchEnum RegsArm::Arch() { } uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) { - if (!elf->valid()) { - return rel_pc; - } - uint64_t load_bias = elf->GetLoadBias(); if (rel_pc < load_bias) { return rel_pc; diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp index b4b92d635..25def40c4 100644 --- a/libunwindstack/Symbols.cpp +++ b/libunwindstack/Symbols.cpp @@ -108,8 +108,35 @@ bool Symbols::GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std return return_value; } +template +bool Symbols::GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address) { + uint64_t cur_offset = offset_; + while (cur_offset + entry_size_ <= end_) { + SymType entry; + if (!elf_memory->ReadFully(cur_offset, &entry, sizeof(entry))) { + return false; + } + cur_offset += entry_size_; + + if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_OBJECT && + ELF32_ST_BIND(entry.st_info) == STB_GLOBAL) { + uint64_t str_offset = str_offset_ + entry.st_name; + if (str_offset < str_end_) { + std::string symbol; + if (elf_memory->ReadString(str_offset, &symbol, str_end_ - str_offset) && symbol == name) { + *memory_address = entry.st_value; + return true; + } + } + } + } + return false; +} + // Instantiate all of the needed template functions. template bool Symbols::GetName(uint64_t, uint64_t, Memory*, std::string*, uint64_t*); template bool Symbols::GetName(uint64_t, uint64_t, Memory*, std::string*, uint64_t*); +template bool Symbols::GetGlobal(Memory*, const std::string&, uint64_t*); +template bool Symbols::GetGlobal(Memory*, const std::string&, uint64_t*); } // namespace unwindstack diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h index 689144b6a..7d239c16b 100644 --- a/libunwindstack/Symbols.h +++ b/libunwindstack/Symbols.h @@ -47,6 +47,9 @@ class Symbols { bool GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name, uint64_t* func_offset); + template + bool GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address); + void ClearCache() { symbols_.clear(); cur_offset_ = offset_; diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp index a83f85b9c..b0a1c0c0b 100644 --- a/libunwindstack/Unwinder.cpp +++ b/libunwindstack/Unwinder.cpp @@ -27,12 +27,13 @@ #include #include +#include #include #include namespace unwindstack { -void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc) { +void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t func_pc) { size_t frame_num = frames_.size(); frames_.resize(frame_num + 1); FrameData* frame = &frames_.at(frame_num); @@ -53,7 +54,7 @@ void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc frame->map_flags = map_info->flags; frame->map_load_bias = elf->GetLoadBias(); - if (!elf->GetFunctionName(frame->rel_pc, &frame->function_name, &frame->function_offset)) { + if (!elf->GetFunctionName(func_pc, &frame->function_name, &frame->function_offset)) { frame->function_name = ""; frame->function_offset = 0; } @@ -79,17 +80,20 @@ void Unwinder::Unwind(const std::vector* initial_map_names_to_skip, bool return_address_attempt = false; bool adjust_pc = false; + std::unique_ptr jit_debug; for (; frames_.size() < max_frames_;) { uint64_t cur_pc = regs_->pc(); uint64_t cur_sp = regs_->sp(); MapInfo* map_info = maps_->Find(regs_->pc()); uint64_t rel_pc; + uint64_t adjusted_pc; uint64_t adjusted_rel_pc; Elf* elf; if (map_info == nullptr) { rel_pc = regs_->pc(); adjusted_rel_pc = rel_pc; + adjusted_pc = rel_pc; } else { if (ShouldStop(map_suffixes_to_ignore, map_info->name)) { break; @@ -97,16 +101,30 @@ void Unwinder::Unwind(const std::vector* initial_map_names_to_skip, elf = map_info->GetElf(process_memory_, true); rel_pc = elf->GetRelPc(regs_->pc(), map_info); if (adjust_pc) { - adjusted_rel_pc = regs_->GetAdjustedPc(rel_pc, elf); + adjusted_pc = regs_->GetAdjustedPc(rel_pc, elf); } else { - adjusted_rel_pc = rel_pc; + adjusted_pc = rel_pc; + } + adjusted_rel_pc = adjusted_pc; + + // If the pc is in an invalid elf file, try and get an Elf object + // using the jit debug information. + if (!elf->valid() && jit_debug_ != nullptr) { + uint64_t adjusted_jit_pc = regs_->pc() - (rel_pc - adjusted_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. + adjusted_pc = adjusted_jit_pc; + adjusted_rel_pc = adjusted_pc - map_info->start; + elf = jit_elf; + } } } if (map_info == nullptr || initial_map_names_to_skip == nullptr || std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(), basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) { - FillInFrame(map_info, elf, adjusted_rel_pc); + FillInFrame(map_info, elf, adjusted_rel_pc, adjusted_pc); // Once a frame is added, stop skipping frames. initial_map_names_to_skip = nullptr; @@ -134,7 +152,7 @@ void Unwinder::Unwind(const std::vector* initial_map_names_to_skip, in_device_map = true; } else { bool finished; - stepped = elf->Step(rel_pc, adjusted_rel_pc, map_info->elf_offset, regs_, + stepped = elf->Step(rel_pc, adjusted_pc, map_info->elf_offset, regs_, process_memory_.get(), &finished); if (stepped && finished) { break; @@ -174,13 +192,13 @@ std::string Unwinder::FormatFrame(size_t frame_num) { if (frame_num >= frames_.size()) { return ""; } - return FormatFrame(frames_[frame_num], regs_->Format32Bit()); + return FormatFrame(frames_[frame_num], regs_->Is32Bit()); } -std::string Unwinder::FormatFrame(const FrameData& frame, bool bits32) { +std::string Unwinder::FormatFrame(const FrameData& frame, bool is32bit) { std::string data; - if (bits32) { + if (is32bit) { data += android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame.num, frame.rel_pc); } else { data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc); @@ -208,4 +226,9 @@ std::string Unwinder::FormatFrame(const FrameData& frame, bool bits32) { return data; } +void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) { + jit_debug->SetArch(arch); + jit_debug_ = jit_debug; +} + } // namespace unwindstack diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h index a85e5f458..5f343916a 100644 --- a/libunwindstack/include/unwindstack/Elf.h +++ b/libunwindstack/include/unwindstack/Elf.h @@ -59,6 +59,8 @@ class Elf { bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset); + bool GetGlobalVariable(const std::string& name, uint64_t* memory_address); + uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info); bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs, @@ -68,6 +70,8 @@ class Elf { uint64_t GetLoadBias() { return load_bias_; } + bool IsValidPc(uint64_t pc); + bool valid() { return valid_; } uint32_t machine_type() { return machine_type_; } diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h index 5d3cd5ebd..faa61ee97 100644 --- a/libunwindstack/include/unwindstack/ElfInterface.h +++ b/libunwindstack/include/unwindstack/ElfInterface.h @@ -60,9 +60,13 @@ class ElfInterface { virtual bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name, uint64_t* offset) = 0; + virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0; + virtual bool Step(uint64_t rel_pc, uint64_t load_bias, Regs* regs, Memory* process_memory, bool* finished); + virtual bool IsValidPc(uint64_t pc); + Memory* CreateGnuDebugdataMemory(); Memory* memory() { return memory_; } @@ -72,6 +76,7 @@ class ElfInterface { void SetGnuDebugdataInterface(ElfInterface* interface) { gnu_debugdata_interface_ = interface; } uint64_t dynamic_offset() { return dynamic_offset_; } + uint64_t dynamic_vaddr() { return dynamic_vaddr_; } uint64_t dynamic_size() { return dynamic_size_; } uint64_t eh_frame_hdr_offset() { return eh_frame_hdr_offset_; } uint64_t eh_frame_hdr_size() { return eh_frame_hdr_size_; } @@ -108,6 +113,9 @@ class ElfInterface { bool GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, std::string* name, uint64_t* func_offset); + template + bool GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address); + virtual bool HandleType(uint64_t, uint32_t, uint64_t) { return false; } template @@ -118,6 +126,7 @@ class ElfInterface { // Stored elf data. uint64_t dynamic_offset_ = 0; + uint64_t dynamic_vaddr_ = 0; uint64_t dynamic_size_ = 0; uint64_t eh_frame_hdr_offset_ = 0; @@ -163,6 +172,10 @@ class ElfInterface32 : public ElfInterface { return ElfInterface::GetFunctionNameWithTemplate(addr, load_bias, name, func_offset); } + bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override { + return ElfInterface::GetGlobalVariableWithTemplate(name, memory_address); + } + static void GetMaxSize(Memory* memory, uint64_t* size) { GetMaxSizeWithTemplate(memory, size); } @@ -188,6 +201,10 @@ class ElfInterface64 : public ElfInterface { return ElfInterface::GetFunctionNameWithTemplate(addr, load_bias, name, func_offset); } + bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override { + return ElfInterface::GetGlobalVariableWithTemplate(name, memory_address); + } + static void GetMaxSize(Memory* memory, uint64_t* size) { GetMaxSizeWithTemplate(memory, size); } diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h new file mode 100644 index 000000000..0bcd0b0ca --- /dev/null +++ b/libunwindstack/include/unwindstack/JitDebug.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2017 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_JIT_DEBUG_H +#define _LIBUNWINDSTACK_JIT_DEBUG_H + +#include + +#include +#include +#include +#include + +namespace unwindstack { + +// Forward declarations. +class Elf; +class Maps; +class Memory; +enum ArchEnum : uint8_t; + +class JitDebug { + public: + explicit JitDebug(std::shared_ptr& memory); + JitDebug(std::shared_ptr& memory, std::vector& search_libs); + ~JitDebug(); + + Elf* GetElf(Maps* maps, uint64_t pc); + + void SetArch(ArchEnum arch); + + private: + void Init(Maps* maps); + + std::shared_ptr memory_; + uint64_t entry_addr_ = 0; + bool initialized_ = false; + std::vector elf_list_; + std::vector search_libs_; + + std::mutex lock_; + + 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); +}; + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_JIT_DEBUG_H diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h index 94ceaabf3..19bce0475 100644 --- a/libunwindstack/include/unwindstack/Memory.h +++ b/libunwindstack/include/unwindstack/Memory.h @@ -151,6 +151,19 @@ class MemoryOffline : public Memory { std::unique_ptr memory_; }; +class MemoryOfflineParts : public Memory { + public: + MemoryOfflineParts() = default; + virtual ~MemoryOfflineParts(); + + void Add(MemoryOffline* memory) { memories_.push_back(memory); } + + size_t Read(uint64_t addr, void* dst, size_t size) override; + + private: + std::vector memories_; +}; + } // namespace unwindstack #endif // _LIBUNWINDSTACK_MEMORY_H diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h index 613682ffd..1904d4daa 100644 --- a/libunwindstack/include/unwindstack/Regs.h +++ b/libunwindstack/include/unwindstack/Regs.h @@ -51,7 +51,7 @@ class Regs { virtual ArchEnum Arch() = 0; - virtual bool Format32Bit() = 0; + virtual bool Is32Bit() = 0; virtual void* RawData() = 0; virtual uint64_t pc() = 0; @@ -94,7 +94,7 @@ class RegsImpl : public Regs { void set_pc(AddressType pc) { pc_ = pc; } void set_sp(AddressType sp) { sp_ = sp; } - bool Format32Bit() override { return sizeof(AddressType) == sizeof(uint32_t); } + bool Is32Bit() override { return sizeof(AddressType) == sizeof(uint32_t); } inline AddressType& operator[](size_t reg) { return regs_[reg]; } diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h index b64d46036..5adec4f4b 100644 --- a/libunwindstack/include/unwindstack/Unwinder.h +++ b/libunwindstack/include/unwindstack/Unwinder.h @@ -32,6 +32,8 @@ namespace unwindstack { // Forward declarations. class Elf; +class JitDebug; +enum ArchEnum : uint8_t; struct FrameData { size_t num; @@ -67,16 +69,19 @@ class Unwinder { const std::vector& frames() { return frames_; } std::string FormatFrame(size_t frame_num); - static std::string FormatFrame(const FrameData& frame, bool bits32); + static std::string FormatFrame(const FrameData& frame, bool is32bit); + + void SetJitDebug(JitDebug* jit_debug, ArchEnum arch); private: - void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc); + void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t adjusted_pc); size_t max_frames_; Maps* maps_; Regs* regs_; std::vector frames_; std::shared_ptr process_memory_; + JitDebug* jit_debug_ = nullptr; }; } // namespace unwindstack diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp index 68de797f5..ae9da5eb4 100644 --- a/libunwindstack/tests/ElfFake.cpp +++ b/libunwindstack/tests/ElfFake.cpp @@ -43,6 +43,15 @@ bool ElfInterfaceFake::GetFunctionName(uint64_t, uint64_t, std::string* name, ui return true; } +bool ElfInterfaceFake::GetGlobalVariable(const std::string& global, uint64_t* offset) { + auto entry = globals_.find(global); + if (entry == globals_.end()) { + return false; + } + *offset = entry->second; + return true; +} + bool ElfInterfaceFake::Step(uint64_t, uint64_t, Regs* regs, Memory*, bool* finished) { if (steps_.empty()) { return false; diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h index abf992759..099026ce0 100644 --- a/libunwindstack/tests/ElfFake.h +++ b/libunwindstack/tests/ElfFake.h @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -55,6 +56,9 @@ class ElfFake : public Elf { void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; } void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); } + void FakeSetGnuDebugdataInterface(ElfInterface* interface) { + gnu_debugdata_interface_.reset(interface); + } }; class ElfInterfaceFake : public ElfInterface { @@ -67,9 +71,14 @@ class ElfInterfaceFake : public ElfInterface { bool GetSoname(std::string*) override { return false; } bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override; + bool GetGlobalVariable(const std::string&, uint64_t*) override; bool Step(uint64_t, uint64_t, Regs*, Memory*, bool*) override; + void FakeSetGlobalVariable(const std::string& global, uint64_t offset) { + globals_[global] = offset; + } + static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); } static void FakePushStepData(const StepData data) { steps_.push_back(data); } @@ -79,6 +88,8 @@ class ElfInterfaceFake : public ElfInterface { } private: + std::unordered_map globals_; + static std::deque functions_; static std::deque steps_; }; diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp index e138c3acf..042c5fb3b 100644 --- a/libunwindstack/tests/ElfInterfaceTest.cpp +++ b/libunwindstack/tests/ElfInterfaceTest.cpp @@ -958,4 +958,189 @@ TEST_F(ElfInterfaceTest, init_section_headers_offsets64) { InitSectionHeadersOffsets(); } +TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) { + std::unique_ptr elf(new ElfInterface32(&memory_)); + + Elf32_Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_phoff = 0x100; + ehdr.e_phnum = 1; + ehdr.e_phentsize = sizeof(Elf32_Phdr); + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + Elf32_Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_vaddr = 0; + phdr.p_memsz = 0x10000; + phdr.p_flags = PF_R | PF_X; + phdr.p_align = 0x1000; + memory_.SetMemory(0x100, &phdr, sizeof(phdr)); + + uint64_t load_bias = 0; + ASSERT_TRUE(elf->Init(&load_bias)); + EXPECT_EQ(0U, load_bias); + EXPECT_TRUE(elf->IsValidPc(0)); + EXPECT_TRUE(elf->IsValidPc(0x5000)); + EXPECT_TRUE(elf->IsValidPc(0xffff)); + EXPECT_FALSE(elf->IsValidPc(0x10000)); +} + +TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load_non_zero_load_bias) { + std::unique_ptr elf(new ElfInterface32(&memory_)); + + Elf32_Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_phoff = 0x100; + ehdr.e_phnum = 1; + ehdr.e_phentsize = sizeof(Elf32_Phdr); + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + Elf32_Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_vaddr = 0x2000; + phdr.p_memsz = 0x10000; + phdr.p_flags = PF_R | PF_X; + phdr.p_align = 0x1000; + memory_.SetMemory(0x100, &phdr, sizeof(phdr)); + + uint64_t load_bias = 0; + ASSERT_TRUE(elf->Init(&load_bias)); + EXPECT_EQ(0x2000U, load_bias); + EXPECT_FALSE(elf->IsValidPc(0)); + EXPECT_FALSE(elf->IsValidPc(0x1000)); + EXPECT_FALSE(elf->IsValidPc(0x1fff)); + EXPECT_TRUE(elf->IsValidPc(0x2000)); + EXPECT_TRUE(elf->IsValidPc(0x5000)); + EXPECT_TRUE(elf->IsValidPc(0x11fff)); + EXPECT_FALSE(elf->IsValidPc(0x12000)); +} + +TEST_F(ElfInterfaceTest, is_valid_pc_from_debug_frame) { + std::unique_ptr elf(new ElfInterface32(&memory_)); + + uint64_t sh_offset = 0x100; + + Elf32_Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_shstrndx = 1; + ehdr.e_shoff = sh_offset; + ehdr.e_shentsize = sizeof(Elf32_Shdr); + ehdr.e_shnum = 3; + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + Elf32_Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_NULL; + memory_.SetMemory(sh_offset, &shdr, sizeof(shdr)); + + sh_offset += sizeof(shdr); + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_STRTAB; + shdr.sh_name = 1; + shdr.sh_offset = 0x500; + shdr.sh_size = 0x100; + memory_.SetMemory(sh_offset, &shdr, sizeof(shdr)); + memory_.SetMemory(0x500, ".debug_frame"); + + sh_offset += sizeof(shdr); + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_PROGBITS; + shdr.sh_name = 0; + shdr.sh_addr = 0x600; + shdr.sh_offset = 0x600; + shdr.sh_size = 0x200; + memory_.SetMemory(sh_offset, &shdr, sizeof(shdr)); + + // CIE 32. + memory_.SetData32(0x600, 0xfc); + memory_.SetData32(0x604, 0xffffffff); + memory_.SetData8(0x608, 1); + memory_.SetData8(0x609, '\0'); + memory_.SetData8(0x60a, 0x4); + memory_.SetData8(0x60b, 0x4); + memory_.SetData8(0x60c, 0x1); + + // FDE 32. + memory_.SetData32(0x700, 0xfc); + memory_.SetData32(0x704, 0); + memory_.SetData32(0x708, 0x2100); + memory_.SetData32(0x70c, 0x200); + + uint64_t load_bias = 0; + ASSERT_TRUE(elf->Init(&load_bias)); + elf->InitHeaders(); + EXPECT_EQ(0U, load_bias); + EXPECT_FALSE(elf->IsValidPc(0)); + EXPECT_FALSE(elf->IsValidPc(0x20ff)); + EXPECT_TRUE(elf->IsValidPc(0x2100)); + EXPECT_TRUE(elf->IsValidPc(0x2200)); + EXPECT_TRUE(elf->IsValidPc(0x22ff)); + EXPECT_FALSE(elf->IsValidPc(0x2300)); +} + +TEST_F(ElfInterfaceTest, is_valid_pc_from_eh_frame) { + std::unique_ptr elf(new ElfInterface32(&memory_)); + + uint64_t sh_offset = 0x100; + + Elf32_Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_shstrndx = 1; + ehdr.e_shoff = sh_offset; + ehdr.e_shentsize = sizeof(Elf32_Shdr); + ehdr.e_shnum = 3; + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + Elf32_Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_NULL; + memory_.SetMemory(sh_offset, &shdr, sizeof(shdr)); + + sh_offset += sizeof(shdr); + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_STRTAB; + shdr.sh_name = 1; + shdr.sh_offset = 0x500; + shdr.sh_size = 0x100; + memory_.SetMemory(sh_offset, &shdr, sizeof(shdr)); + memory_.SetMemory(0x500, ".eh_frame"); + + sh_offset += sizeof(shdr); + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_PROGBITS; + shdr.sh_name = 0; + shdr.sh_addr = 0x600; + shdr.sh_offset = 0x600; + shdr.sh_size = 0x200; + memory_.SetMemory(sh_offset, &shdr, sizeof(shdr)); + + // CIE 32. + memory_.SetData32(0x600, 0xfc); + memory_.SetData32(0x604, 0); + memory_.SetData8(0x608, 1); + memory_.SetData8(0x609, '\0'); + memory_.SetData8(0x60a, 0x4); + memory_.SetData8(0x60b, 0x4); + memory_.SetData8(0x60c, 0x1); + + // FDE 32. + memory_.SetData32(0x700, 0xfc); + memory_.SetData32(0x704, 0x104); + memory_.SetData32(0x708, 0x20f8); + memory_.SetData32(0x70c, 0x200); + + uint64_t load_bias = 0; + ASSERT_TRUE(elf->Init(&load_bias)); + elf->InitHeaders(); + EXPECT_EQ(0U, load_bias); + EXPECT_FALSE(elf->IsValidPc(0)); + EXPECT_FALSE(elf->IsValidPc(0x27ff)); + EXPECT_TRUE(elf->IsValidPc(0x2800)); + EXPECT_TRUE(elf->IsValidPc(0x2900)); + EXPECT_TRUE(elf->IsValidPc(0x29ff)); + EXPECT_FALSE(elf->IsValidPc(0x2a00)); +} + } // namespace unwindstack diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp index 5e808ef56..7e6a62a32 100644 --- a/libunwindstack/tests/ElfTest.cpp +++ b/libunwindstack/tests/ElfTest.cpp @@ -346,7 +346,14 @@ class ElfInterfaceMock : public ElfInterface { void InitHeaders() override {} bool GetSoname(std::string*) override { return false; } bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override { return false; } + MOCK_METHOD5(Step, bool(uint64_t, uint64_t, Regs*, Memory*, bool*)); + MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*)); + MOCK_METHOD1(IsValidPc, bool(uint64_t)); + + void MockSetDynamicOffset(uint64_t offset) { dynamic_offset_ = offset; } + void MockSetDynamicVaddr(uint64_t vaddr) { dynamic_vaddr_ = vaddr; } + void MockSetDynamicSize(uint64_t size) { dynamic_size_ = size; } }; TEST_F(ElfTest, step_in_interface) { @@ -378,14 +385,200 @@ TEST_F(ElfTest, step_in_interface_non_zero_load_bias) { elf.FakeSetInterface(interface); MemoryFake process_memory; - // Invalid relative pc given load_bias. bool finished; - ASSERT_FALSE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished)); - EXPECT_CALL(*interface, Step(0x7300, 0x4000, ®s, &process_memory, &finished)) .WillOnce(::testing::Return(true)); ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, ®s, &process_memory, &finished)); } +TEST_F(ElfTest, get_global_invalid_elf) { + ElfFake elf(memory_); + elf.FakeSetValid(false); + + std::string global("something"); + uint64_t offset; + ASSERT_FALSE(elf.GetGlobalVariable(global, &offset)); +} + +TEST_F(ElfTest, get_global_valid_not_in_interface) { + ElfFake elf(memory_); + elf.FakeSetValid(true); + elf.FakeSetLoadBias(0); + + ElfInterfaceMock* interface = new ElfInterfaceMock(memory_); + elf.FakeSetInterface(interface); + + uint64_t offset; + std::string global("something"); + EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)).WillOnce(::testing::Return(false)); + + ASSERT_FALSE(elf.GetGlobalVariable(global, &offset)); +} + +TEST_F(ElfTest, get_global_valid_below_load_bias) { + ElfFake elf(memory_); + elf.FakeSetValid(true); + elf.FakeSetLoadBias(0x1000); + + ElfInterfaceMock* interface = new ElfInterfaceMock(memory_); + elf.FakeSetInterface(interface); + + uint64_t offset; + std::string global("something"); + EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)) + .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true))); + + ASSERT_FALSE(elf.GetGlobalVariable(global, &offset)); +} + +TEST_F(ElfTest, get_global_valid_dynamic_zero) { + ElfFake elf(memory_); + elf.FakeSetValid(true); + elf.FakeSetLoadBias(0); + + ElfInterfaceMock* interface = new ElfInterfaceMock(memory_); + elf.FakeSetInterface(interface); + + ElfInterfaceMock* gnu_interface = new ElfInterfaceMock(memory_); + elf.FakeSetGnuDebugdataInterface(gnu_interface); + + uint64_t offset; + std::string global("something"); + EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)).WillOnce(::testing::Return(false)); + + EXPECT_CALL(*gnu_interface, GetGlobalVariable(global, &offset)) + .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x500), ::testing::Return(true))); + + ASSERT_TRUE(elf.GetGlobalVariable(global, &offset)); + EXPECT_EQ(0x500U, offset); +} + +TEST_F(ElfTest, get_global_valid_in_gnu_debugdata_dynamic_zero) { + ElfFake elf(memory_); + elf.FakeSetValid(true); + elf.FakeSetLoadBias(0); + + ElfInterfaceMock* interface = new ElfInterfaceMock(memory_); + elf.FakeSetInterface(interface); + + uint64_t offset; + std::string global("something"); + EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)) + .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true))); + + ASSERT_TRUE(elf.GetGlobalVariable(global, &offset)); + EXPECT_EQ(0x300U, offset); +} + +TEST_F(ElfTest, get_global_valid_dynamic_zero_non_zero_load_bias) { + ElfFake elf(memory_); + elf.FakeSetValid(true); + elf.FakeSetLoadBias(0x100); + + ElfInterfaceMock* interface = new ElfInterfaceMock(memory_); + elf.FakeSetInterface(interface); + + uint64_t offset; + std::string global("something"); + EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)) + .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true))); + + ASSERT_TRUE(elf.GetGlobalVariable(global, &offset)); + EXPECT_EQ(0x200U, offset); +} + +TEST_F(ElfTest, get_global_valid_dynamic_adjust_negative) { + ElfFake elf(memory_); + elf.FakeSetValid(true); + elf.FakeSetLoadBias(0); + + ElfInterfaceMock* interface = new ElfInterfaceMock(memory_); + interface->MockSetDynamicOffset(0x400); + interface->MockSetDynamicVaddr(0x800); + interface->MockSetDynamicSize(0x100); + elf.FakeSetInterface(interface); + + uint64_t offset; + std::string global("something"); + EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)) + .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x850), ::testing::Return(true))); + + ASSERT_TRUE(elf.GetGlobalVariable(global, &offset)); + EXPECT_EQ(0x450U, offset); +} + +TEST_F(ElfTest, get_global_valid_dynamic_adjust_positive) { + ElfFake elf(memory_); + elf.FakeSetValid(true); + elf.FakeSetLoadBias(0); + + ElfInterfaceMock* interface = new ElfInterfaceMock(memory_); + interface->MockSetDynamicOffset(0x1000); + interface->MockSetDynamicVaddr(0x800); + interface->MockSetDynamicSize(0x100); + elf.FakeSetInterface(interface); + + uint64_t offset; + std::string global("something"); + EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)) + .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x850), ::testing::Return(true))); + + ASSERT_TRUE(elf.GetGlobalVariable(global, &offset)); + EXPECT_EQ(0x1050U, offset); +} + +TEST_F(ElfTest, is_valid_pc_elf_invalid) { + ElfFake elf(memory_); + elf.FakeSetValid(false); + elf.FakeSetLoadBias(0); + + EXPECT_FALSE(elf.IsValidPc(0x100)); + EXPECT_FALSE(elf.IsValidPc(0x200)); +} + +TEST_F(ElfTest, is_valid_pc_interface) { + ElfFake elf(memory_); + elf.FakeSetValid(true); + elf.FakeSetLoadBias(0); + + ElfInterfaceMock* interface = new ElfInterfaceMock(memory_); + elf.FakeSetInterface(interface); + + EXPECT_CALL(*interface, IsValidPc(0x1500)).WillOnce(::testing::Return(true)); + + EXPECT_TRUE(elf.IsValidPc(0x1500)); +} + +TEST_F(ElfTest, is_valid_pc_non_zero_load_bias) { + ElfFake elf(memory_); + elf.FakeSetValid(true); + elf.FakeSetLoadBias(0x1000); + + ElfInterfaceMock* interface = new ElfInterfaceMock(memory_); + elf.FakeSetInterface(interface); + + EXPECT_CALL(*interface, IsValidPc(0x500)).WillOnce(::testing::Return(true)); + + EXPECT_FALSE(elf.IsValidPc(0x100)); + EXPECT_FALSE(elf.IsValidPc(0x200)); + EXPECT_TRUE(elf.IsValidPc(0x1500)); +} + +TEST_F(ElfTest, is_valid_pc_from_gnu_debugdata) { + ElfFake elf(memory_); + elf.FakeSetValid(true); + elf.FakeSetLoadBias(0); + + ElfInterfaceMock* interface = new ElfInterfaceMock(memory_); + elf.FakeSetInterface(interface); + ElfInterfaceMock* gnu_interface = new ElfInterfaceMock(memory_); + elf.FakeSetGnuDebugdataInterface(gnu_interface); + + EXPECT_CALL(*interface, IsValidPc(0x1500)).WillOnce(::testing::Return(false)); + EXPECT_CALL(*gnu_interface, IsValidPc(0x1500)).WillOnce(::testing::Return(true)); + + EXPECT_TRUE(elf.IsValidPc(0x1500)); +} + } // namespace unwindstack diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp new file mode 100644 index 000000000..83d7a4952 --- /dev/null +++ b/libunwindstack/tests/JitDebugTest.cpp @@ -0,0 +1,377 @@ +/* + * 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 +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "ElfFake.h" +#include "MemoryFake.h" + +namespace unwindstack { + +class JitDebugTest : public ::testing::Test { + protected: + void SetUp() override { + memory_ = new MemoryFake; + process_memory_.reset(memory_); + + jit_debug_.reset(new JitDebug(process_memory_)); + jit_debug_->SetArch(ARCH_ARM); + + maps_.reset( + new BufferMaps("1000-4000 ---s 00000000 00:00 0\n" + "4000-6000 r--s 00000000 00:00 0\n" + "6000-8000 -w-s 00000000 00:00 0\n" + "a000-c000 --xp 00000000 00:00 0\n" + "c000-f000 rwxp 00000000 00:00 0\n" + "f000-11000 r-xp 00000000 00:00 0\n" + "100000-110000 rw-p 0000000 00:00 0\n" + "200000-210000 rw-p 0000000 00:00 0\n")); + ASSERT_TRUE(maps_->Parse()); + + MapInfo* map_info = maps_->Get(3); + ASSERT_TRUE(map_info != nullptr); + elf_memories_.push_back(new MemoryFake); + ElfFake* elf = new ElfFake(elf_memories_.back()); + elf->FakeSetValid(true); + ElfInterfaceFake* interface = new ElfInterfaceFake(elf_memories_.back()); + elf->FakeSetInterface(interface); + interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800); + map_info->elf = elf; + + map_info = maps_->Get(5); + ASSERT_TRUE(map_info != nullptr); + elf_memories_.push_back(new MemoryFake); + elf = new ElfFake(elf_memories_.back()); + elf->FakeSetValid(true); + interface = new ElfInterfaceFake(elf_memories_.back()); + elf->FakeSetInterface(interface); + interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800); + map_info->elf = elf; + } + + template + void CreateElf(uint64_t offset, uint8_t class_type, uint8_t machine_type, uint32_t pc, + uint32_t size) { + EhdrType ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + uint64_t sh_offset = sizeof(ehdr); + memcpy(ehdr.e_ident, ELFMAG, SELFMAG); + ehdr.e_ident[EI_CLASS] = class_type; + ehdr.e_machine = machine_type; + ehdr.e_shstrndx = 1; + ehdr.e_shoff = sh_offset; + ehdr.e_shentsize = sizeof(ShdrType); + ehdr.e_shnum = 3; + memory_->SetMemory(offset, &ehdr, sizeof(ehdr)); + + ShdrType shdr; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_NULL; + memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr)); + + sh_offset += sizeof(shdr); + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_STRTAB; + shdr.sh_name = 1; + shdr.sh_offset = 0x500; + shdr.sh_size = 0x100; + memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr)); + memory_->SetMemory(offset + 0x500, ".debug_frame"); + + sh_offset += sizeof(shdr); + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_PROGBITS; + shdr.sh_name = 0; + shdr.sh_addr = 0x600; + shdr.sh_offset = 0x600; + shdr.sh_size = 0x200; + memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr)); + + // Now add a single cie/fde. + uint64_t dwarf_offset = offset + 0x600; + if (class_type == ELFCLASS32) { + // CIE 32 information. + memory_->SetData32(dwarf_offset, 0xfc); + memory_->SetData32(dwarf_offset + 0x4, 0xffffffff); + memory_->SetData8(dwarf_offset + 0x8, 1); + memory_->SetData8(dwarf_offset + 0x9, '\0'); + memory_->SetData8(dwarf_offset + 0xa, 0x4); + memory_->SetData8(dwarf_offset + 0xb, 0x4); + memory_->SetData8(dwarf_offset + 0xc, 0x1); + + // FDE 32 information. + memory_->SetData32(dwarf_offset + 0x100, 0xfc); + memory_->SetData32(dwarf_offset + 0x104, 0); + memory_->SetData32(dwarf_offset + 0x108, pc); + memory_->SetData32(dwarf_offset + 0x10c, size); + } else { + // CIE 64 information. + memory_->SetData32(dwarf_offset, 0xffffffff); + memory_->SetData64(dwarf_offset + 4, 0xf4); + memory_->SetData64(dwarf_offset + 0xc, 0xffffffffffffffffULL); + memory_->SetData8(dwarf_offset + 0x14, 1); + memory_->SetData8(dwarf_offset + 0x15, '\0'); + memory_->SetData8(dwarf_offset + 0x16, 0x4); + memory_->SetData8(dwarf_offset + 0x17, 0x4); + memory_->SetData8(dwarf_offset + 0x18, 0x1); + + // FDE 64 information. + memory_->SetData32(dwarf_offset + 0x100, 0xffffffff); + memory_->SetData64(dwarf_offset + 0x104, 0xf4); + memory_->SetData64(dwarf_offset + 0x10c, 0); + memory_->SetData64(dwarf_offset + 0x114, pc); + memory_->SetData64(dwarf_offset + 0x11c, size); + } + } + + void WriteDescriptor32(uint64_t addr, uint32_t entry); + void WriteDescriptor64(uint64_t addr, uint64_t entry); + void WriteEntry32Pack(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr, + uint64_t elf_size); + void WriteEntry32Pad(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr, + uint64_t elf_size); + void WriteEntry64(uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr, + uint64_t elf_size); + + std::shared_ptr process_memory_; + MemoryFake* memory_; + std::vector elf_memories_; + std::unique_ptr jit_debug_; + std::unique_ptr maps_; +}; + +void JitDebugTest::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 JitDebugTest::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 JitDebugTest::WriteEntry32Pack(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr, + uint64_t elf_size) { + // Format of the 32 bit JITCodeEntry structure: + // uint32_t next + memory_->SetData32(addr, next); + // uint32_t prev + memory_->SetData32(addr + 4, prev); + // uint32_t symfile_addr + memory_->SetData32(addr + 8, elf_addr); + // uint64_t symfile_size + memory_->SetData64(addr + 12, elf_size); +} + +void JitDebugTest::WriteEntry32Pad(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr, + uint64_t elf_size) { + // Format of the 32 bit JITCodeEntry structure: + // uint32_t next + memory_->SetData32(addr, next); + // uint32_t prev + memory_->SetData32(addr + 4, prev); + // uint32_t symfile_addr + memory_->SetData32(addr + 8, elf_addr); + // uint32_t pad + memory_->SetData32(addr + 12, 0); + // uint64_t symfile_size + memory_->SetData64(addr + 16, elf_size); +} + +void JitDebugTest::WriteEntry64(uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr, + uint64_t elf_size) { + // Format of the 64 bit JITCodeEntry structure: + // uint64_t next + memory_->SetData64(addr, next); + // uint64_t prev + memory_->SetData64(addr + 8, prev); + // uint64_t symfile_addr + memory_->SetData64(addr + 16, elf_addr); + // uint64_t symfile_size + memory_->SetData64(addr + 24, elf_size); +} + +TEST_F(JitDebugTest, get_elf_invalid) { + 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_->GetElf(maps_.get(), 0x1500); + ASSERT_TRUE(elf == nullptr); +} + +TEST_F(JitDebugTest, get_elf_no_valid_descriptor_in_memory) { + CreateElf(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200); + + Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500); + ASSERT_TRUE(elf == nullptr); +} + +TEST_F(JitDebugTest, get_elf_no_valid_code_entry) { + CreateElf(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200); + + WriteDescriptor32(0xf800, 0x200000); + + Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500); + ASSERT_TRUE(elf == nullptr); +} + +TEST_F(JitDebugTest, get_elf_invalid_descriptor_first_entry) { + CreateElf(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200); + + WriteDescriptor32(0xf800, 0); + + Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500); + ASSERT_TRUE(elf == nullptr); +} + +TEST_F(JitDebugTest, get_elf_invalid_descriptor_version) { + CreateElf(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200); + + WriteDescriptor32(0xf800, 0x20000); + // Set the version to an invalid value. + memory_->SetData32(0xf800, 2); + + Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500); + ASSERT_TRUE(elf == nullptr); +} + +TEST_F(JitDebugTest, get_elf_32) { + CreateElf(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200); + + WriteDescriptor32(0xf800, 0x200000); + WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000); + + 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(); + Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500); + ASSERT_TRUE(elf2 != nullptr); + EXPECT_EQ(elf, elf2); +} + +TEST_F(JitDebugTest, get_elf_x86) { + CreateElf(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200); + + WriteDescriptor32(0xf800, 0x200000); + WriteEntry32Pack(0x200000, 0, 0, 0x4000, 0x1000); + + 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(); + Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500); + ASSERT_TRUE(elf2 != nullptr); + EXPECT_EQ(elf, elf2); +} + +TEST_F(JitDebugTest, get_elf_64) { + CreateElf(0x4000, ELFCLASS64, EM_AARCH64, 0x1500, 0x200); + + WriteDescriptor64(0xf800, 0x200000); + WriteEntry64(0x200000, 0, 0, 0x4000, 0x1000); + + jit_debug_->SetArch(ARCH_ARM64); + 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(); + Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500); + ASSERT_TRUE(elf2 != nullptr); + EXPECT_EQ(elf, elf2); +} + +TEST_F(JitDebugTest, get_elf_multiple_entries) { + CreateElf(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200); + CreateElf(0x5000, ELFCLASS32, EM_ARM, 0x2300, 0x400); + + WriteDescriptor32(0xf800, 0x200000); + WriteEntry32Pad(0x200000, 0, 0x200100, 0x4000, 0x1000); + WriteEntry32Pad(0x200100, 0x200100, 0, 0x5000, 0x1000); + + Elf* elf_2 = jit_debug_->GetElf(maps_.get(), 0x2400); + ASSERT_TRUE(elf_2 != nullptr); + + 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(); + 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) { + CreateElf(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200); + + WriteDescriptor32(0xf800, 0x200000); + WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000); + + // Only search a given named list of libs. + std::vector libs{"libart.so"}; + 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"; + 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(); + jit_debug_->SetArch(ARCH_ARM); + EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr); +} + +} // namespace unwindstack diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h index b81b2ca5d..8f7d91349 100644 --- a/libunwindstack/tests/RegsFake.h +++ b/libunwindstack/tests/RegsFake.h @@ -45,7 +45,7 @@ class RegsFake : public Regs { void IterateRegisters(std::function) override {} - bool Format32Bit() { return false; } + bool Is32Bit() { return false; } uint64_t GetAdjustedPc(uint64_t rel_pc, Elf*) override { return rel_pc - 2; } diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp index 3f848909b..15d54586d 100644 --- a/libunwindstack/tests/RegsTest.cpp +++ b/libunwindstack/tests/RegsTest.cpp @@ -188,7 +188,7 @@ TEST_F(RegsTest, elf_invalid) { regs_arm.set_pc(0x1500); EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info)); - EXPECT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, invalid_elf)); + EXPECT_EQ(0x4fcU, regs_arm.GetAdjustedPc(0x500U, invalid_elf)); regs_arm64.set_pc(0x1600); EXPECT_EQ(0x600U, invalid_elf->GetRelPc(regs_arm64.pc(), &map_info)); diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp index da258a6c9..45a7b58a0 100644 --- a/libunwindstack/tests/SymbolsTest.cpp +++ b/libunwindstack/tests/SymbolsTest.cpp @@ -330,9 +330,69 @@ TYPED_TEST_P(SymbolsTest, symtab_read_cached) { ASSERT_EQ(3U, func_offset); } +TYPED_TEST_P(SymbolsTest, get_global) { + uint64_t start_offset = 0x1000; + uint64_t str_offset = 0xa000; + Symbols symbols(start_offset, 4 * sizeof(TypeParam), sizeof(TypeParam), str_offset, 0x1000); + + TypeParam sym; + memset(&sym, 0, sizeof(sym)); + sym.st_shndx = SHN_COMMON; + sym.st_info = STT_OBJECT | (STB_GLOBAL << 4); + sym.st_name = 0x100; + this->memory_.SetMemory(start_offset, &sym, sizeof(sym)); + this->memory_.SetMemory(str_offset + 0x100, "global_0"); + + start_offset += sizeof(sym); + memset(&sym, 0, sizeof(sym)); + sym.st_shndx = SHN_COMMON; + sym.st_info = STT_FUNC; + sym.st_name = 0x200; + sym.st_value = 0x10000; + sym.st_size = 0x100; + this->memory_.SetMemory(start_offset, &sym, sizeof(sym)); + this->memory_.SetMemory(str_offset + 0x200, "function_0"); + + start_offset += sizeof(sym); + memset(&sym, 0, sizeof(sym)); + sym.st_shndx = SHN_COMMON; + sym.st_info = STT_OBJECT | (STB_GLOBAL << 4); + sym.st_name = 0x300; + this->memory_.SetMemory(start_offset, &sym, sizeof(sym)); + this->memory_.SetMemory(str_offset + 0x300, "global_1"); + + start_offset += sizeof(sym); + memset(&sym, 0, sizeof(sym)); + sym.st_shndx = SHN_COMMON; + sym.st_info = STT_FUNC; + sym.st_name = 0x400; + sym.st_value = 0x12000; + sym.st_size = 0x100; + this->memory_.SetMemory(start_offset, &sym, sizeof(sym)); + this->memory_.SetMemory(str_offset + 0x400, "function_1"); + + uint64_t offset; + EXPECT_TRUE(symbols.GetGlobal(&this->memory_, "global_0", &offset)); + EXPECT_TRUE(symbols.GetGlobal(&this->memory_, "global_1", &offset)); + EXPECT_TRUE(symbols.GetGlobal(&this->memory_, "global_0", &offset)); + EXPECT_TRUE(symbols.GetGlobal(&this->memory_, "global_1", &offset)); + + EXPECT_FALSE(symbols.GetGlobal(&this->memory_, "function_0", &offset)); + EXPECT_FALSE(symbols.GetGlobal(&this->memory_, "function_1", &offset)); + + std::string name; + EXPECT_TRUE(symbols.GetName(0x10002, 0, &this->memory_, &name, &offset)); + EXPECT_EQ("function_0", name); + EXPECT_EQ(2U, offset); + + EXPECT_TRUE(symbols.GetName(0x12004, 0, &this->memory_, &name, &offset)); + EXPECT_EQ("function_1", name); + EXPECT_EQ(4U, offset); +} + REGISTER_TYPED_TEST_CASE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries, multiple_entries_nonstandard_size, load_bias, symtab_value_out_of_bounds, - symtab_read_cached); + symtab_read_cached, get_global); typedef ::testing::Types SymbolsTestTypes; INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes); diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp index 8f2803690..921620444 100644 --- a/libunwindstack/tests/UnwindOfflineTest.cpp +++ b/libunwindstack/tests/UnwindOfflineTest.cpp @@ -25,14 +25,17 @@ #include #include +#include #include #include #include #include +#include #include #include "MachineArm.h" #include "MachineArm64.h" +#include "MachineX86.h" #include "ElfTestUtils.h" @@ -92,7 +95,7 @@ TEST(UnwindOfflineTest, pc_straddle_arm32) { " #00 pc 0001a9f8 libc.so (abort+63)\n" " #01 pc 00006a1b libbase.so (_ZN7android4base14DefaultAborterEPKc+6)\n" " #02 pc 00007441 libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n" - " #03 pc 00015149 /does/not/exist/libhidlbase.so\n", + " #03 pc 00015147 /does/not/exist/libhidlbase.so\n", frame_info); } @@ -200,4 +203,207 @@ TEST(UnwindOfflineTest, pc_straddle_arm64) { frame_info); } +static void AddMemory(std::string file_name, MemoryOfflineParts* parts) { + MemoryOffline* memory = new MemoryOffline; + ASSERT_TRUE(memory->Init(file_name.c_str(), 0)); + parts->Add(memory); +} + +TEST(UnwindOfflineTest, jit_debug_x86_32) { + std::string dir(TestGetFileDirectory() + "offline/jit_debug_x86_32/"); + + MemoryOfflineParts* memory = new MemoryOfflineParts; + AddMemory(dir + "descriptor.data", memory); + AddMemory(dir + "stack.data", memory); + for (size_t i = 0; i < 7; i++) { + AddMemory(dir + "entry" + std::to_string(i) + ".data", memory); + AddMemory(dir + "jit" + std::to_string(i) + ".data", memory); + } + + FILE* fp = fopen((dir + "regs.txt").c_str(), "r"); + ASSERT_TRUE(fp != nullptr); + RegsX86 regs; + uint64_t reg_value; + ASSERT_EQ(1, fscanf(fp, "eax: %" SCNx64 "\n", ®_value)); + regs[X86_REG_EAX] = reg_value; + ASSERT_EQ(1, fscanf(fp, "ebx: %" SCNx64 "\n", ®_value)); + regs[X86_REG_EBX] = reg_value; + ASSERT_EQ(1, fscanf(fp, "ecx: %" SCNx64 "\n", ®_value)); + regs[X86_REG_ECX] = reg_value; + ASSERT_EQ(1, fscanf(fp, "edx: %" SCNx64 "\n", ®_value)); + regs[X86_REG_EDX] = reg_value; + ASSERT_EQ(1, fscanf(fp, "ebp: %" SCNx64 "\n", ®_value)); + regs[X86_REG_EBP] = reg_value; + ASSERT_EQ(1, fscanf(fp, "edi: %" SCNx64 "\n", ®_value)); + regs[X86_REG_EDI] = reg_value; + ASSERT_EQ(1, fscanf(fp, "esi: %" SCNx64 "\n", ®_value)); + regs[X86_REG_ESI] = reg_value; + ASSERT_EQ(1, fscanf(fp, "esp: %" SCNx64 "\n", ®_value)); + regs[X86_REG_ESP] = reg_value; + ASSERT_EQ(1, fscanf(fp, "eip: %" SCNx64 "\n", ®_value)); + regs[X86_REG_EIP] = reg_value; + regs.SetFromRaw(); + fclose(fp); + + fp = fopen((dir + "maps.txt").c_str(), "r"); + ASSERT_TRUE(fp != nullptr); + // The file is guaranteed to be less than 4096 bytes. + std::vector buffer(4096); + ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp)); + fclose(fp); + + BufferMaps maps(buffer.data()); + ASSERT_TRUE(maps.Parse()); + + ASSERT_EQ(ARCH_X86, regs.Arch()); + + std::shared_ptr process_memory(memory); + + char* cwd = getcwd(nullptr, 0); + ASSERT_EQ(0, chdir(dir.c_str())); + JitDebug jit_debug(process_memory); + Unwinder unwinder(128, &maps, ®s, process_memory); + unwinder.SetJitDebug(&jit_debug, regs.Arch()); + unwinder.Unwind(); + ASSERT_EQ(0, chdir(cwd)); + free(cwd); + + std::string frame_info(DumpFrames(unwinder)); + ASSERT_EQ(69U, unwinder.NumFrames()) << "Unwind:\n" << frame_info; + EXPECT_EQ( + " #00 pc 00068fb8 libarttestd.so (_ZN3artL13CauseSegfaultEv+72)\n" + " #01 pc 00067f00 libarttestd.so (Java_Main_unwindInProcess+10032)\n" + " #02 pc 000021a8 (offset 0x2000) 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, " + "boolean)+136)\n" + " #03 pc 0000fe81 anonymous:ee74c000 (boolean Main.bar(boolean)+65)\n" + " #04 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n" + " #05 pc 00146ab5 libartd.so " + "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n" + " #06 pc 0039cf0d libartd.so " + "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_" + "11ShadowFrameEtPNS_6JValueE+653)\n" + " #07 pc 00392552 libartd.so " + "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_" + "6JValueEb+354)\n" + " #08 pc 0039399a libartd.so " + "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_" + "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n" + " #09 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n" + " #10 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n" + " #11 pc 0000fe04 anonymous:ee74c000 (int Main.compare(Main, Main)+52)\n" + " #12 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n" + " #13 pc 00146ab5 libartd.so " + "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n" + " #14 pc 0039cf0d libartd.so " + "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_" + "11ShadowFrameEtPNS_6JValueE+653)\n" + " #15 pc 00392552 libartd.so " + "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_" + "6JValueEb+354)\n" + " #16 pc 0039399a libartd.so " + "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_" + "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n" + " #17 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n" + " #18 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n" + " #19 pc 0000fd3c anonymous:ee74c000 (int Main.compare(java.lang.Object, " + "java.lang.Object)+108)\n" + " #20 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n" + " #21 pc 00146ab5 libartd.so " + "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n" + " #22 pc 0039cf0d libartd.so " + "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_" + "11ShadowFrameEtPNS_6JValueE+653)\n" + " #23 pc 00392552 libartd.so " + "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_" + "6JValueEb+354)\n" + " #24 pc 0039399a libartd.so " + "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_" + "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n" + " #25 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n" + " #26 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n" + " #27 pc 0000fbdc anonymous:ee74c000 (int " + "java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, " + "java.util.Comparator)+332)\n" + " #28 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n" + " #29 pc 00146acb libartd.so " + "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n" + " #30 pc 0039cf0d libartd.so " + "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_" + "11ShadowFrameEtPNS_6JValueE+653)\n" + " #31 pc 00392552 libartd.so " + "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_" + "6JValueEb+354)\n" + " #32 pc 0039399a libartd.so " + "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_" + "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n" + " #33 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n" + " #34 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n" + " #35 pc 0000f625 anonymous:ee74c000 (boolean Main.foo()+165)\n" + " #36 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n" + " #37 pc 00146ab5 libartd.so " + "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n" + " #38 pc 0039cf0d libartd.so " + "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_" + "11ShadowFrameEtPNS_6JValueE+653)\n" + " #39 pc 00392552 libartd.so " + "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_" + "6JValueEb+354)\n" + " #40 pc 0039399a libartd.so " + "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_" + "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n" + " #41 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n" + " #42 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n" + " #43 pc 0000eedc anonymous:ee74c000 (void Main.runPrimary()+60)\n" + " #44 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n" + " #45 pc 00146ab5 libartd.so " + "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n" + " #46 pc 0039cf0d libartd.so " + "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_" + "11ShadowFrameEtPNS_6JValueE+653)\n" + " #47 pc 00392552 libartd.so " + "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_" + "6JValueEb+354)\n" + " #48 pc 0039399a libartd.so " + "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_" + "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n" + " #49 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n" + " #50 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n" + " #51 pc 0000ac22 anonymous:ee74c000 (void Main.main(java.lang.String[])+98)\n" + " #52 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n" + " #53 pc 00146acb libartd.so " + "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n" + " #54 pc 0039cf0d libartd.so " + "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_" + "11ShadowFrameEtPNS_6JValueE+653)\n" + " #55 pc 00392552 libartd.so " + "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_" + "6JValueEb+354)\n" + " #56 pc 0039399a libartd.so " + "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_" + "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n" + " #57 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n" + " #58 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n" + " #59 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n" + " #60 pc 00146acb libartd.so " + "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n" + " #61 pc 005aac95 libartd.so " + "(_ZN3artL18InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_9ArtMethodEPNS_" + "8ArgArrayEPNS_6JValueEPKc+85)\n" + " #62 pc 005aab5a libartd.so " + "(_ZN3art17InvokeWithVarArgsERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_" + "jmethodIDPc+362)\n" + " #63 pc 0048a3dd libartd.so " + "(_ZN3art3JNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+125)\n" + " #64 pc 0018448c libartd.so " + "(_ZN3art8CheckJNI11CallMethodVEPKcP7_JNIEnvP8_jobjectP7_jclassP10_jmethodIDPcNS_" + "9Primitive4TypeENS_10InvokeTypeE+1964)\n" + " #65 pc 0017cf06 libartd.so " + "(_ZN3art8CheckJNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+70)\n" + " #66 pc 00001d8c dalvikvm32 " + "(_ZN7_JNIEnv20CallStaticVoidMethodEP7_jclassP10_jmethodIDz+60)\n" + " #67 pc 00001a80 dalvikvm32 (main+1312)\n" + " #68 pc 00018275 libc.so\n", + frame_info); +} + } // namespace unwindstack diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/137-cfi.odex b/libunwindstack/tests/files/offline/jit_debug_x86_32/137-cfi.odex new file mode 100644 index 000000000..870ac0a74 Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/137-cfi.odex differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/dalvikvm32 b/libunwindstack/tests/files/offline/jit_debug_x86_32/dalvikvm32 new file mode 100644 index 000000000..76ffad93e Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/dalvikvm32 differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/descriptor.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/descriptor.data new file mode 100644 index 000000000..466dae28b Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/descriptor.data differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry0.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry0.data new file mode 100644 index 000000000..3a725e800 Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry0.data differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry1.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry1.data new file mode 100644 index 000000000..767550f00 Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry1.data differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry2.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry2.data new file mode 100644 index 000000000..e7e492e79 Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry2.data differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry3.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry3.data new file mode 100644 index 000000000..65f9cd432 Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry3.data differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry4.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry4.data new file mode 100644 index 000000000..30aa28cfb Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry4.data differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry5.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry5.data new file mode 100644 index 000000000..3c89673f2 Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry5.data differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry6.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry6.data new file mode 100644 index 000000000..9c9b83c3c Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry6.data differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit0.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit0.data new file mode 100644 index 000000000..eaad1420a Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit0.data differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit1.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit1.data new file mode 100644 index 000000000..d53481644 Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit1.data differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit2.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit2.data new file mode 100644 index 000000000..dbeb886cc Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit2.data differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit3.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit3.data new file mode 100644 index 000000000..bf2142d82 Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit3.data differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit4.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit4.data new file mode 100644 index 000000000..e2ba1b0f2 Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit4.data differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit5.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit5.data new file mode 100644 index 000000000..c27ba541c Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit5.data differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit6.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit6.data new file mode 100644 index 000000000..5fc8fae46 Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit6.data differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so b/libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so new file mode 100644 index 000000000..70352dbb7 Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so new file mode 100644 index 000000000..e72e673ad Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/libc.so b/libunwindstack/tests/files/offline/jit_debug_x86_32/libc.so new file mode 100644 index 000000000..9c787902b Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/libc.so differ diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86_32/maps.txt new file mode 100644 index 000000000..db4f9f79b --- /dev/null +++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/maps.txt @@ -0,0 +1,6 @@ +56573000-56577000 r-xp 0 00:00 0 dalvikvm32 +eb833000-eb8cc000 r-xp 0 00:00 0 libarttestd.so +ec606000-ec607000 r-xp 2000 00:00 0 137-cfi.odex +ee74c000-f074c000 r-xp 0 00:00 0 anonymous:ee74c000 +f6be1000-f732b000 r-xp 0 00:00 0 libartd.so +f734b000-f74fc000 r-xp 0 00:00 0 libc.so diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/regs.txt b/libunwindstack/tests/files/offline/jit_debug_x86_32/regs.txt new file mode 100644 index 000000000..f68305b81 --- /dev/null +++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/regs.txt @@ -0,0 +1,9 @@ +eax: eb8cccd0 +ebx: eb8cccd0 +ecx: ff +edx: ffeb2ca8 +ebp: ffeb5298 +edi: ffeb5c08 +esi: ffeb5c00 +esp: ffeb5280 +eip: eb89bfb8 diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/stack.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/stack.data new file mode 100644 index 000000000..c3457624e Binary files /dev/null and b/libunwindstack/tests/files/offline/jit_debug_x86_32/stack.data differ diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp index 81bedb7d5..07e48af60 100644 --- a/libunwindstack/tools/unwind.cpp +++ b/libunwindstack/tools/unwind.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -90,6 +91,8 @@ void DoUnwind(pid_t pid) { auto process_memory = unwindstack::Memory::CreateProcessMemory(pid); unwindstack::Unwinder unwinder(128, &remote_maps, regs, process_memory); + unwindstack::JitDebug jit_debug(process_memory); + unwinder.SetJitDebug(&jit_debug, regs->Arch()); unwinder.Unwind(); // Print the frames. diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp index d64ef8f9c..74868d43d 100644 --- a/libunwindstack/tools/unwind_for_offline.cpp +++ b/libunwindstack/tools/unwind_for_offline.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -191,7 +192,9 @@ int SaveData(pid_t pid) { // elf files are involved. uint64_t sp = regs->sp(); auto process_memory = unwindstack::Memory::CreateProcessMemory(pid); + unwindstack::JitDebug jit_debug(process_memory); unwindstack::Unwinder unwinder(1024, &maps, regs, process_memory); + unwinder.SetJitDebug(&jit_debug, regs->Arch()); unwinder.Unwind(); std::unordered_map maps_by_start; @@ -214,6 +217,10 @@ int SaveData(pid_t pid) { } } + for (size_t i = 0; i < unwinder.NumFrames(); i++) { + printf("%s\n", unwinder.FormatFrame(i).c_str()); + } + if (!SaveStack(pid, sp, last_sp)) { return 1; }