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