Create lookup table of DEX symbols.
Create fast lookup table instead of iterating every single time.
This will create the cache as methods are searched for.
Test: 137-cfi
Change-Id: I4be190bb1a637fef5d385b993be6a7e2203a6814
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index b18b0ce..3d982f6 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -68,17 +68,51 @@
return false; // The DEX offset is not within the bytecode of this dex file.
}
- for (uint32_t i = 0; i < dex_file_->NumClassDefs(); ++i) {
- const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(i);
+ if (dex_file_->IsCompactDexFile()) {
+ // The data section of compact dex files might be shared.
+ // Check the subrange unique to this compact dex.
+ const auto& cdex_header = dex_file_->AsCompactDexFile()->GetHeader();
+ uint32_t begin = cdex_header.data_off_ + cdex_header.OwnedDataBegin();
+ uint32_t end = cdex_header.data_off_ + cdex_header.OwnedDataEnd();
+ if (dex_offset < begin || dex_offset >= end) {
+ return false; // The DEX offset is not within the bytecode of this dex file.
+ }
+ }
+
+ // The method data is cached in a std::map indexed by method end offset and
+ // contains the start offset and the method member index.
+ // Only cache the method data as it is searched. Do not read the entire
+ // set of method data into the cache at once.
+ // This is done because many unwinds only find a single frame with dex file
+ // info, so reading the entire method data is wasteful. However, still cache
+ // the data so that anything doing multiple unwinds will have this data
+ // cached for future use.
+
+ // First look in the method cache.
+ auto entry = method_cache_.upper_bound(dex_offset);
+ if (entry != method_cache_.end() && dex_offset >= entry->second.first) {
+ *method_name = dex_file_->PrettyMethod(entry->second.second, false);
+ *method_offset = dex_offset - entry->second.first;
+ return true;
+ }
+
+ // Check the methods we haven't cached.
+ for (; class_def_index_ < dex_file_->NumClassDefs(); class_def_index_++) {
+ const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(class_def_index_);
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()) {
+
+ if (class_it_.get() == nullptr || !class_it_->HasNext()) {
+ class_it_.reset(new art::ClassDataItemIterator(*dex_file_.get(), class_data));
+ }
+
+ for (; class_it_->HasNext(); class_it_->Next()) {
+ if (!class_it_->IsAtMethod()) {
continue;
}
- const art::DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+ const art::DexFile::CodeItem* code_item = class_it_->GetMethodCodeItem();
if (code_item == nullptr) {
continue;
}
@@ -87,11 +121,15 @@
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);
+ uint32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
+ uint32_t offset_end = offset + code.InsnsSizeInCodeUnits() * sizeof(uint16_t);
+ uint32_t member_index = class_it_->GetMemberIndex();
+ method_cache_[offset_end] = std::make_pair(offset, member_index);
+ if (offset <= dex_offset && dex_offset < offset_end) {
+ *method_name = dex_file_->PrettyMethod(member_index, false);
*method_offset = dex_offset - offset;
+ // Move past this element.
+ class_it_->Next();
return true;
}
}
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
index 3ce2f1e..508692d 100644
--- a/libunwindstack/DexFile.h
+++ b/libunwindstack/DexFile.h
@@ -19,8 +19,10 @@
#include <stdint.h>
+#include <map>
#include <memory>
#include <string>
+#include <utility>
#include <vector>
#include <dex/dex_file-inl.h>
@@ -37,7 +39,13 @@
static DexFile* Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info);
protected:
+ void Init();
+
std::unique_ptr<const art::DexFile> dex_file_;
+ std::map<uint32_t, std::pair<uint64_t, uint32_t>> method_cache_; // dex offset to method index.
+
+ uint32_t class_def_index_ = 0;
+ std::unique_ptr<art::ClassDataItemIterator> class_it_;
};
class DexFileFromFile : public DexFile {
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 0b02c5b..4dd8cb0 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -206,15 +206,42 @@
std::string method;
uint64_t method_offset;
- dex_file->GetMethodInformation(0x102, &method, &method_offset);
+ ASSERT_TRUE(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);
+ ASSERT_TRUE(dex_file->GetMethodInformation(0x118, &method, &method_offset));
+ EXPECT_EQ("Main.main", method);
+ EXPECT_EQ(0U, method_offset);
+
+ // Make sure that any data that is cached is still retrievable.
+ ASSERT_TRUE(dex_file->GetMethodInformation(0x104, &method, &method_offset));
+ EXPECT_EQ("Main.<init>", method);
+ EXPECT_EQ(4U, method_offset);
+
+ ASSERT_TRUE(dex_file->GetMethodInformation(0x119, &method, &method_offset));
+ EXPECT_EQ("Main.main", method);
+ EXPECT_EQ(1U, method_offset);
+}
+
+TEST(DexFileTest, get_method_empty) {
+ MemoryFake memory;
+ memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+ MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+ std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
+ ASSERT_TRUE(dex_file != nullptr);
+
+ std::string method;
+ uint64_t method_offset;
+ EXPECT_FALSE(dex_file->GetMethodInformation(0x100000, &method, &method_offset));
+
+ EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset));
+
+ // Make sure that once the whole dex file has been cached, no problems occur.
+ EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset));
+
+ // Choose a value that is in the cached map, but not in a valid method.
+ EXPECT_FALSE(dex_file->GetMethodInformation(0x110, &method, &method_offset));
}
} // namespace unwindstack