diff options
author | Christopher Ferris | 2018-07-26 15:01:15 -0500 |
---|---|---|
committer | Gerrit Code Review | 2018-07-26 15:01:15 -0500 |
commit | d1a1202eb7435ff36180bcb6ab39f0861f608fd8 (patch) | |
tree | ca6c04dd69149c5db42a7dc227c501753847f96c | |
parent | d6f97d67e094e8962f231ab290bd4468363fd8b3 (diff) | |
parent | 02d0f7962d17fc05efdebb947e908817d304673d (diff) | |
download | platform-system-core-d1a1202eb7435ff36180bcb6ab39f0861f608fd8.tar.gz platform-system-core-d1a1202eb7435ff36180bcb6ab39f0861f608fd8.tar.xz platform-system-core-d1a1202eb7435ff36180bcb6ab39f0861f608fd8.zip |
Merge "Create lookup table of DEX symbols."
-rw-r--r-- | libunwindstack/DexFile.cpp | 56 | ||||
-rw-r--r-- | libunwindstack/DexFile.h | 8 | ||||
-rw-r--r-- | libunwindstack/tests/DexFileTest.cpp | 39 |
3 files changed, 88 insertions, 15 deletions
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp index b18b0ce3d..3d982f6e9 100644 --- a/libunwindstack/DexFile.cpp +++ b/libunwindstack/DexFile.cpp | |||
@@ -68,17 +68,51 @@ bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name | |||
68 | return false; // The DEX offset is not within the bytecode of this dex file. | 68 | return false; // The DEX offset is not within the bytecode of this dex file. |
69 | } | 69 | } |
70 | 70 | ||
71 | for (uint32_t i = 0; i < dex_file_->NumClassDefs(); ++i) { | 71 | if (dex_file_->IsCompactDexFile()) { |
72 | const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(i); | 72 | // The data section of compact dex files might be shared. |
73 | // Check the subrange unique to this compact dex. | ||
74 | const auto& cdex_header = dex_file_->AsCompactDexFile()->GetHeader(); | ||
75 | uint32_t begin = cdex_header.data_off_ + cdex_header.OwnedDataBegin(); | ||
76 | uint32_t end = cdex_header.data_off_ + cdex_header.OwnedDataEnd(); | ||
77 | if (dex_offset < begin || dex_offset >= end) { | ||
78 | return false; // The DEX offset is not within the bytecode of this dex file. | ||
79 | } | ||
80 | } | ||
81 | |||
82 | // The method data is cached in a std::map indexed by method end offset and | ||
83 | // contains the start offset and the method member index. | ||
84 | // Only cache the method data as it is searched. Do not read the entire | ||
85 | // set of method data into the cache at once. | ||
86 | // This is done because many unwinds only find a single frame with dex file | ||
87 | // info, so reading the entire method data is wasteful. However, still cache | ||
88 | // the data so that anything doing multiple unwinds will have this data | ||
89 | // cached for future use. | ||
90 | |||
91 | // First look in the method cache. | ||
92 | auto entry = method_cache_.upper_bound(dex_offset); | ||
93 | if (entry != method_cache_.end() && dex_offset >= entry->second.first) { | ||
94 | *method_name = dex_file_->PrettyMethod(entry->second.second, false); | ||
95 | *method_offset = dex_offset - entry->second.first; | ||
96 | return true; | ||
97 | } | ||
98 | |||
99 | // Check the methods we haven't cached. | ||
100 | for (; class_def_index_ < dex_file_->NumClassDefs(); class_def_index_++) { | ||
101 | const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(class_def_index_); | ||
73 | const uint8_t* class_data = dex_file_->GetClassData(class_def); | 102 | const uint8_t* class_data = dex_file_->GetClassData(class_def); |
74 | if (class_data == nullptr) { | 103 | if (class_data == nullptr) { |
75 | continue; | 104 | continue; |
76 | } | 105 | } |
77 | for (art::ClassDataItemIterator it(*dex_file_.get(), class_data); it.HasNext(); it.Next()) { | 106 | |
78 | if (!it.IsAtMethod()) { | 107 | if (class_it_.get() == nullptr || !class_it_->HasNext()) { |
108 | class_it_.reset(new art::ClassDataItemIterator(*dex_file_.get(), class_data)); | ||
109 | } | ||
110 | |||
111 | for (; class_it_->HasNext(); class_it_->Next()) { | ||
112 | if (!class_it_->IsAtMethod()) { | ||
79 | continue; | 113 | continue; |
80 | } | 114 | } |
81 | const art::DexFile::CodeItem* code_item = it.GetMethodCodeItem(); | 115 | const art::DexFile::CodeItem* code_item = class_it_->GetMethodCodeItem(); |
82 | if (code_item == nullptr) { | 116 | if (code_item == nullptr) { |
83 | continue; | 117 | continue; |
84 | } | 118 | } |
@@ -87,11 +121,15 @@ bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name | |||
87 | continue; | 121 | continue; |
88 | } | 122 | } |
89 | 123 | ||
90 | uint64_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin(); | 124 | uint32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin(); |
91 | size_t size = code.InsnsSizeInCodeUnits() * sizeof(uint16_t); | 125 | uint32_t offset_end = offset + code.InsnsSizeInCodeUnits() * sizeof(uint16_t); |
92 | if (offset <= dex_offset && dex_offset < offset + size) { | 126 | uint32_t member_index = class_it_->GetMemberIndex(); |
93 | *method_name = dex_file_->PrettyMethod(it.GetMemberIndex(), false); | 127 | method_cache_[offset_end] = std::make_pair(offset, member_index); |
128 | if (offset <= dex_offset && dex_offset < offset_end) { | ||
129 | *method_name = dex_file_->PrettyMethod(member_index, false); | ||
94 | *method_offset = dex_offset - offset; | 130 | *method_offset = dex_offset - offset; |
131 | // Move past this element. | ||
132 | class_it_->Next(); | ||
95 | return true; | 133 | return true; |
96 | } | 134 | } |
97 | } | 135 | } |
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h index 3ce2f1edd..508692d33 100644 --- a/libunwindstack/DexFile.h +++ b/libunwindstack/DexFile.h | |||
@@ -19,8 +19,10 @@ | |||
19 | 19 | ||
20 | #include <stdint.h> | 20 | #include <stdint.h> |
21 | 21 | ||
22 | #include <map> | ||
22 | #include <memory> | 23 | #include <memory> |
23 | #include <string> | 24 | #include <string> |
25 | #include <utility> | ||
24 | #include <vector> | 26 | #include <vector> |
25 | 27 | ||
26 | #include <dex/dex_file-inl.h> | 28 | #include <dex/dex_file-inl.h> |
@@ -37,7 +39,13 @@ class DexFile { | |||
37 | static DexFile* Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info); | 39 | static DexFile* Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info); |
38 | 40 | ||
39 | protected: | 41 | protected: |
42 | void Init(); | ||
43 | |||
40 | std::unique_ptr<const art::DexFile> dex_file_; | 44 | std::unique_ptr<const art::DexFile> dex_file_; |
45 | std::map<uint32_t, std::pair<uint64_t, uint32_t>> method_cache_; // dex offset to method index. | ||
46 | |||
47 | uint32_t class_def_index_ = 0; | ||
48 | std::unique_ptr<art::ClassDataItemIterator> class_it_; | ||
41 | }; | 49 | }; |
42 | 50 | ||
43 | class DexFileFromFile : public DexFile { | 51 | class DexFileFromFile : public DexFile { |
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp index 0b02c5bf1..4dd8cb046 100644 --- a/libunwindstack/tests/DexFileTest.cpp +++ b/libunwindstack/tests/DexFileTest.cpp | |||
@@ -206,15 +206,42 @@ TEST(DexFileTest, get_method) { | |||
206 | 206 | ||
207 | std::string method; | 207 | std::string method; |
208 | uint64_t method_offset; | 208 | uint64_t method_offset; |
209 | dex_file->GetMethodInformation(0x102, &method, &method_offset); | 209 | ASSERT_TRUE(dex_file->GetMethodInformation(0x102, &method, &method_offset)); |
210 | EXPECT_EQ("Main.<init>", method); | 210 | EXPECT_EQ("Main.<init>", method); |
211 | EXPECT_EQ(2U, method_offset); | 211 | EXPECT_EQ(2U, method_offset); |
212 | 212 | ||
213 | method = "not_in_a_method"; | 213 | ASSERT_TRUE(dex_file->GetMethodInformation(0x118, &method, &method_offset)); |
214 | method_offset = 0x123; | 214 | EXPECT_EQ("Main.main", method); |
215 | dex_file->GetMethodInformation(0x100000, &method, &method_offset); | 215 | EXPECT_EQ(0U, method_offset); |
216 | EXPECT_EQ("not_in_a_method", method); | 216 | |
217 | EXPECT_EQ(0x123U, method_offset); | 217 | // Make sure that any data that is cached is still retrievable. |
218 | ASSERT_TRUE(dex_file->GetMethodInformation(0x104, &method, &method_offset)); | ||
219 | EXPECT_EQ("Main.<init>", method); | ||
220 | EXPECT_EQ(4U, method_offset); | ||
221 | |||
222 | ASSERT_TRUE(dex_file->GetMethodInformation(0x119, &method, &method_offset)); | ||
223 | EXPECT_EQ("Main.main", method); | ||
224 | EXPECT_EQ(1U, method_offset); | ||
225 | } | ||
226 | |||
227 | TEST(DexFileTest, get_method_empty) { | ||
228 | MemoryFake memory; | ||
229 | memory.SetMemory(0x4000, kDexData, sizeof(kDexData)); | ||
230 | MapInfo info(0x100, 0x10000, 0x200, 0x5, ""); | ||
231 | std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info)); | ||
232 | ASSERT_TRUE(dex_file != nullptr); | ||
233 | |||
234 | std::string method; | ||
235 | uint64_t method_offset; | ||
236 | EXPECT_FALSE(dex_file->GetMethodInformation(0x100000, &method, &method_offset)); | ||
237 | |||
238 | EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset)); | ||
239 | |||
240 | // Make sure that once the whole dex file has been cached, no problems occur. | ||
241 | EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset)); | ||
242 | |||
243 | // Choose a value that is in the cached map, but not in a valid method. | ||
244 | EXPECT_FALSE(dex_file->GetMethodInformation(0x110, &method, &method_offset)); | ||
218 | } | 245 | } |
219 | 246 | ||
220 | } // namespace unwindstack | 247 | } // namespace unwindstack |