Merge "Load dex files from ART-specific data structure."
This commit is contained in:
commit
7f5615e8d4
13 changed files with 608 additions and 101 deletions
|
@ -46,7 +46,7 @@ bool UnwindStackMap::Build() {
|
|||
std::vector<std::string> search_libs_{"libart.so", "libartd.so"};
|
||||
jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_));
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
dex_files_.reset(new unwindstack::DexFiles(process_memory_));
|
||||
dex_files_.reset(new unwindstack::DexFiles(process_memory_, search_libs_));
|
||||
#endif
|
||||
|
||||
if (!stack_maps_->Parse()) {
|
||||
|
|
|
@ -128,6 +128,12 @@ cc_library_static {
|
|||
"DexFile.cpp",
|
||||
"DexFiles.cpp",
|
||||
],
|
||||
target: {
|
||||
// Always disable optimizations for host to make it easier to debug.
|
||||
host: {
|
||||
cflags: ["-O0", "-g"],
|
||||
},
|
||||
},
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
|
@ -151,8 +157,13 @@ cc_test_library {
|
|||
vendor_available: false,
|
||||
defaults: ["libunwindstack_flags"],
|
||||
|
||||
shared: {
|
||||
enabled: false,
|
||||
},
|
||||
|
||||
srcs: [
|
||||
"tests/DexFileTest.cpp",
|
||||
"tests/DexFilesTest.cpp",
|
||||
],
|
||||
local_include_dirs: ["include"],
|
||||
allow_undefined_symbols: true,
|
||||
|
|
|
@ -52,10 +52,20 @@ DexFile* DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory, Map
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
|
||||
DexFileFromFile::~DexFileFromFile() {
|
||||
if (size_ != 0) {
|
||||
munmap(mapped_memory_, size_);
|
||||
}
|
||||
}
|
||||
|
||||
bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
|
||||
uint64_t* method_offset) {
|
||||
if (dex_file_ == nullptr) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dex_file_->IsInDataSection(dex_file_->Begin() + dex_offset)) {
|
||||
return false; // The DEX offset is not within the bytecode of this dex file.
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < dex_file_->NumClassDefs(); ++i) {
|
||||
|
@ -82,16 +92,11 @@ void DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name
|
|||
if (offset <= dex_offset && dex_offset < offset + size) {
|
||||
*method_name = dex_file_->PrettyMethod(it.GetMemberIndex(), false);
|
||||
*method_offset = dex_offset - offset;
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DexFileFromFile::~DexFileFromFile() {
|
||||
if (size_ != 0) {
|
||||
munmap(mapped_memory_, size_);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) {
|
||||
|
@ -139,25 +144,41 @@ bool DexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string&
|
|||
}
|
||||
|
||||
bool DexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, Memory* memory) {
|
||||
art::DexFile::Header header;
|
||||
if (!memory->ReadFully(dex_file_offset_in_memory, &header, sizeof(header))) {
|
||||
memory_.resize(sizeof(art::DexFile::Header));
|
||||
if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!art::StandardDexFile::IsMagicValid(header.magic_) &&
|
||||
!art::CompactDexFile::IsMagicValid(header.magic_)) {
|
||||
art::DexFile::Header* header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
|
||||
bool modify_data_off = false;
|
||||
uint32_t file_size = header->file_size_;
|
||||
if (art::CompactDexFile::IsMagicValid(header->magic_)) {
|
||||
uint32_t computed_file_size;
|
||||
if (__builtin_add_overflow(header->data_off_, header->data_size_, &computed_file_size)) {
|
||||
return false;
|
||||
}
|
||||
if (computed_file_size > file_size) {
|
||||
file_size = computed_file_size;
|
||||
modify_data_off = true;
|
||||
}
|
||||
} else if (!art::StandardDexFile::IsMagicValid(header->magic_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memory_.resize(header.file_size_);
|
||||
if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), header.file_size_)) {
|
||||
memory_.resize(file_size);
|
||||
if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
|
||||
if (modify_data_off) {
|
||||
header->data_off_ = header->file_size_;
|
||||
}
|
||||
|
||||
art::DexFileLoader loader;
|
||||
std::string error_msg;
|
||||
auto dex =
|
||||
loader.Open(memory_.data(), header.file_size_, "", 0, nullptr, false, false, &error_msg);
|
||||
loader.Open(memory_.data(), header->file_size_, "", 0, nullptr, false, false, &error_msg);
|
||||
dex_file_.reset(dex.release());
|
||||
return dex_file_ != nullptr;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ class DexFile {
|
|||
DexFile() = default;
|
||||
virtual ~DexFile() = default;
|
||||
|
||||
void GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
|
||||
bool GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
|
||||
|
||||
static DexFile* Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info);
|
||||
|
||||
|
|
|
@ -24,23 +24,138 @@
|
|||
|
||||
#include <unwindstack/DexFiles.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "DexFile.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
struct DEXFileEntry32 {
|
||||
uint32_t next;
|
||||
uint32_t prev;
|
||||
uint32_t dex_file;
|
||||
};
|
||||
|
||||
struct DEXFileEntry64 {
|
||||
uint64_t next;
|
||||
uint64_t prev;
|
||||
uint64_t dex_file;
|
||||
};
|
||||
|
||||
DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : memory_(memory) {}
|
||||
|
||||
DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
|
||||
: memory_(memory), search_libs_(search_libs) {}
|
||||
|
||||
DexFiles::~DexFiles() {
|
||||
for (auto& entry : files_) {
|
||||
delete entry.second;
|
||||
}
|
||||
}
|
||||
|
||||
void DexFiles::SetArch(ArchEnum arch) {
|
||||
switch (arch) {
|
||||
case ARCH_ARM:
|
||||
case ARCH_MIPS:
|
||||
case ARCH_X86:
|
||||
read_entry_ptr_func_ = &DexFiles::ReadEntryPtr32;
|
||||
read_entry_func_ = &DexFiles::ReadEntry32;
|
||||
break;
|
||||
|
||||
case ARCH_ARM64:
|
||||
case ARCH_MIPS64:
|
||||
case ARCH_X86_64:
|
||||
read_entry_ptr_func_ = &DexFiles::ReadEntryPtr64;
|
||||
read_entry_func_ = &DexFiles::ReadEntry64;
|
||||
break;
|
||||
|
||||
case ARCH_UNKNOWN:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t DexFiles::ReadEntryPtr32(uint64_t addr) {
|
||||
uint32_t entry;
|
||||
if (!memory_->ReadFully(addr, &entry, sizeof(entry))) {
|
||||
return 0;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
uint64_t DexFiles::ReadEntryPtr64(uint64_t addr) {
|
||||
uint64_t entry;
|
||||
if (!memory_->ReadFully(addr, &entry, sizeof(entry))) {
|
||||
return 0;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
bool DexFiles::ReadEntry32() {
|
||||
DEXFileEntry32 entry;
|
||||
if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
|
||||
entry_addr_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
addrs_.push_back(entry.dex_file);
|
||||
entry_addr_ = entry.next;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DexFiles::ReadEntry64() {
|
||||
DEXFileEntry64 entry;
|
||||
if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
|
||||
entry_addr_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
addrs_.push_back(entry.dex_file);
|
||||
entry_addr_ = entry.next;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DexFiles::Init(Maps* maps) {
|
||||
if (initialized_) {
|
||||
return;
|
||||
}
|
||||
initialized_ = true;
|
||||
entry_addr_ = 0;
|
||||
|
||||
const std::string dex_debug_name("__art_debug_dexfiles");
|
||||
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 (const std::string& name : search_libs_) {
|
||||
if (name == lib) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Elf* elf = info->GetElf(memory_, true);
|
||||
uint64_t ptr;
|
||||
// Find first non-empty list (libart might be loaded multiple times).
|
||||
if (elf->GetGlobalVariable(dex_debug_name, &ptr) && ptr != 0) {
|
||||
entry_addr_ = (this->*read_entry_ptr_func_)(ptr + info->start);
|
||||
if (entry_addr_ != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
|
||||
// Lock while processing the data.
|
||||
std::lock_guard<std::mutex> guard(files_lock_);
|
||||
DexFile* dex_file;
|
||||
auto entry = files_.find(dex_file_offset);
|
||||
if (entry == files_.end()) {
|
||||
|
@ -52,14 +167,38 @@ DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
|
|||
return dex_file;
|
||||
}
|
||||
|
||||
void DexFiles::GetMethodInformation(uint64_t dex_file_offset, uint64_t dex_offset, MapInfo* info,
|
||||
bool DexFiles::GetAddr(size_t index, uint64_t* addr) {
|
||||
if (index < addrs_.size()) {
|
||||
*addr = addrs_[index];
|
||||
return true;
|
||||
}
|
||||
if (entry_addr_ != 0 && (this->*read_entry_func_)()) {
|
||||
*addr = addrs_.back();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DexFiles::GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc,
|
||||
std::string* method_name, uint64_t* method_offset) {
|
||||
DexFile* dex_file = GetDexFile(dex_file_offset, info);
|
||||
if (dex_file != nullptr) {
|
||||
dex_file->GetMethodInformation(dex_offset, method_name, method_offset);
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
if (!initialized_) {
|
||||
Init(maps);
|
||||
}
|
||||
|
||||
size_t index = 0;
|
||||
uint64_t addr;
|
||||
while (GetAddr(index++, &addr)) {
|
||||
if (addr < info->start || addr >= info->end) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DexFile* dex_file = GetDexFile(addr, info);
|
||||
if (dex_file != nullptr &&
|
||||
dex_file->GetMethodInformation(dex_pc - addr, method_name, method_offset)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DexFiles::SetArch(ArchEnum) {}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -172,7 +172,7 @@ void JitDebug::Init(Maps* maps) {
|
|||
// Regardless of what happens below, consider the init finished.
|
||||
initialized_ = true;
|
||||
|
||||
std::string descriptor_name("__jit_debug_descriptor");
|
||||
const std::string descriptor_name("__jit_debug_descriptor");
|
||||
for (MapInfo* info : *maps) {
|
||||
if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) {
|
||||
continue;
|
||||
|
|
|
@ -53,49 +53,22 @@ void Unwinder::FillInDexFrame() {
|
|||
frame->pc = dex_pc;
|
||||
frame->sp = regs_->sp();
|
||||
|
||||
auto it = maps_->begin();
|
||||
uint64_t rel_dex_pc;
|
||||
MapInfo* info;
|
||||
for (; it != maps_->end(); ++it) {
|
||||
auto entry = *it;
|
||||
if (dex_pc >= entry->start && dex_pc < entry->end) {
|
||||
info = entry;
|
||||
rel_dex_pc = dex_pc - entry->start;
|
||||
frame->map_start = entry->start;
|
||||
frame->map_end = entry->end;
|
||||
frame->map_offset = entry->offset;
|
||||
frame->map_load_bias = entry->load_bias;
|
||||
frame->map_flags = entry->flags;
|
||||
frame->map_name = entry->name;
|
||||
frame->rel_pc = rel_dex_pc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (it == maps_->end() || ++it == maps_->end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto entry = *it;
|
||||
unwindstack::Elf* elf = entry->GetElf(process_memory_, true);
|
||||
if (!elf->valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust the relative dex by the offset.
|
||||
rel_dex_pc += entry->elf_offset;
|
||||
|
||||
uint64_t dex_offset;
|
||||
if (!elf->GetFunctionName(rel_dex_pc, &frame->function_name, &dex_offset)) {
|
||||
return;
|
||||
}
|
||||
frame->function_offset = dex_offset;
|
||||
if (frame->function_name != "$dexfile") {
|
||||
return;
|
||||
}
|
||||
MapInfo* info = maps_->Find(dex_pc);
|
||||
frame->map_start = info->start;
|
||||
frame->map_end = info->end;
|
||||
frame->map_offset = info->offset;
|
||||
frame->map_load_bias = info->load_bias;
|
||||
frame->map_flags = info->flags;
|
||||
frame->map_name = info->name;
|
||||
frame->rel_pc = dex_pc - info->start;
|
||||
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
dex_files_->GetMethodInformation(dex_pc - dex_offset, dex_offset, info, &frame->function_name,
|
||||
if (dex_files_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// dex_files_->GetMethodInformation(dex_pc - dex_offset, dex_offset, info, &frame->function_name,
|
||||
dex_files_->GetMethodInformation(maps_, info, dex_pc, &frame->function_name,
|
||||
&frame->function_offset);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
|
@ -36,19 +37,40 @@ enum ArchEnum : uint8_t;
|
|||
class DexFiles {
|
||||
public:
|
||||
explicit DexFiles(std::shared_ptr<Memory>& memory);
|
||||
DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
|
||||
~DexFiles();
|
||||
|
||||
DexFile* GetDexFile(uint64_t dex_file_offset, MapInfo* info);
|
||||
|
||||
void GetMethodInformation(uint64_t dex_file_offset, uint64_t dex_offset, MapInfo* info,
|
||||
std::string* method_name, uint64_t* method_offset);
|
||||
void GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc, std::string* method_name,
|
||||
uint64_t* method_offset);
|
||||
|
||||
void SetArch(ArchEnum arch);
|
||||
|
||||
private:
|
||||
void Init(Maps* maps);
|
||||
|
||||
bool GetAddr(size_t index, uint64_t* addr);
|
||||
|
||||
uint64_t ReadEntryPtr32(uint64_t addr);
|
||||
|
||||
uint64_t ReadEntryPtr64(uint64_t addr);
|
||||
|
||||
bool ReadEntry32();
|
||||
|
||||
bool ReadEntry64();
|
||||
|
||||
std::shared_ptr<Memory> memory_;
|
||||
std::mutex files_lock_;
|
||||
std::vector<std::string> search_libs_;
|
||||
|
||||
std::mutex lock_;
|
||||
bool initialized_ = false;
|
||||
std::unordered_map<uint64_t, DexFile*> files_;
|
||||
|
||||
uint64_t entry_addr_ = 0;
|
||||
uint64_t (DexFiles::*read_entry_ptr_func_)(uint64_t) = nullptr;
|
||||
bool (DexFiles::*read_entry_func_)() = nullptr;
|
||||
std::vector<uint64_t> addrs_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
45
libunwindstack/tests/DexFileData.h
Normal file
45
libunwindstack/tests/DexFileData.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_DEXFILESDATA_H
|
||||
#define _LIBUNWINDSTACK_DEXFILESDATA_H
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
// Borrowed from art/dex/dex_file_test.cc.
|
||||
static constexpr uint32_t kDexData[] = {
|
||||
0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
|
||||
0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
|
||||
0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
|
||||
0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
|
||||
0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
|
||||
0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
|
||||
0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
|
||||
0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
|
||||
0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
|
||||
0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
|
||||
0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
|
||||
0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
|
||||
0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
|
||||
0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
|
||||
0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
|
||||
0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
|
||||
0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_DEXFILESDATA_H
|
|
@ -32,31 +32,11 @@
|
|||
|
||||
#include "DexFile.h"
|
||||
|
||||
#include "DexFileData.h"
|
||||
#include "MemoryFake.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
// Borrowed from art/dex/dex_file_test.cc.
|
||||
static constexpr uint32_t kDexData[] = {
|
||||
0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
|
||||
0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
|
||||
0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
|
||||
0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
|
||||
0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
|
||||
0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
|
||||
0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
|
||||
0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
|
||||
0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
|
||||
0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
|
||||
0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
|
||||
0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
|
||||
0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
|
||||
0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
|
||||
0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
|
||||
0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
|
||||
0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
|
||||
};
|
||||
|
||||
TEST(DexFileTest, from_file_open_non_exist) {
|
||||
DexFileFromFile dex_file;
|
||||
ASSERT_FALSE(dex_file.Open(0, "/file/does/not/exist"));
|
||||
|
|
311
libunwindstack/tests/DexFilesTest.cpp
Normal file
311
libunwindstack/tests/DexFilesTest.cpp
Normal file
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* 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/DexFiles.h>
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "DexFileData.h"
|
||||
#include "ElfFake.h"
|
||||
#include "MemoryFake.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class DexFilesTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
memory_ = new MemoryFake;
|
||||
process_memory_.reset(memory_);
|
||||
|
||||
dex_files_.reset(new DexFiles(process_memory_));
|
||||
dex_files_->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 r-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"
|
||||
"300000-400000 rw-p 0000000 00:00 0\n"));
|
||||
ASSERT_TRUE(maps_->Parse());
|
||||
|
||||
// Global variable in a section that is not readable/executable.
|
||||
MapInfo* map_info = maps_->Get(kMapGlobalNonReadableExectable);
|
||||
ASSERT_TRUE(map_info != nullptr);
|
||||
MemoryFake* memory = new MemoryFake;
|
||||
ElfFake* elf = new ElfFake(memory);
|
||||
elf->FakeSetValid(true);
|
||||
ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
|
||||
elf->FakeSetInterface(interface);
|
||||
interface->FakeSetGlobalVariable("__art_debug_dexfiles", 0x800);
|
||||
map_info->elf.reset(elf);
|
||||
|
||||
// Global variable not set by default.
|
||||
map_info = maps_->Get(kMapGlobalSetToZero);
|
||||
ASSERT_TRUE(map_info != nullptr);
|
||||
memory = new MemoryFake;
|
||||
elf = new ElfFake(memory);
|
||||
elf->FakeSetValid(true);
|
||||
interface = new ElfInterfaceFake(memory);
|
||||
elf->FakeSetInterface(interface);
|
||||
interface->FakeSetGlobalVariable("__art_debug_dexfiles", 0x800);
|
||||
map_info->elf.reset(elf);
|
||||
|
||||
// Global variable set in this map.
|
||||
map_info = maps_->Get(kMapGlobal);
|
||||
ASSERT_TRUE(map_info != nullptr);
|
||||
memory = new MemoryFake;
|
||||
elf = new ElfFake(memory);
|
||||
elf->FakeSetValid(true);
|
||||
interface = new ElfInterfaceFake(memory);
|
||||
elf->FakeSetInterface(interface);
|
||||
interface->FakeSetGlobalVariable("__art_debug_dexfiles", 0x800);
|
||||
map_info->elf.reset(elf);
|
||||
}
|
||||
|
||||
void WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev, uint32_t dex_file);
|
||||
void WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev, uint64_t dex_file);
|
||||
void WriteDex(uint64_t dex_file);
|
||||
|
||||
static constexpr size_t kMapGlobalNonReadableExectable = 3;
|
||||
static constexpr size_t kMapGlobalSetToZero = 4;
|
||||
static constexpr size_t kMapGlobal = 5;
|
||||
static constexpr size_t kMapDexFileEntries = 7;
|
||||
static constexpr size_t kMapDexFiles = 8;
|
||||
|
||||
std::shared_ptr<Memory> process_memory_;
|
||||
MemoryFake* memory_;
|
||||
std::unique_ptr<DexFiles> dex_files_;
|
||||
std::unique_ptr<BufferMaps> maps_;
|
||||
};
|
||||
|
||||
void DexFilesTest::WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev,
|
||||
uint32_t dex_file) {
|
||||
// Format of the 32 bit DEXFileEntry structure:
|
||||
// uint32_t next
|
||||
memory_->SetData32(entry_addr, next);
|
||||
// uint32_t prev
|
||||
memory_->SetData32(entry_addr + 4, prev);
|
||||
// uint32_t dex_file
|
||||
memory_->SetData32(entry_addr + 8, dex_file);
|
||||
}
|
||||
|
||||
void DexFilesTest::WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev,
|
||||
uint64_t dex_file) {
|
||||
// Format of the 64 bit DEXFileEntry structure:
|
||||
// uint64_t next
|
||||
memory_->SetData64(entry_addr, next);
|
||||
// uint64_t prev
|
||||
memory_->SetData64(entry_addr + 8, prev);
|
||||
// uint64_t dex_file
|
||||
memory_->SetData64(entry_addr + 16, dex_file);
|
||||
}
|
||||
|
||||
void DexFilesTest::WriteDex(uint64_t dex_file) {
|
||||
memory_->SetMemory(dex_file, kDexData, sizeof(kDexData) * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
TEST_F(DexFilesTest, get_method_information_invalid) {
|
||||
std::string method_name = "nothing";
|
||||
uint64_t method_offset = 0x124;
|
||||
MapInfo* info = maps_->Get(kMapDexFileEntries);
|
||||
|
||||
dex_files_->GetMethodInformation(maps_.get(), info, 0, &method_name, &method_offset);
|
||||
EXPECT_EQ("nothing", method_name);
|
||||
EXPECT_EQ(0x124U, method_offset);
|
||||
}
|
||||
|
||||
TEST_F(DexFilesTest, get_method_information_32) {
|
||||
std::string method_name = "nothing";
|
||||
uint64_t method_offset = 0x124;
|
||||
MapInfo* info = maps_->Get(kMapDexFiles);
|
||||
|
||||
memory_->SetData32(0xf800, 0x200000);
|
||||
WriteEntry32(0x200000, 0, 0, 0x300000);
|
||||
WriteDex(0x300000);
|
||||
|
||||
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
|
||||
EXPECT_EQ("Main.<init>", method_name);
|
||||
EXPECT_EQ(0U, method_offset);
|
||||
}
|
||||
|
||||
TEST_F(DexFilesTest, get_method_information_64) {
|
||||
std::string method_name = "nothing";
|
||||
uint64_t method_offset = 0x124;
|
||||
MapInfo* info = maps_->Get(kMapDexFiles);
|
||||
|
||||
dex_files_->SetArch(ARCH_ARM64);
|
||||
memory_->SetData64(0xf800, 0x200000);
|
||||
WriteEntry64(0x200000, 0, 0, 0x301000);
|
||||
WriteDex(0x301000);
|
||||
|
||||
dex_files_->GetMethodInformation(maps_.get(), info, 0x301102, &method_name, &method_offset);
|
||||
EXPECT_EQ("Main.<init>", method_name);
|
||||
EXPECT_EQ(2U, method_offset);
|
||||
}
|
||||
|
||||
TEST_F(DexFilesTest, get_method_information_not_first_entry_32) {
|
||||
std::string method_name = "nothing";
|
||||
uint64_t method_offset = 0x124;
|
||||
MapInfo* info = maps_->Get(kMapDexFiles);
|
||||
|
||||
memory_->SetData32(0xf800, 0x200000);
|
||||
WriteEntry32(0x200000, 0x200100, 0, 0x100000);
|
||||
WriteEntry32(0x200100, 0, 0x200000, 0x300000);
|
||||
WriteDex(0x300000);
|
||||
|
||||
dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
|
||||
EXPECT_EQ("Main.<init>", method_name);
|
||||
EXPECT_EQ(4U, method_offset);
|
||||
}
|
||||
|
||||
TEST_F(DexFilesTest, get_method_information_not_first_entry_64) {
|
||||
std::string method_name = "nothing";
|
||||
uint64_t method_offset = 0x124;
|
||||
MapInfo* info = maps_->Get(kMapDexFiles);
|
||||
|
||||
dex_files_->SetArch(ARCH_ARM64);
|
||||
memory_->SetData64(0xf800, 0x200000);
|
||||
WriteEntry64(0x200000, 0x200100, 0, 0x100000);
|
||||
WriteEntry64(0x200100, 0, 0x200000, 0x300000);
|
||||
WriteDex(0x300000);
|
||||
|
||||
dex_files_->GetMethodInformation(maps_.get(), info, 0x300106, &method_name, &method_offset);
|
||||
EXPECT_EQ("Main.<init>", method_name);
|
||||
EXPECT_EQ(6U, method_offset);
|
||||
}
|
||||
|
||||
TEST_F(DexFilesTest, get_method_information_cached) {
|
||||
std::string method_name = "nothing";
|
||||
uint64_t method_offset = 0x124;
|
||||
MapInfo* info = maps_->Get(kMapDexFiles);
|
||||
|
||||
memory_->SetData32(0xf800, 0x200000);
|
||||
WriteEntry32(0x200000, 0, 0, 0x300000);
|
||||
WriteDex(0x300000);
|
||||
|
||||
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
|
||||
EXPECT_EQ("Main.<init>", method_name);
|
||||
EXPECT_EQ(0U, method_offset);
|
||||
|
||||
// Clear all memory and make sure that data is acquired from the cache.
|
||||
memory_->Clear();
|
||||
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
|
||||
EXPECT_EQ("Main.<init>", method_name);
|
||||
EXPECT_EQ(0U, method_offset);
|
||||
}
|
||||
|
||||
TEST_F(DexFilesTest, get_method_information_search_libs) {
|
||||
std::string method_name = "nothing";
|
||||
uint64_t method_offset = 0x124;
|
||||
MapInfo* info = maps_->Get(kMapDexFiles);
|
||||
|
||||
memory_->SetData32(0xf800, 0x200000);
|
||||
WriteEntry32(0x200000, 0x200100, 0, 0x100000);
|
||||
WriteEntry32(0x200100, 0, 0x200000, 0x300000);
|
||||
WriteDex(0x300000);
|
||||
|
||||
// Only search a given named list of libs.
|
||||
std::vector<std::string> libs{"libart.so"};
|
||||
dex_files_.reset(new DexFiles(process_memory_, libs));
|
||||
dex_files_->SetArch(ARCH_ARM);
|
||||
|
||||
dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
|
||||
EXPECT_EQ("nothing", method_name);
|
||||
EXPECT_EQ(0x124U, method_offset);
|
||||
|
||||
MapInfo* map_info = maps_->Get(kMapGlobal);
|
||||
map_info->name = "/system/lib/libart.so";
|
||||
dex_files_.reset(new DexFiles(process_memory_, libs));
|
||||
dex_files_->SetArch(ARCH_ARM);
|
||||
// Make sure that clearing out copy of the libs doesn't affect the
|
||||
// DexFiles object.
|
||||
libs.clear();
|
||||
|
||||
dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
|
||||
EXPECT_EQ("Main.<init>", method_name);
|
||||
EXPECT_EQ(4U, method_offset);
|
||||
}
|
||||
|
||||
TEST_F(DexFilesTest, get_method_information_global_skip_zero_32) {
|
||||
std::string method_name = "nothing";
|
||||
uint64_t method_offset = 0x124;
|
||||
MapInfo* info = maps_->Get(kMapDexFiles);
|
||||
|
||||
// First global variable found, but value is zero.
|
||||
memory_->SetData32(0xc800, 0);
|
||||
|
||||
memory_->SetData32(0xf800, 0x200000);
|
||||
WriteEntry32(0x200000, 0, 0, 0x300000);
|
||||
WriteDex(0x300000);
|
||||
|
||||
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
|
||||
EXPECT_EQ("Main.<init>", method_name);
|
||||
EXPECT_EQ(0U, method_offset);
|
||||
|
||||
// Verify that second is ignored when first is set to non-zero
|
||||
dex_files_.reset(new DexFiles(process_memory_));
|
||||
dex_files_->SetArch(ARCH_ARM);
|
||||
method_name = "fail";
|
||||
method_offset = 0x123;
|
||||
memory_->SetData32(0xc800, 0x100000);
|
||||
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
|
||||
EXPECT_EQ("fail", method_name);
|
||||
EXPECT_EQ(0x123U, method_offset);
|
||||
}
|
||||
|
||||
TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) {
|
||||
std::string method_name = "nothing";
|
||||
uint64_t method_offset = 0x124;
|
||||
MapInfo* info = maps_->Get(kMapDexFiles);
|
||||
|
||||
// First global variable found, but value is zero.
|
||||
memory_->SetData64(0xc800, 0);
|
||||
|
||||
memory_->SetData64(0xf800, 0x200000);
|
||||
WriteEntry64(0x200000, 0, 0, 0x300000);
|
||||
WriteDex(0x300000);
|
||||
|
||||
dex_files_->SetArch(ARCH_ARM64);
|
||||
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
|
||||
EXPECT_EQ("Main.<init>", method_name);
|
||||
EXPECT_EQ(0U, method_offset);
|
||||
|
||||
// Verify that second is ignored when first is set to non-zero
|
||||
dex_files_.reset(new DexFiles(process_memory_));
|
||||
dex_files_->SetArch(ARCH_ARM64);
|
||||
method_name = "fail";
|
||||
method_offset = 0x123;
|
||||
memory_->SetData32(0xc800, 0x100000);
|
||||
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
|
||||
EXPECT_EQ("fail", method_name);
|
||||
EXPECT_EQ(0x123U, method_offset);
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
|
@ -56,30 +56,30 @@ class JitDebugTest : public ::testing::Test {
|
|||
|
||||
MapInfo* map_info = maps_->Get(3);
|
||||
ASSERT_TRUE(map_info != nullptr);
|
||||
elf_memories_.push_back(new MemoryFake);
|
||||
ElfFake* elf = new ElfFake(elf_memories_.back());
|
||||
MemoryFake* memory = new MemoryFake;
|
||||
ElfFake* elf = new ElfFake(memory);
|
||||
elf->FakeSetValid(true);
|
||||
ElfInterfaceFake* interface = new ElfInterfaceFake(elf_memories_.back());
|
||||
ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
|
||||
elf->FakeSetInterface(interface);
|
||||
interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
|
||||
map_info->elf.reset(elf);
|
||||
|
||||
map_info = maps_->Get(5);
|
||||
ASSERT_TRUE(map_info != nullptr);
|
||||
elf_memories_.push_back(new MemoryFake);
|
||||
elf = new ElfFake(elf_memories_.back());
|
||||
memory = new MemoryFake;
|
||||
elf = new ElfFake(memory);
|
||||
elf->FakeSetValid(true);
|
||||
interface = new ElfInterfaceFake(elf_memories_.back());
|
||||
interface = new ElfInterfaceFake(memory);
|
||||
elf->FakeSetInterface(interface);
|
||||
interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
|
||||
map_info->elf.reset(elf);
|
||||
|
||||
map_info = maps_->Get(6);
|
||||
ASSERT_TRUE(map_info != nullptr);
|
||||
elf_memories_.push_back(new MemoryFake);
|
||||
elf = new ElfFake(elf_memories_.back());
|
||||
memory = new MemoryFake;
|
||||
elf = new ElfFake(memory);
|
||||
elf->FakeSetValid(true);
|
||||
interface = new ElfInterfaceFake(elf_memories_.back());
|
||||
interface = new ElfInterfaceFake(memory);
|
||||
elf->FakeSetInterface(interface);
|
||||
interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
|
||||
map_info->elf.reset(elf);
|
||||
|
@ -171,7 +171,6 @@ class JitDebugTest : public ::testing::Test {
|
|||
|
||||
std::shared_ptr<Memory> process_memory_;
|
||||
MemoryFake* memory_;
|
||||
std::vector<MemoryFake*> elf_memories_;
|
||||
std::unique_ptr<JitDebug> jit_debug_;
|
||||
std::unique_ptr<BufferMaps> maps_;
|
||||
};
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <unwindstack/DexFiles.h>
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/JitDebug.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
|
@ -91,8 +92,13 @@ 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());
|
||||
|
||||
unwindstack::DexFiles dex_files(process_memory);
|
||||
unwinder.SetDexFiles(&dex_files, regs->Arch());
|
||||
|
||||
unwinder.Unwind();
|
||||
|
||||
// Print the frames.
|
||||
|
|
Loading…
Reference in a new issue