Merge "Add extra frame when dex_pc is non-zero."
am: 0ad424358c
Change-Id: I829267e02fa1342d09923c9087e3a4f3f9d63b1d
This commit is contained in:
commit
ce7a773232
|
@ -114,6 +114,7 @@ cc_library_static {
|
|||
"libbacktrace",
|
||||
"libunwind",
|
||||
"libunwindstack",
|
||||
"libdexfile",
|
||||
"liblzma",
|
||||
"libcutils",
|
||||
],
|
||||
|
|
|
@ -50,6 +50,7 @@ libbacktrace_sources = [
|
|||
"BacktracePtrace.cpp",
|
||||
"thread_utils.c",
|
||||
"ThreadEntry.cpp",
|
||||
"UnwindDexFile.cpp",
|
||||
"UnwindStack.cpp",
|
||||
"UnwindStackMap.cpp",
|
||||
]
|
||||
|
@ -92,13 +93,27 @@ cc_library {
|
|||
"liblog",
|
||||
"libunwind",
|
||||
"libunwindstack",
|
||||
"libdexfile",
|
||||
],
|
||||
|
||||
static_libs: ["libcutils"],
|
||||
|
||||
// libdexfile will eventually properly export headers, for now
|
||||
// include these directly.
|
||||
include_dirs: [
|
||||
"art/runtime",
|
||||
],
|
||||
|
||||
header_libs: [ "jni_headers", ],
|
||||
},
|
||||
android: {
|
||||
static_libs: ["libasync_safe"],
|
||||
},
|
||||
vendor: {
|
||||
cflags: ["-DNO_LIBDEXFILE"],
|
||||
exclude_srcs: ["UnwindDexFile.cpp"],
|
||||
exclude_shared_libs: ["libdexfile"],
|
||||
},
|
||||
},
|
||||
whole_static_libs: ["libdemangle"],
|
||||
}
|
||||
|
@ -161,6 +176,8 @@ cc_test {
|
|||
"backtrace_test.cpp",
|
||||
"GetPss.cpp",
|
||||
"thread_utils.c",
|
||||
|
||||
"unwind_dex_test.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
|
@ -172,6 +189,7 @@ cc_test {
|
|||
shared_libs: [
|
||||
"libbacktrace_test",
|
||||
"libbacktrace",
|
||||
"libdexfile",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"liblog",
|
||||
|
@ -212,6 +230,12 @@ cc_test {
|
|||
},
|
||||
},
|
||||
|
||||
// libdexfile will eventually properly export headers, for now
|
||||
// include these directly.
|
||||
include_dirs: [
|
||||
"art/runtime",
|
||||
],
|
||||
|
||||
data: [
|
||||
"testdata/arm/*",
|
||||
"testdata/arm64/*",
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* 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 <android-base/unique_fd.h>
|
||||
|
||||
#include <dex/code_item_accessors-no_art-inl.h>
|
||||
#include <dex/compact_dex_file.h>
|
||||
#include <dex/dex_file-inl.h>
|
||||
#include <dex/dex_file_loader.h>
|
||||
#include <dex/standard_dex_file.h>
|
||||
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "UnwindDexFile.h"
|
||||
|
||||
UnwindDexFile* UnwindDexFile::Create(uint64_t dex_file_offset_in_memory,
|
||||
unwindstack::Memory* memory, unwindstack::MapInfo* info) {
|
||||
if (!info->name.empty()) {
|
||||
std::unique_ptr<UnwindDexFileFromFile> dex_file(new UnwindDexFileFromFile);
|
||||
if (dex_file->Open(dex_file_offset_in_memory - info->start + info->offset, info->name)) {
|
||||
return dex_file.release();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<UnwindDexFileFromMemory> dex_file(new UnwindDexFileFromMemory);
|
||||
if (dex_file->Open(dex_file_offset_in_memory, memory)) {
|
||||
return dex_file.release();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UnwindDexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
|
||||
uint64_t* method_offset) {
|
||||
if (dex_file_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < dex_file_->NumClassDefs(); ++i) {
|
||||
const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(i);
|
||||
const uint8_t* class_data = dex_file_->GetClassData(class_def);
|
||||
if (class_data == nullptr) {
|
||||
continue;
|
||||
}
|
||||
for (art::ClassDataItemIterator it(*dex_file_.get(), class_data); it.HasNext(); it.Next()) {
|
||||
if (!it.IsAtMethod()) {
|
||||
continue;
|
||||
}
|
||||
const art::DexFile::CodeItem* code_item = it.GetMethodCodeItem();
|
||||
if (code_item == nullptr) {
|
||||
continue;
|
||||
}
|
||||
art::CodeItemInstructionAccessor code(*dex_file_.get(), code_item);
|
||||
if (!code.HasCodeItem()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
|
||||
size_t size = code.InsnsSizeInCodeUnits() * sizeof(uint16_t);
|
||||
if (offset <= dex_offset && dex_offset < offset + size) {
|
||||
*method_name = dex_file_->PrettyMethod(it.GetMemberIndex(), false);
|
||||
*method_offset = dex_offset - offset;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnwindDexFileFromFile::~UnwindDexFileFromFile() {
|
||||
if (size_ != 0) {
|
||||
munmap(mapped_memory_, size_);
|
||||
}
|
||||
}
|
||||
|
||||
bool UnwindDexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) {
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
struct stat buf;
|
||||
if (fstat(fd, &buf) == -1) {
|
||||
return false;
|
||||
}
|
||||
uint64_t length;
|
||||
if (buf.st_size < 0 ||
|
||||
__builtin_add_overflow(dex_file_offset_in_file, sizeof(art::DexFile::Header), &length) ||
|
||||
static_cast<uint64_t>(buf.st_size) < length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mapped_memory_ = mmap(nullptr, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (mapped_memory_ == MAP_FAILED) {
|
||||
return false;
|
||||
}
|
||||
size_ = buf.st_size;
|
||||
|
||||
uint8_t* memory = reinterpret_cast<uint8_t*>(mapped_memory_);
|
||||
|
||||
art::DexFile::Header* header =
|
||||
reinterpret_cast<art::DexFile::Header*>(&memory[dex_file_offset_in_file]);
|
||||
if (!art::StandardDexFile::IsMagicValid(header->magic_) &&
|
||||
!art::CompactDexFile::IsMagicValid(header->magic_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (__builtin_add_overflow(dex_file_offset_in_file, header->file_size_, &length) ||
|
||||
static_cast<uint64_t>(buf.st_size) < length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
art::DexFileLoader loader;
|
||||
std::string error_msg;
|
||||
auto dex = loader.Open(&memory[dex_file_offset_in_file], header->file_size_, "", 0, nullptr,
|
||||
false, false, &error_msg);
|
||||
dex_file_.reset(dex.release());
|
||||
return dex_file_ != nullptr;
|
||||
}
|
||||
|
||||
bool UnwindDexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, unwindstack::Memory* memory) {
|
||||
art::DexFile::Header header;
|
||||
if (!memory->ReadFully(dex_file_offset_in_memory, &header, sizeof(header))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!art::StandardDexFile::IsMagicValid(header.magic_) &&
|
||||
!art::CompactDexFile::IsMagicValid(header.magic_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memory_.resize(header.file_size_);
|
||||
if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), header.file_size_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
art::DexFileLoader loader;
|
||||
std::string error_msg;
|
||||
auto dex =
|
||||
loader.Open(memory_.data(), header.file_size_, "", 0, nullptr, false, false, &error_msg);
|
||||
dex_file_.reset(dex.release());
|
||||
return dex_file_ != nullptr;
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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 _LIBBACKTRACE_UNWIND_DEX_FILE_H
|
||||
#define _LIBBACKTRACE_UNWIND_DEX_FILE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <dex/dex_file-inl.h>
|
||||
|
||||
namespace unwindstack {
|
||||
class Memory;
|
||||
struct MapInfo;
|
||||
} // namespace unwindstack
|
||||
|
||||
class UnwindDexFile {
|
||||
public:
|
||||
UnwindDexFile() = default;
|
||||
virtual ~UnwindDexFile() = default;
|
||||
|
||||
void GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
|
||||
|
||||
static UnwindDexFile* Create(uint64_t dex_file_offset_in_memory, unwindstack::Memory* memory,
|
||||
unwindstack::MapInfo* info);
|
||||
|
||||
protected:
|
||||
std::unique_ptr<const art::DexFile> dex_file_;
|
||||
};
|
||||
|
||||
class UnwindDexFileFromFile : public UnwindDexFile {
|
||||
public:
|
||||
UnwindDexFileFromFile() = default;
|
||||
virtual ~UnwindDexFileFromFile();
|
||||
|
||||
bool Open(uint64_t dex_file_offset_in_file, const std::string& name);
|
||||
|
||||
private:
|
||||
void* mapped_memory_ = nullptr;
|
||||
size_t size_ = 0;
|
||||
};
|
||||
|
||||
class UnwindDexFileFromMemory : public UnwindDexFile {
|
||||
public:
|
||||
UnwindDexFileFromMemory() = default;
|
||||
virtual ~UnwindDexFileFromMemory() = default;
|
||||
|
||||
bool Open(uint64_t dex_file_offset_in_memory, unwindstack::Memory* memory);
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> memory_;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_UNWIND_DEX_FILE_H
|
|
@ -40,9 +40,67 @@
|
|||
#include <unwindstack/Unwinder.h>
|
||||
|
||||
#include "BacktraceLog.h"
|
||||
#ifndef NO_LIBDEXFILE
|
||||
#include "UnwindDexFile.h"
|
||||
#endif
|
||||
#include "UnwindStack.h"
|
||||
#include "UnwindStackMap.h"
|
||||
|
||||
static void FillInDexFrame(UnwindStackMap* stack_map, uint64_t dex_pc,
|
||||
backtrace_frame_data_t* frame) {
|
||||
// The DEX PC points into the .dex section within an ELF file.
|
||||
// However, this is a BBS section manually mmaped to a .vdex file,
|
||||
// so we need to get the following map to find the ELF data.
|
||||
unwindstack::Maps* maps = stack_map->stack_maps();
|
||||
auto it = maps->begin();
|
||||
uint64_t rel_dex_pc;
|
||||
unwindstack::MapInfo* info;
|
||||
for (; it != maps->end(); ++it) {
|
||||
auto entry = *it;
|
||||
if (dex_pc >= entry->start && dex_pc < entry->end) {
|
||||
info = entry;
|
||||
rel_dex_pc = dex_pc - entry->start;
|
||||
frame->map.start = entry->start;
|
||||
frame->map.end = entry->end;
|
||||
frame->map.offset = entry->offset;
|
||||
frame->map.load_bias = entry->load_bias;
|
||||
frame->map.flags = entry->flags;
|
||||
frame->map.name = entry->name;
|
||||
frame->rel_pc = rel_dex_pc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (it == maps->end() || ++it == maps->end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto entry = *it;
|
||||
auto process_memory = stack_map->process_memory();
|
||||
unwindstack::Elf* elf = entry->GetElf(process_memory, true);
|
||||
if (!elf->valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust the relative dex by the offset.
|
||||
rel_dex_pc += entry->elf_offset;
|
||||
|
||||
uint64_t dex_offset;
|
||||
if (!elf->GetFunctionName(rel_dex_pc, &frame->func_name, &dex_offset)) {
|
||||
return;
|
||||
}
|
||||
frame->func_offset = dex_offset;
|
||||
if (frame->func_name != "$dexfile") {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef NO_LIBDEXFILE
|
||||
UnwindDexFile* dex_file = stack_map->GetDexFile(dex_pc - dex_offset, info);
|
||||
if (dex_file != nullptr) {
|
||||
dex_file->GetMethodInformation(dex_offset, &frame->func_name, &frame->func_offset);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
|
||||
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
|
||||
std::vector<std::string>* skip_names) {
|
||||
|
@ -60,19 +118,25 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
|
|||
return true;
|
||||
}
|
||||
|
||||
frames->resize(unwinder.NumFrames() - num_ignore_frames);
|
||||
auto unwinder_frames = unwinder.frames();
|
||||
// Get the real number of frames we'll need.
|
||||
size_t total_frames = 0;
|
||||
for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++, total_frames++) {
|
||||
if (unwinder_frames[i].dex_pc != 0) {
|
||||
total_frames++;
|
||||
}
|
||||
}
|
||||
frames->resize(total_frames);
|
||||
size_t cur_frame = 0;
|
||||
for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++, cur_frame++) {
|
||||
auto frame = &unwinder_frames[i];
|
||||
backtrace_frame_data_t* back_frame = &frames->at(cur_frame);
|
||||
|
||||
back_frame->num = frame->num - num_ignore_frames;
|
||||
back_frame->num = cur_frame;
|
||||
|
||||
back_frame->rel_pc = frame->rel_pc;
|
||||
back_frame->pc = frame->pc;
|
||||
back_frame->sp = frame->sp;
|
||||
back_frame->dex_pc = frame->dex_pc;
|
||||
|
||||
back_frame->func_name = demangle(frame->function_name.c_str());
|
||||
back_frame->func_offset = frame->function_offset;
|
||||
|
@ -83,6 +147,19 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
|
|||
back_frame->map.offset = frame->map_offset;
|
||||
back_frame->map.load_bias = frame->map_load_bias;
|
||||
back_frame->map.flags = frame->map_flags;
|
||||
|
||||
// Inject a frame that represents the dex pc data.
|
||||
if (frame->dex_pc != 0) {
|
||||
cur_frame++;
|
||||
backtrace_frame_data_t* dex_frame = &frames->at(cur_frame);
|
||||
dex_frame->num = cur_frame;
|
||||
dex_frame->pc = frame->dex_pc;
|
||||
dex_frame->rel_pc = frame->dex_pc;
|
||||
dex_frame->sp = back_frame->sp;
|
||||
dex_frame->stack_size = 0;
|
||||
dex_frame->func_offset = 0;
|
||||
FillInDexFrame(stack_map, frame->dex_pc, dex_frame);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -26,11 +26,20 @@
|
|||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
|
||||
#include "UnwindDexFile.h"
|
||||
#include "UnwindStackMap.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
UnwindStackMap::UnwindStackMap(pid_t pid) : BacktraceMap(pid) {}
|
||||
|
||||
UnwindStackMap::~UnwindStackMap() {
|
||||
#ifndef NO_LIBDEXFILE
|
||||
for (auto& entry : dex_files_) {
|
||||
delete entry.second;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UnwindStackMap::Build() {
|
||||
if (pid_ == 0) {
|
||||
pid_ = getpid();
|
||||
|
@ -118,6 +127,26 @@ std::shared_ptr<unwindstack::Memory> UnwindStackMap::GetProcessMemory() {
|
|||
return process_memory_;
|
||||
}
|
||||
|
||||
#ifdef NO_LIBDEXFILE
|
||||
UnwindDexFile* UnwindStackMap::GetDexFile(uint64_t, unwindstack::MapInfo*) {
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
UnwindDexFile* UnwindStackMap::GetDexFile(uint64_t dex_file_offset, unwindstack::MapInfo* info) {
|
||||
// Lock while we get the data.
|
||||
std::lock_guard<std::mutex> guard(dex_lock_);
|
||||
UnwindDexFile* dex_file;
|
||||
auto entry = dex_files_.find(dex_file_offset);
|
||||
if (entry == dex_files_.end()) {
|
||||
dex_file = UnwindDexFile::Create(dex_file_offset, process_memory_.get(), info);
|
||||
dex_files_[dex_file_offset] = dex_file;
|
||||
} else {
|
||||
dex_file = entry->second;
|
||||
}
|
||||
return dex_file;
|
||||
}
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// BacktraceMap create function.
|
||||
//-------------------------------------------------------------------------
|
||||
|
|
|
@ -21,15 +21,20 @@
|
|||
#include <sys/types.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <unwindstack/JitDebug.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
|
||||
// Forward declarations.
|
||||
class UnwindDexFile;
|
||||
|
||||
class UnwindStackMap : public BacktraceMap {
|
||||
public:
|
||||
explicit UnwindStackMap(pid_t pid);
|
||||
~UnwindStackMap() = default;
|
||||
~UnwindStackMap();
|
||||
|
||||
bool Build() override;
|
||||
|
||||
|
@ -44,12 +49,18 @@ class UnwindStackMap : public BacktraceMap {
|
|||
|
||||
unwindstack::JitDebug* GetJitDebug() { return jit_debug_.get(); }
|
||||
|
||||
UnwindDexFile* GetDexFile(uint64_t dex_file_offset, unwindstack::MapInfo* info);
|
||||
|
||||
protected:
|
||||
uint64_t GetLoadBias(size_t index) override;
|
||||
|
||||
std::unique_ptr<unwindstack::Maps> stack_maps_;
|
||||
std::shared_ptr<unwindstack::Memory> process_memory_;
|
||||
std::unique_ptr<unwindstack::JitDebug> jit_debug_;
|
||||
#ifndef NO_LIBDEXFILE
|
||||
std::mutex dex_lock_;
|
||||
std::unordered_map<uint64_t, UnwindDexFile*> dex_files_;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H
|
||||
|
|
|
@ -81,7 +81,6 @@ struct backtrace_frame_data_t {
|
|||
uint64_t rel_pc; // The relative pc.
|
||||
uint64_t sp; // The top of the stack.
|
||||
size_t stack_size; // The size of the stack, zero indicate an unknown stack size.
|
||||
uint64_t dex_pc; // If non-zero, the Dex PC for the ART interpreter.
|
||||
backtrace_map_t map; // The map associated with the given pc.
|
||||
std::string func_name; // The function name associated with this pc, NULL if not found.
|
||||
uint64_t func_offset; // pc relative to the start of the function, only valid if func_name is not
|
||||
|
|
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
* 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/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include <android-base/test_utils.h>
|
||||
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include <dex/code_item_accessors-no_art-inl.h>
|
||||
#include <dex/standard_dex_file.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "UnwindDexFile.h"
|
||||
|
||||
class MemoryFake : public unwindstack::Memory {
|
||||
public:
|
||||
MemoryFake() = default;
|
||||
virtual ~MemoryFake() = default;
|
||||
|
||||
size_t Read(uint64_t addr, void* buffer, size_t size) override;
|
||||
|
||||
void SetMemory(uint64_t addr, const void* memory, size_t length);
|
||||
|
||||
void Clear() { data_.clear(); }
|
||||
|
||||
private:
|
||||
std::unordered_map<uint64_t, uint8_t> data_;
|
||||
};
|
||||
|
||||
void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
|
||||
const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
|
||||
for (size_t i = 0; i < length; i++, addr++) {
|
||||
auto value = data_.find(addr);
|
||||
if (value != data_.end()) {
|
||||
value->second = src[i];
|
||||
} else {
|
||||
data_.insert({addr, src[i]});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t MemoryFake::Read(uint64_t addr, void* memory, size_t size) {
|
||||
uint8_t* dst = reinterpret_cast<uint8_t*>(memory);
|
||||
for (size_t i = 0; i < size; i++, addr++) {
|
||||
auto value = data_.find(addr);
|
||||
if (value == data_.end()) {
|
||||
return i;
|
||||
}
|
||||
dst[i] = value->second;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
// Borrowed from art/dex/dex_file_test.cc.
|
||||
static constexpr uint32_t kDexData[] = {
|
||||
0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
|
||||
0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
|
||||
0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
|
||||
0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
|
||||
0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
|
||||
0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
|
||||
0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
|
||||
0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
|
||||
0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
|
||||
0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
|
||||
0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
|
||||
0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
|
||||
0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
|
||||
0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
|
||||
0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
|
||||
0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
|
||||
0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
|
||||
};
|
||||
|
||||
TEST(UnwindDexTest, from_file_open_non_exist) {
|
||||
UnwindDexFileFromFile dex_file;
|
||||
ASSERT_FALSE(dex_file.Open(0, "/file/does/not/exist"));
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, from_file_open_too_small) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
ASSERT_EQ(sizeof(art::DexFile::Header) - 2,
|
||||
static_cast<size_t>(
|
||||
TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header)) - 2)));
|
||||
|
||||
// Header too small.
|
||||
UnwindDexFileFromFile dex_file;
|
||||
ASSERT_FALSE(dex_file.Open(0, tf.path));
|
||||
|
||||
// Header correct, file too small.
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
|
||||
ASSERT_EQ(sizeof(art::DexFile::Header), static_cast<size_t>(TEMP_FAILURE_RETRY(write(
|
||||
tf.fd, kDexData, sizeof(art::DexFile::Header)))));
|
||||
ASSERT_FALSE(dex_file.Open(0, tf.path));
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, from_file_open) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
ASSERT_EQ(sizeof(kDexData),
|
||||
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
|
||||
|
||||
UnwindDexFileFromFile dex_file;
|
||||
ASSERT_TRUE(dex_file.Open(0, tf.path));
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, from_file_open_non_zero_offset) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
ASSERT_EQ(0x100, lseek(tf.fd, 0x100, SEEK_SET));
|
||||
ASSERT_EQ(sizeof(kDexData),
|
||||
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
|
||||
|
||||
UnwindDexFileFromFile dex_file;
|
||||
ASSERT_TRUE(dex_file.Open(0x100, tf.path));
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, from_memory_fail_too_small_for_header) {
|
||||
MemoryFake memory;
|
||||
|
||||
memory.SetMemory(0x1000, kDexData, sizeof(art::DexFile::Header) - 1);
|
||||
UnwindDexFileFromMemory dex_file;
|
||||
|
||||
ASSERT_FALSE(dex_file.Open(0x1000, &memory));
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, from_memory_fail_too_small_for_data) {
|
||||
MemoryFake memory;
|
||||
|
||||
memory.SetMemory(0x1000, kDexData, sizeof(kDexData) - 2);
|
||||
UnwindDexFileFromMemory dex_file;
|
||||
|
||||
ASSERT_FALSE(dex_file.Open(0x1000, &memory));
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, from_memory_open) {
|
||||
MemoryFake memory;
|
||||
|
||||
memory.SetMemory(0x1000, kDexData, sizeof(kDexData));
|
||||
UnwindDexFileFromMemory dex_file;
|
||||
|
||||
ASSERT_TRUE(dex_file.Open(0x1000, &memory));
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, create_using_file) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
|
||||
ASSERT_EQ(sizeof(kDexData),
|
||||
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
|
||||
|
||||
MemoryFake memory;
|
||||
unwindstack::MapInfo info(0, 0x10000, 0, 0x5, tf.path);
|
||||
std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x500, &memory, &info));
|
||||
ASSERT_TRUE(dex_file != nullptr);
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, create_using_file_non_zero_start) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
|
||||
ASSERT_EQ(sizeof(kDexData),
|
||||
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
|
||||
|
||||
MemoryFake memory;
|
||||
unwindstack::MapInfo info(0x100, 0x10000, 0, 0x5, tf.path);
|
||||
std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x600, &memory, &info));
|
||||
ASSERT_TRUE(dex_file != nullptr);
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, create_using_file_non_zero_offset) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
|
||||
ASSERT_EQ(sizeof(kDexData),
|
||||
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
|
||||
|
||||
MemoryFake memory;
|
||||
unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, tf.path);
|
||||
std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x400, &memory, &info));
|
||||
ASSERT_TRUE(dex_file != nullptr);
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, create_using_memory_empty_file) {
|
||||
MemoryFake memory;
|
||||
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
|
||||
unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
|
||||
std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x4000, &memory, &info));
|
||||
ASSERT_TRUE(dex_file != nullptr);
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, create_using_memory_file_does_not_exist) {
|
||||
MemoryFake memory;
|
||||
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
|
||||
unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
|
||||
std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x4000, &memory, &info));
|
||||
ASSERT_TRUE(dex_file != nullptr);
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, create_using_memory_file_is_malformed) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
ASSERT_EQ(sizeof(kDexData) - 10,
|
||||
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData) - 10))));
|
||||
|
||||
MemoryFake memory;
|
||||
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
|
||||
unwindstack::MapInfo info(0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
|
||||
std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x4000, &memory, &info));
|
||||
ASSERT_TRUE(dex_file != nullptr);
|
||||
|
||||
// Check it came from memory by clearing memory and verifying it fails.
|
||||
memory.Clear();
|
||||
dex_file.reset(UnwindDexFile::Create(0x4000, &memory, &info));
|
||||
ASSERT_TRUE(dex_file == nullptr);
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, get_method_not_opened) {
|
||||
std::string method("something");
|
||||
uint64_t method_offset = 100;
|
||||
UnwindDexFile dex_file;
|
||||
dex_file.GetMethodInformation(0x100, &method, &method_offset);
|
||||
EXPECT_EQ("something", method);
|
||||
EXPECT_EQ(100U, method_offset);
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, get_method) {
|
||||
MemoryFake memory;
|
||||
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
|
||||
unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
|
||||
std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x4000, &memory, &info));
|
||||
ASSERT_TRUE(dex_file != nullptr);
|
||||
|
||||
std::string method;
|
||||
uint64_t method_offset;
|
||||
dex_file->GetMethodInformation(0x102, &method, &method_offset);
|
||||
EXPECT_EQ("Main.<init>", method);
|
||||
EXPECT_EQ(2U, method_offset);
|
||||
|
||||
method = "not_in_a_method";
|
||||
method_offset = 0x123;
|
||||
dex_file->GetMethodInformation(0x100000, &method, &method_offset);
|
||||
EXPECT_EQ("not_in_a_method", method);
|
||||
EXPECT_EQ(0x123U, method_offset);
|
||||
}
|
Loading…
Reference in New Issue