Add ability to read jit gdb data.

Changes:
- New JitDebug class to handle all of the jit gdb interface.
- Add unit tests for all, along with new offline test using debug data.
- Add new Memory type called MemoryOfflineParts that has multiple
  MemoryOffline objects to support the offline test.
- Update the tools to use the JitDebug object.
- Modify libbacktrace to use the JitDebug, but only looking in libart.so
  and libartd.so.
- Change the Format32Bits to Is32Bit since it's more accurate and I use
  it in a different context where original name didn't make sense.
- Add a new function to find global variables in an elf file
  (GetGlobalVariable).
- Add a new function to determine if a pc is valid for this elf (IsValidPc).

Bug: 68396769

Test: Ran new unit tests. Added new offline test that uses jit debug data.
Test: Ran art test that generates jit data and verified a crash unwinds
Test: through the jit data.
Change-Id: I6e7ee2f5bab2242028a06feece156dff21c0a974
This commit is contained in:
Christopher Ferris 2017-12-20 18:49:01 -08:00
parent 55feb241b1
commit 150db124f3
52 changed files with 1625 additions and 24 deletions

View File

@ -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()) {

View File

@ -18,6 +18,9 @@
#include <stdlib.h>
#include <sys/types.h>
#include <string>
#include <vector>
#include <backtrace/BacktraceMap.h>
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
@ -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<std::string> search_libs_{"libart.so", "libartd.so"};
jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_));
if (!stack_maps_->Parse()) {
return false;
}

View File

@ -23,6 +23,7 @@
#include <memory>
#include <backtrace/BacktraceMap.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
class UnwindStackMap : public BacktraceMap {
@ -41,11 +42,14 @@ class UnwindStackMap : public BacktraceMap {
const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; }
unwindstack::JitDebug* GetJitDebug() { return jit_debug_.get(); }
protected:
uint64_t GetLoadBias(size_t index) override;
std::unique_ptr<unwindstack::Maps> stack_maps_;
std::shared_ptr<unwindstack::Memory> process_memory_;
std::unique_ptr<unwindstack::JitDebug> jit_debug_;
};
#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H

View File

