diff options
author | Christopher Ferris <cferris@google.com> | 2018-01-25 12:15:56 -0800 |
---|---|---|
committer | Christopher Ferris <cferris@google.com> | 2018-01-25 17:11:33 -0800 |
commit | 0b79ae155f0a5740a04723071e9f05f2527cbb86 (patch) | |
tree | 48a9e16eaa9ec0e26f685b9a096fcc885e340069 /libunwindstack | |
parent | dc0edb586dcfbc3ce441d2ac34a67309bd76d6fe (diff) | |
download | core-0b79ae155f0a5740a04723071e9f05f2527cbb86.tar.gz core-0b79ae155f0a5740a04723071e9f05f2527cbb86.tar.bz2 core-0b79ae155f0a5740a04723071e9f05f2527cbb86.zip |
Add a global elf cache.
Bug: 65682279
Test: Ran new unit tests.
Change-Id: I19c64614b2b11a27f58204d4cc34913c02e04c36
Diffstat (limited to 'libunwindstack')
-rw-r--r-- | libunwindstack/Android.bp | 1 | ||||
-rw-r--r-- | libunwindstack/Elf.cpp | 42 | ||||
-rw-r--r-- | libunwindstack/MapInfo.cpp | 40 | ||||
-rw-r--r-- | libunwindstack/include/unwindstack/Elf.h | 13 | ||||
-rw-r--r-- | libunwindstack/include/unwindstack/MapInfo.h | 5 | ||||
-rw-r--r-- | libunwindstack/tests/ElfCacheTest.cpp | 201 | ||||
-rw-r--r-- | libunwindstack/tests/JitDebugTest.cpp | 6 | ||||
-rw-r--r-- | libunwindstack/tests/MapInfoGetElfTest.cpp | 17 | ||||
-rw-r--r-- | libunwindstack/tests/MapInfoGetLoadBiasTest.cpp | 6 | ||||
-rw-r--r-- | libunwindstack/tests/RegsTest.cpp | 2 | ||||
-rw-r--r-- | libunwindstack/tests/UnwinderTest.cpp | 10 |
11 files changed, 313 insertions, 30 deletions
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 8dae956c6..28d7e64c8 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -124,6 +124,7 @@ cc_test { "tests/DwarfOpTest.cpp", "tests/DwarfSectionTest.cpp", "tests/DwarfSectionImplTest.cpp", + "tests/ElfCacheTest.cpp", "tests/ElfFake.cpp", "tests/ElfInterfaceArmTest.cpp", "tests/ElfInterfaceTest.cpp", diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp index f120da2b2..dbf772e5d 100644 --- a/libunwindstack/Elf.cpp +++ b/libunwindstack/Elf.cpp @@ -35,6 +35,10 @@ namespace unwindstack { +bool Elf::cache_enabled_; +std::unordered_map<std::string, std::shared_ptr<Elf>>* Elf::cache_; +std::mutex* Elf::cache_lock_; + bool Elf::Init(bool init_gnu_debugdata) { load_bias_ = 0; if (!memory_) { @@ -301,4 +305,42 @@ uint64_t Elf::GetLoadBias(Memory* memory) { return 0; } +void Elf::SetCachingEnabled(bool enable) { + if (!cache_enabled_ && enable) { + cache_enabled_ = true; + cache_ = new std::unordered_map<std::string, std::shared_ptr<Elf>>; + cache_lock_ = new std::mutex; + } else if (cache_enabled_ && !enable) { + cache_enabled_ = false; + delete cache_; + delete cache_lock_; + } +} + +void Elf::CacheLock() { + cache_lock_->lock(); +} + +void Elf::CacheUnlock() { + cache_lock_->unlock(); +} + +void Elf::CacheAdd(MapInfo* info) { + if (info->offset == 0) { + (*cache_)[info->name] = info->elf; + } else { + std::string name(info->name + ':' + std::to_string(info->offset)); + (*cache_)[name] = info->elf; + } +} + +bool Elf::CacheGet(const std::string& name, std::shared_ptr<Elf>* elf) { + auto entry = cache_->find(name); + if (entry != cache_->end()) { + *elf = entry->second; + return true; + } + return false; +} + } // namespace unwindstack diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp index 89fe038dc..0c153351d 100644 --- a/libunwindstack/MapInfo.cpp +++ b/libunwindstack/MapInfo.cpp @@ -109,16 +109,46 @@ Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gn // Make sure no other thread is trying to add the elf to this map. std::lock_guard<std::mutex> guard(mutex_); - if (elf) { - return elf; + if (elf.get() != nullptr) { + return elf.get(); } - elf = new Elf(CreateMemory(process_memory)); - elf->Init(init_gnu_debugdata); + bool locked = false; + if (Elf::CachingEnabled() && !name.empty()) { + Elf::CacheLock(); + locked = true; + if (offset != 0) { + std::string hash(name + ':' + std::to_string(offset)); + if (Elf::CacheGet(hash, &elf)) { + Elf::CacheUnlock(); + return elf.get(); + } + } else if (Elf::CacheGet(name, &elf)) { + Elf::CacheUnlock(); + return elf.get(); + } + } + Memory* memory = CreateMemory(process_memory); + if (locked && offset != 0 && elf_offset != 0) { + // In this case, the whole file is the elf, need to see if the elf + // data was cached. + if (Elf::CacheGet(name, &elf)) { + delete memory; + Elf::CacheUnlock(); + return elf.get(); + } + } + elf.reset(new Elf(memory)); // If the init fails, keep the elf around as an invalid object so we // don't try to reinit the object. - return elf; + elf->Init(init_gnu_debugdata); + + if (locked) { + Elf::CacheAdd(this); + Elf::CacheUnlock(); + } + return elf.get(); } uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) { diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h index e562d6de9..a874709d2 100644 --- a/libunwindstack/include/unwindstack/Elf.h +++ b/libunwindstack/include/unwindstack/Elf.h @@ -22,6 +22,7 @@ #include <memory> #include <mutex> #include <string> +#include <unordered_map> #include <unwindstack/ElfInterface.h> #include <unwindstack/Memory.h> @@ -96,6 +97,14 @@ class Elf { static uint64_t GetLoadBias(Memory* memory); + static void SetCachingEnabled(bool enable); + static bool CachingEnabled() { return cache_enabled_; } + + static void CacheLock(); + static void CacheUnlock(); + static void CacheAdd(MapInfo* info); + static bool CacheGet(const std::string& name, std::shared_ptr<Elf>* elf); + protected: bool valid_ = false; uint64_t load_bias_ = 0; @@ -109,6 +118,10 @@ class Elf { std::unique_ptr<Memory> gnu_debugdata_memory_; std::unique_ptr<ElfInterface> gnu_debugdata_interface_; + + static bool cache_enabled_; + static std::unordered_map<std::string, std::shared_ptr<Elf>>* cache_; + static std::mutex* cache_lock_; }; } // namespace unwindstack diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h index 22e48f71c..a57fe68e9 100644 --- a/libunwindstack/include/unwindstack/MapInfo.h +++ b/libunwindstack/include/unwindstack/MapInfo.h @@ -20,6 +20,7 @@ #include <stdint.h> #include <atomic> +#include <memory> #include <mutex> #include <string> @@ -40,14 +41,14 @@ struct MapInfo { flags(flags), name(name), load_bias(static_cast<uint64_t>(-1)) {} - ~MapInfo() { delete elf; } + ~MapInfo() = default; uint64_t start = 0; uint64_t end = 0; uint64_t offset = 0; uint16_t flags = 0; std::string name; - Elf* elf = nullptr; + std::shared_ptr<Elf> elf; // This value is only non-zero if the offset is non-zero but there is // no elf signature found at that offset. This indicates that the // entire file is represented by the Memory object returned by CreateMemory, diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp new file mode 100644 index 000000000..0086c9edb --- /dev/null +++ b/libunwindstack/tests/ElfCacheTest.cpp @@ -0,0 +1,201 @@ +/* + * 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 <unistd.h> + +#include <android-base/file.h> +#include <android-base/test_utils.h> + +#include <gtest/gtest.h> + +#include <unwindstack/Elf.h> +#include <unwindstack/MapInfo.h> + +#include "ElfTestUtils.h" +#include "MemoryFake.h" + +namespace unwindstack { + +class ElfCacheTest : public ::testing::Test { + protected: + static void SetUpTestCase() { memory_.reset(new MemoryFake); } + + void SetUp() override { Elf::SetCachingEnabled(true); } + + void TearDown() override { Elf::SetCachingEnabled(false); } + + void WriteElfFile(uint64_t offset, TemporaryFile* tf, uint32_t type) { + ASSERT_TRUE(type == EM_ARM || type == EM_386 || type == EM_X86_64); + size_t ehdr_size; + Elf32_Ehdr ehdr32; + Elf64_Ehdr ehdr64; + void* ptr; + if (type == EM_ARM || type == EM_386) { + ehdr_size = sizeof(ehdr32); + ptr = &ehdr32; + TestInitEhdr(&ehdr32, ELFCLASS32, type); + } else { + ehdr_size = sizeof(ehdr64); + ptr = &ehdr64; + TestInitEhdr(&ehdr64, ELFCLASS64, type); + } + + ASSERT_EQ(offset, static_cast<uint64_t>(lseek(tf->fd, offset, SEEK_SET))); + ASSERT_TRUE(android::base::WriteFully(tf->fd, ptr, ehdr_size)); + } + + void VerifyWithinSameMap(bool cache_enabled); + void VerifySameMap(bool cache_enabled); + + static std::shared_ptr<Memory> memory_; +}; + +std::shared_ptr<Memory> ElfCacheTest::memory_; + +void ElfCacheTest::VerifySameMap(bool cache_enabled) { + if (!cache_enabled) { + Elf::SetCachingEnabled(false); + } + + TemporaryFile tf; + ASSERT_TRUE(tf.fd != -1); + WriteElfFile(0, &tf, EM_ARM); + close(tf.fd); + + uint64_t start = 0x1000; + uint64_t end = 0x20000; + MapInfo info1(start, end, 0, 0x5, tf.path); + MapInfo info2(start, end, 0, 0x5, tf.path); + + Elf* elf1 = info1.GetElf(memory_, true); + ASSERT_TRUE(elf1->valid()); + Elf* elf2 = info2.GetElf(memory_, true); + ASSERT_TRUE(elf2->valid()); + + if (cache_enabled) { + EXPECT_EQ(elf1, elf2); + } else { + EXPECT_NE(elf1, elf2); + } +} + +TEST_F(ElfCacheTest, no_caching) { + VerifySameMap(false); +} + +TEST_F(ElfCacheTest, caching_invalid_elf) { + VerifySameMap(true); +} + +void ElfCacheTest::VerifyWithinSameMap(bool cache_enabled) { + if (!cache_enabled) { + Elf::SetCachingEnabled(false); + } + + TemporaryFile tf; + ASSERT_TRUE(tf.fd != -1); + WriteElfFile(0, &tf, EM_ARM); + WriteElfFile(0x100, &tf, EM_386); + WriteElfFile(0x200, &tf, EM_X86_64); + lseek(tf.fd, 0x500, SEEK_SET); + uint8_t value = 0; + write(tf.fd, &value, 1); + close(tf.fd); + + uint64_t start = 0x1000; + uint64_t end = 0x20000; + // Will have an elf at offset 0 in file. + MapInfo info0_1(start, end, 0, 0x5, tf.path); + MapInfo info0_2(start, end, 0, 0x5, tf.path); + // Will have an elf at offset 0x100 in file. + MapInfo info100_1(start, end, 0x100, 0x5, tf.path); + MapInfo info100_2(start, end, 0x100, 0x5, tf.path); + // Will have an elf at offset 0x200 in file. + MapInfo info200_1(start, end, 0x200, 0x5, tf.path); + MapInfo info200_2(start, end, 0x200, 0x5, tf.path); + // Will have an elf at offset 0 in file. + MapInfo info300_1(start, end, 0x300, 0x5, tf.path); + MapInfo info300_2(start, end, 0x300, 0x5, tf.path); + + Elf* elf0_1 = info0_1.GetElf(memory_, true); + ASSERT_TRUE(elf0_1->valid()); + EXPECT_EQ(ARCH_ARM, elf0_1->arch()); + Elf* elf0_2 = info0_2.GetElf(memory_, true); + ASSERT_TRUE(elf0_2->valid()); + EXPECT_EQ(ARCH_ARM, elf0_2->arch()); + EXPECT_EQ(0U, info0_1.elf_offset); + EXPECT_EQ(0U, info0_2.elf_offset); + if (cache_enabled) { + EXPECT_EQ(elf0_1, elf0_2); + } else { + EXPECT_NE(elf0_1, elf0_2); + } + + Elf* elf100_1 = info100_1.GetElf(memory_, true); + ASSERT_TRUE(elf100_1->valid()); + EXPECT_EQ(ARCH_X86, elf100_1->arch()); + Elf* elf100_2 = info100_2.GetElf(memory_, true); + ASSERT_TRUE(elf100_2->valid()); + EXPECT_EQ(ARCH_X86, elf100_2->arch()); + EXPECT_EQ(0U, info100_1.elf_offset); + EXPECT_EQ(0U, info100_2.elf_offset); + if (cache_enabled) { + EXPECT_EQ(elf100_1, elf100_2); + } else { + EXPECT_NE(elf100_1, elf100_2); + } + + Elf* elf200_1 = info200_1.GetElf(memory_, true); + ASSERT_TRUE(elf200_1->valid()); + EXPECT_EQ(ARCH_X86_64, elf200_1->arch()); + Elf* elf200_2 = info200_2.GetElf(memory_, true); + ASSERT_TRUE(elf200_2->valid()); + EXPECT_EQ(ARCH_X86_64, elf200_2->arch()); + EXPECT_EQ(0U, info200_1.elf_offset); + EXPECT_EQ(0U, info200_2.elf_offset); + if (cache_enabled) { + EXPECT_EQ(elf200_1, elf200_2); + } else { + EXPECT_NE(elf200_1, elf200_2); + } + + Elf* elf300_1 = info300_1.GetElf(memory_, true); + ASSERT_TRUE(elf300_1->valid()); + EXPECT_EQ(ARCH_ARM, elf300_1->arch()); + Elf* elf300_2 = info300_2.GetElf(memory_, true); + ASSERT_TRUE(elf300_2->valid()); + EXPECT_EQ(ARCH_ARM, elf300_2->arch()); + EXPECT_EQ(0x300U, info300_1.elf_offset); + EXPECT_EQ(0x300U, info300_2.elf_offset); + if (cache_enabled) { + EXPECT_EQ(elf300_1, elf300_2); + EXPECT_EQ(elf0_1, elf300_1); + } else { + EXPECT_NE(elf300_1, elf300_2); + EXPECT_NE(elf0_1, elf300_1); + } +} + +TEST_F(ElfCacheTest, no_caching_valid_elf_offset_non_zero) { + VerifyWithinSameMap(false); +} + +TEST_F(ElfCacheTest, caching_valid_elf_offset_non_zero) { + VerifyWithinSameMap(true); +} + +} // namespace unwindstack diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp index 1a5098916..37628f893 100644 --- a/libunwindstack/tests/JitDebugTest.cpp +++ b/libunwindstack/tests/JitDebugTest.cpp @@ -62,7 +62,7 @@ class JitDebugTest : public ::testing::Test { ElfInterfaceFake* interface = new ElfInterfaceFake(elf_memories_.back()); elf->FakeSetInterface(interface); interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800); - map_info->elf = elf; + map_info->elf.reset(elf); map_info = maps_->Get(5); ASSERT_TRUE(map_info != nullptr); @@ -72,7 +72,7 @@ class JitDebugTest : public ::testing::Test { interface = new ElfInterfaceFake(elf_memories_.back()); elf->FakeSetInterface(interface); interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800); - map_info->elf = elf; + map_info->elf.reset(elf); map_info = maps_->Get(6); ASSERT_TRUE(map_info != nullptr); @@ -82,7 +82,7 @@ class JitDebugTest : public ::testing::Test { interface = new ElfInterfaceFake(elf_memories_.back()); elf->FakeSetInterface(interface); interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800); - map_info->elf = elf; + map_info->elf.reset(elf); } template <typename EhdrType, typename ShdrType> diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp index 948597b8e..f59950375 100644 --- a/libunwindstack/tests/MapInfoGetElfTest.cpp +++ b/libunwindstack/tests/MapInfoGetElfTest.cpp @@ -180,16 +180,14 @@ TEST_F(MapInfoGetElfTest, end_le_start) { ASSERT_TRUE(elf != nullptr); ASSERT_FALSE(elf->valid()); - delete info.elf; - info.elf = nullptr; + info.elf.reset(); info.end = 0xfff; elf = info.GetElf(process_memory_, false); ASSERT_TRUE(elf != nullptr); ASSERT_FALSE(elf->valid()); // Make sure this test is valid. - delete info.elf; - info.elf = nullptr; + info.elf.reset(); info.end = 0x2000; elf = info.GetElf(process_memory_, false); ASSERT_TRUE(elf != nullptr); @@ -328,8 +326,7 @@ TEST_F(MapInfoGetElfTest, process_memory_not_read_only) { ASSERT_TRUE(elf != nullptr); ASSERT_FALSE(elf->valid()); - delete info.elf; - info.elf = nullptr; + info.elf.reset(); info.flags = PROT_READ; elf = info.GetElf(process_memory_, false); ASSERT_TRUE(elf->valid()); @@ -352,15 +349,13 @@ TEST_F(MapInfoGetElfTest, check_device_maps) { ASSERT_FALSE(elf->valid()); // Set the name to nothing to verify that it still fails. - delete info.elf; - info.elf = nullptr; + info.elf.reset(); info.name = ""; elf = info.GetElf(process_memory_, false); ASSERT_FALSE(elf->valid()); // Change the flags and verify the elf is valid now. - delete info.elf; - info.elf = nullptr; + info.elf.reset(); info.flags = PROT_READ; elf = info.GetElf(process_memory_, false); ASSERT_TRUE(elf->valid()); @@ -403,7 +398,7 @@ TEST_F(MapInfoGetElfTest, multiple_thread_get_elf) { } // Now verify that all of the elf files are exactly the same and valid. - Elf* elf = info.elf; + Elf* elf = info.elf.get(); ASSERT_TRUE(elf != nullptr); EXPECT_TRUE(elf->valid()); for (size_t i = 0; i < kNumConcurrentThreads; i++) { diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp index 631036b87..7e64a8a67 100644 --- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp +++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp @@ -69,7 +69,7 @@ TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) { } TEST_F(MapInfoGetLoadBiasTest, load_bias_cached_from_elf) { - map_info_->elf = elf_container_.release(); + map_info_->elf.reset(elf_container_.release()); elf_->FakeSetLoadBias(0); EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_)); @@ -79,7 +79,7 @@ TEST_F(MapInfoGetLoadBiasTest, load_bias_cached_from_elf) { } TEST_F(MapInfoGetLoadBiasTest, elf_exists) { - map_info_->elf = elf_container_.release(); + map_info_->elf.reset(elf_container_.release()); elf_->FakeSetLoadBias(0); EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_)); @@ -122,7 +122,7 @@ void MapInfoGetLoadBiasTest::MultipleThreadTest(uint64_t expected_load_bias) { } TEST_F(MapInfoGetLoadBiasTest, multiple_thread_elf_exists) { - map_info_->elf = elf_container_.release(); + map_info_->elf.reset(elf_container_.release()); elf_->FakeSetLoadBias(0x1000); MultipleThreadTest(0x1000); diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp index 15d54586d..7c063739d 100644 --- a/libunwindstack/tests/RegsTest.cpp +++ b/libunwindstack/tests/RegsTest.cpp @@ -184,7 +184,7 @@ TEST_F(RegsTest, elf_invalid) { RegsMips64 regs_mips64; MapInfo map_info(0x1000, 0x2000); Elf* invalid_elf = new Elf(new MemoryFake); - map_info.elf = invalid_elf; + map_info.elf.reset(invalid_elf); regs_arm.set_pc(0x1500); EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info)); diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp index bf2c1d892..45cf9074d 100644 --- a/libunwindstack/tests/UnwinderTest.cpp +++ b/libunwindstack/tests/UnwinderTest.cpp @@ -60,7 +60,7 @@ class UnwinderTest : public ::testing::Test { maps_.FakeClear(); MapInfo* info = new MapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so"); ElfFake* elf = new ElfFake(new MemoryFake); - info->elf = elf; + info->elf.reset(elf); elf->FakeSetInterface(new ElfInterfaceFake(nullptr)); maps_.FakeAddMapInfo(info); @@ -73,25 +73,25 @@ class UnwinderTest : public ::testing::Test { info = new MapInfo(0x20000, 0x22000, 0, PROT_READ | PROT_WRITE, "/system/fake/libunwind.so"); elf = new ElfFake(new MemoryFake); - info->elf = elf; + info->elf.reset(elf); elf->FakeSetInterface(new ElfInterfaceFake(nullptr)); maps_.FakeAddMapInfo(info); info = new MapInfo(0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so"); elf = new ElfFake(new MemoryFake); - info->elf = elf; + info->elf.reset(elf); elf->FakeSetInterface(new ElfInterfaceFake(nullptr)); maps_.FakeAddMapInfo(info); info = new MapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so"); elf = new ElfFake(new MemoryFake); - info->elf = elf; + info->elf.reset(elf); elf->FakeSetInterface(new ElfInterfaceFake(nullptr)); maps_.FakeAddMapInfo(info); info = new MapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk"); elf = new ElfFake(new MemoryFake); - info->elf = elf; + info->elf.reset(elf); elf->FakeSetInterface(new ElfInterfaceFake(nullptr)); maps_.FakeAddMapInfo(info); |