Loading libunwindstack/DexFile.cpp +47 −9 Original line number Diff line number Diff line Loading @@ -68,17 +68,51 @@ bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name 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; } Loading @@ -87,11 +121,15 @@ bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name 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; } } Loading libunwindstack/DexFile.h +8 −0 Original line number Diff line number Diff line Loading @@ -19,8 +19,10 @@ #include <stdint.h> #include <map> #include <memory> #include <string> #include <utility> #include <vector> #include <dex/dex_file-inl.h> Loading @@ -37,7 +39,13 @@ class DexFile { 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 { Loading libunwindstack/tests/DexFileTest.cpp +33 −6 Original line number Diff line number Diff line Loading @@ -206,15 +206,42 @@ TEST(DexFileTest, get_method) { 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 Loading
libunwindstack/DexFile.cpp +47 −9 Original line number Diff line number Diff line Loading @@ -68,17 +68,51 @@ bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name 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; } Loading @@ -87,11 +121,15 @@ bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name 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; } } Loading
libunwindstack/DexFile.h +8 −0 Original line number Diff line number Diff line Loading @@ -19,8 +19,10 @@ #include <stdint.h> #include <map> #include <memory> #include <string> #include <utility> #include <vector> #include <dex/dex_file-inl.h> Loading @@ -37,7 +39,13 @@ class DexFile { 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 { Loading
libunwindstack/tests/DexFileTest.cpp +33 −6 Original line number Diff line number Diff line Loading @@ -206,15 +206,42 @@ TEST(DexFileTest, get_method) { 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