@ -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/*",

View File

@ -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;

View File

@ -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 <typename SymType>
bool ElfInterface::GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address) {
if (symbols_.empty()) {
return false;
}
for (const auto symbol : symbols_) {
if (symbol->GetGlobal<SymType>(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<Elf32_Sym>(uint64_t, uin
template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, uint64_t, std::string*,
uint64_t*);
template bool ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(const std::string&, uint64_t*);
template bool ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(const std::string&, uint64_t*);
template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);

248
libunwindstack/JitDebug.cpp Normal file
View File

@ -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 <stdint.h>
#include <sys/mman.h>
#include <memory>
#include <vector>
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
// 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_(memory) {}
JitDebug::JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& 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<std::mutex> 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

View File

@ -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

View File

@ -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;

View File

@ -108,8 +108,35 @@ bool Symbols::GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std
return return_value;
}
template <typename SymType>
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<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
template bool Symbols::GetGlobal<Elf32_Sym>(Memory*, const std::string&, uint64_t*);
template bool Symbols::GetGlobal<Elf64_Sym>(Memory*, const std::string&, uint64_t*);
} // namespace unwindstack

View File

@ -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 <typename SymType>
bool GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address);
void ClearCache() {
symbols_.clear();
cur_offset_ = offset_;

View File

@ -27,12 +27,13 @@
#include <android-base/stringprintf.h>
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Unwinder.h>
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<std::string>* initial_map_names_to_skip,
bool return_address_attempt = false;
bool adjust_pc = false;
std::unique_ptr<JitDebug> 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<std::string>* 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<std::string>* 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

View File

@ -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_; }

View File

@ -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 <typename SymType>
bool GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address);
virtual bool HandleType(uint64_t, uint32_t, uint64_t) { return false; }
template <typename EhdrType>
@ -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<Elf32_Sym>(addr, load_bias, name, func_offset);
}
bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
return ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(name, memory_address);
}
static void GetMaxSize(Memory* memory, uint64_t* size) {
GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
}
@ -188,6 +201,10 @@ class ElfInterface64 : public ElfInterface {
return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, load_bias, name, func_offset);
}
bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
return ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(name, memory_address);
}
static void GetMaxSize(Memory* memory, uint64_t* size) {
GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
}

View File

@ -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 <stdint.h>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
namespace unwindstack {
// Forward declarations.
class Elf;
class Maps;
class Memory;
enum ArchEnum : uint8_t;
class JitDebug {
public:
explicit JitDebug(std::shared_ptr<Memory>& memory);
JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
~JitDebug();
Elf* GetElf(Maps* maps, uint64_t pc);
void SetArch(ArchEnum arch);
private:
void Init(Maps* maps);
std::shared_ptr<Memory> memory_;
uint64_t entry_addr_ = 0;
bool initialized_ = false;
std::vector<Elf*> elf_list_;
std::vector<std::string> 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

View File

@ -151,6 +151,19 @@ class MemoryOffline : public Memory {
std::unique_ptr<MemoryRange> 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<MemoryOffline*> memories_;
};
} // namespace unwindstack
#endif // _LIBUNWINDSTACK_MEMORY_H

View File

@ -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]; }

View File

@ -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<FrameData>& 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<FrameData> frames_;
std::shared_ptr<Memory> process_memory_;
JitDebug* jit_debug_ = nullptr;
};
} // namespace unwindstack

View File

@ -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;

View File

@ -21,6 +21,7 @@
#include <deque>
#include <string>
#include <unordered_map>
#include <unwindstack/Elf.h>
#include <unwindstack/ElfInterface.h>
@ -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<std::string, uint64_t> globals_;
static std::deque<FunctionData> functions_;
static std::deque<StepData> steps_;
};

View File

@ -958,4 +958,189 @@ TEST_F(ElfInterfaceTest, init_section_headers_offsets64) {
InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
}
TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) {
std::unique_ptr<ElfInterface> 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<ElfInterface> 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<ElfInterface> 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<ElfInterface> 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

View File

@ -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, &regs, &process_memory, &finished));
EXPECT_CALL(*interface, Step(0x7300, 0x4000, &regs, &process_memory, &finished))
.WillOnce(::testing::Return(true));
ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, &regs, &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

View File

@ -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 <elf.h>
#include <string.h>
#include <memory>
#include <vector>
#include <gtest/gtest.h>
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#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 <typename EhdrType, typename ShdrType>
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<Memory> process_memory_;
MemoryFake* memory_;
std::vector<MemoryFake*> elf_memories_;
std::unique_ptr<JitDebug> jit_debug_;
std::unique_ptr<BufferMaps> 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<Elf32_Ehdr, Elf32_Shdr>(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<Elf32_Ehdr, Elf32_Shdr>(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<Elf32_Ehdr, Elf32_Shdr>(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<Elf32_Ehdr, Elf32_Shdr>(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<Elf32_Ehdr, Elf32_Shdr>(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<Elf32_Ehdr, Elf32_Shdr>(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<Elf64_Ehdr, Elf64_Shdr>(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<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
CreateElf<Elf32_Ehdr, Elf32_Shdr>(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<Elf32_Ehdr, Elf32_Shdr>(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<std::string> 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

View File

@ -45,7 +45,7 @@ class RegsFake : public Regs {
void IterateRegisters(std::function<void(const char*, uint64_t)>) override {}
bool Format32Bit() { return false; }
bool Is32Bit() { return false; }
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf*) override { return rel_pc - 2; }

View File

@ -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));

View File

@ -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<TypeParam>(&this->memory_, "global_0", &offset));
EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_1", &offset));
EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_0", &offset));
EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_1", &offset));
EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_0", &offset));
EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_1", &offset));
std::string name;
EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, 0, &this->memory_, &name, &offset));
EXPECT_EQ("function_0", name);
EXPECT_EQ(2U, offset);
EXPECT_TRUE(symbols.GetName<TypeParam>(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<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);

View File

@ -25,14 +25,17 @@
#include <string>
#include <vector>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/RegsArm.h>
#include <unwindstack/RegsArm64.h>
#include <unwindstack/RegsX86.h>
#include <unwindstack/Unwinder.h>
#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", &reg_value));
regs[X86_REG_EAX] = reg_value;
ASSERT_EQ(1, fscanf(fp, "ebx: %" SCNx64 "\n", &reg_value));
regs[X86_REG_EBX] = reg_value;
ASSERT_EQ(1, fscanf(fp, "ecx: %" SCNx64 "\n", &reg_value));
regs[X86_REG_ECX] = reg_value;
ASSERT_EQ(1, fscanf(fp, "edx: %" SCNx64 "\n", &reg_value));
regs[X86_REG_EDX] = reg_value;
ASSERT_EQ(1, fscanf(fp, "ebp: %" SCNx64 "\n", &reg_value));
regs[X86_REG_EBP] = reg_value;
ASSERT_EQ(1, fscanf(fp, "edi: %" SCNx64 "\n", &reg_value));
regs[X86_REG_EDI] = reg_value;
ASSERT_EQ(1, fscanf(fp, "esi: %" SCNx64 "\n", &reg_value));
regs[X86_REG_ESI] = reg_value;
ASSERT_EQ(1, fscanf(fp, "esp: %" SCNx64 "\n", &reg_value));
regs[X86_REG_ESP] = reg_value;
ASSERT_EQ(1, fscanf(fp, "eip: %" SCNx64 "\n", &reg_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<char> 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<Memory> process_memory(memory);
char* cwd = getcwd(nullptr, 0);
ASSERT_EQ(0, chdir(dir.c_str()));
JitDebug jit_debug(process_memory);
Unwinder unwinder(128, &maps, &regs, 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

View File

@ -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

View File

@ -0,0 +1,9 @@
eax: eb8cccd0
ebx: eb8cccd0
ecx: ff
edx: ffeb2ca8
ebp: ffeb5298
edi: ffeb5c08
esi: ffeb5c00
esp: ffeb5280
eip: eb89bfb8

View File

@ -27,6 +27,7 @@
#include <unistd.h>
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
@ -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.

View File

@ -33,6 +33,7 @@
#include <vector>
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
@ -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<uint64_t, map_info_t> 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;
}