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