diff options
| author | Christopher Ferris <cferris@google.com> | 2019-01-16 17:23:39 -0800 |
|---|---|---|
| committer | Christopher Ferris <cferris@google.com> | 2019-01-17 20:14:00 -0800 |
| commit | bf373edc3c12a6e6df91770fde36075cc9d3e257 (patch) | |
| tree | f0281cb2e53007d0ef9b3c2455db292da53dc234 /libunwindstack/tests | |
| parent | b94c2e52a674d43705045d0662e6a427aee2e873 (diff) | |
| download | system_core-bf373edc3c12a6e6df91770fde36075cc9d3e257.tar.gz system_core-bf373edc3c12a6e6df91770fde36075cc9d3e257.tar.bz2 system_core-bf373edc3c12a6e6df91770fde36075cc9d3e257.zip | |
Add caching of build id in MapInfo object.
Change the GetBuildID function to return a std::string.
Added benchmark to check how long it takes to get the build id from
a file versus an elf object.
Added a way to get an elf without passing in a valid process_memory and
added tests for this.
Test: New unit tests.
Change-Id: I3029019767e0181c758d611fe635bc1bf72d6e8e
Diffstat (limited to 'libunwindstack/tests')
| -rw-r--r-- | libunwindstack/tests/ElfFake.h | 8 | ||||
| -rw-r--r-- | libunwindstack/tests/ElfInterfaceTest.cpp | 17 | ||||
| -rw-r--r-- | libunwindstack/tests/ElfTest.cpp | 2 | ||||
| -rw-r--r-- | libunwindstack/tests/MapInfoGetBuildIDTest.cpp | 183 | ||||
| -rw-r--r-- | libunwindstack/tests/MapInfoGetElfTest.cpp | 6 |
5 files changed, 200 insertions, 16 deletions
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h index c2bd0f6c6..946bc3c21 100644 --- a/libunwindstack/tests/ElfFake.h +++ b/libunwindstack/tests/ElfFake.h @@ -72,9 +72,7 @@ class ElfInterfaceFake : public ElfInterface { bool GetFunctionName(uint64_t, std::string*, uint64_t*) override; bool GetGlobalVariable(const std::string&, uint64_t*) override; - bool GetBuildID(std::string*) override { - return false; - } + std::string GetBuildID() override { return fake_build_id_; } bool Step(uint64_t, Regs*, Memory*, bool*) override; @@ -82,6 +80,9 @@ class ElfInterfaceFake : public ElfInterface { globals_[global] = offset; } + void FakeSetBuildID(std::string& build_id) { fake_build_id_ = build_id; } + void FakeSetBuildID(const char* build_id) { fake_build_id_ = build_id; } + static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); } static void FakePushStepData(const StepData data) { steps_.push_back(data); } @@ -96,6 +97,7 @@ class ElfInterfaceFake : public ElfInterface { private: std::unordered_map<std::string, uint64_t> globals_; + std::string fake_build_id_; static std::deque<FunctionData> functions_; static std::deque<StepData> steps_; diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp index 6023dc4b8..7239749c6 100644 --- a/libunwindstack/tests/ElfInterfaceTest.cpp +++ b/libunwindstack/tests/ElfInterfaceTest.cpp @@ -1228,9 +1228,7 @@ void ElfInterfaceTest::BuildID() { uint64_t load_bias = 0; ASSERT_TRUE(elf->Init(&load_bias)); - std::string build_id; - ASSERT_TRUE(elf->GetBuildID(&build_id)); - EXPECT_STREQ(build_id.c_str(), "BUILDID"); + ASSERT_EQ("BUILDID", elf->GetBuildID()); } template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType> @@ -1292,9 +1290,7 @@ void ElfInterfaceTest::BuildIDTwoNotes() { uint64_t load_bias = 0; ASSERT_TRUE(elf->Init(&load_bias)); - std::string build_id; - ASSERT_TRUE(elf->GetBuildID(&build_id)); - EXPECT_STREQ(build_id.c_str(), "BUILDID"); + ASSERT_EQ("BUILDID", elf->GetBuildID()); } template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType> @@ -1346,8 +1342,7 @@ void ElfInterfaceTest::BuildIDSectionTooSmallForName () { uint64_t load_bias = 0; ASSERT_TRUE(elf->Init(&load_bias)); - std::string build_id; - ASSERT_FALSE(elf->GetBuildID(&build_id)); + ASSERT_EQ("", elf->GetBuildID()); } template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType> @@ -1399,8 +1394,7 @@ void ElfInterfaceTest::BuildIDSectionTooSmallForDesc () { uint64_t load_bias = 0; ASSERT_TRUE(elf->Init(&load_bias)); - std::string build_id; - ASSERT_FALSE(elf->GetBuildID(&build_id)); + ASSERT_EQ("", elf->GetBuildID()); } template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType> @@ -1452,8 +1446,7 @@ void ElfInterfaceTest::BuildIDSectionTooSmallForHeader () { uint64_t load_bias = 0; ASSERT_TRUE(elf->Init(&load_bias)); - std::string build_id; - ASSERT_FALSE(elf->GetBuildID(&build_id)); + ASSERT_EQ("", elf->GetBuildID()); } TEST_F(ElfInterfaceTest, build_id32) { diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp index f7689ceb7..1ff23064f 100644 --- a/libunwindstack/tests/ElfTest.cpp +++ b/libunwindstack/tests/ElfTest.cpp @@ -311,7 +311,7 @@ class ElfInterfaceMock : public ElfInterface { void InitHeaders(uint64_t) override {} bool GetSoname(std::string*) override { return false; } bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; } - bool GetBuildID(std::string*) override { return false; } + std::string GetBuildID() override { return ""; } MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*)); MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*)); diff --git a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp new file mode 100644 index 000000000..3b89c5961 --- /dev/null +++ b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2019 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 <sys/mman.h> +#include <unistd.h> + +#include <atomic> +#include <memory> +#include <string> +#include <thread> +#include <vector> + +#include <android-base/test_utils.h> + +#include <gtest/gtest.h> + +#include <unwindstack/Elf.h> +#include <unwindstack/MapInfo.h> +#include <unwindstack/Maps.h> +#include <unwindstack/Memory.h> + +#include "ElfFake.h" +#include "ElfTestUtils.h" +#include "MemoryFake.h" + +namespace unwindstack { + +class MapInfoGetBuildIDTest : public ::testing::Test { + protected: + void SetUp() override { + tf_.reset(new TemporaryFile); + + memory_ = new MemoryFake; + elf_ = new ElfFake(new MemoryFake); + elf_interface_ = new ElfInterfaceFake(memory_); + elf_->FakeSetInterface(elf_interface_); + elf_container_.reset(elf_); + map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path)); + } + + void MultipleThreadTest(std::string expected_build_id); + + MemoryFake* memory_; + ElfFake* elf_; + ElfInterfaceFake* elf_interface_; + std::unique_ptr<ElfFake> elf_container_; + std::unique_ptr<MapInfo> map_info_; + std::unique_ptr<TemporaryFile> tf_; +}; + +TEST_F(MapInfoGetBuildIDTest, no_elf_and_no_valid_elf_in_memory) { + MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, ""); + + EXPECT_EQ("", info.GetBuildID()); +} + +TEST_F(MapInfoGetBuildIDTest, from_elf) { + map_info_->elf.reset(elf_container_.release()); + elf_interface_->FakeSetBuildID("FAKE_BUILD_ID"); + + EXPECT_EQ("FAKE_BUILD_ID", map_info_->GetBuildID()); +} + +void MapInfoGetBuildIDTest::MultipleThreadTest(std::string expected_build_id) { + static constexpr size_t kNumConcurrentThreads = 100; + + std::string build_id_values[kNumConcurrentThreads]; + std::vector<std::thread*> threads; + + std::atomic_bool wait; + wait = true; + // Create all of the threads and have them do the GetLoadBias at the same time + // to make it likely that a race will occur. + for (size_t i = 0; i < kNumConcurrentThreads; i++) { + std::thread* thread = new std::thread([i, this, &wait, &build_id_values]() { + while (wait) + ; + build_id_values[i] = map_info_->GetBuildID(); + }); + threads.push_back(thread); + } + + // Set them all going and wait for the threads to finish. + wait = false; + for (auto thread : threads) { + thread->join(); + delete thread; + } + + // Now verify that all of the elf files are exactly the same and valid. + for (size_t i = 0; i < kNumConcurrentThreads; i++) { + EXPECT_EQ(expected_build_id, build_id_values[i]) << "Thread " << i << " mismatched."; + } +} + +TEST_F(MapInfoGetBuildIDTest, multiple_thread_elf_exists) { + map_info_->elf.reset(elf_container_.release()); + elf_interface_->FakeSetBuildID("FAKE_BUILD_ID"); + + MultipleThreadTest("FAKE_BUILD_ID"); +} + +static void InitElfData(int fd) { + Elf32_Ehdr ehdr; + TestInitEhdr(&ehdr, ELFCLASS32, EM_ARM); + ehdr.e_shoff = 0x2000; + ehdr.e_shnum = 3; + ehdr.e_shentsize = sizeof(Elf32_Shdr); + ehdr.e_shstrndx = 2; + off_t offset = 0; + ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET)); + ASSERT_EQ(static_cast<ssize_t>(sizeof(ehdr)), write(fd, &ehdr, sizeof(ehdr))); + + char note_section[128]; + Elf32_Nhdr note_header = {}; + note_header.n_namesz = 4; // "GNU" + note_header.n_descsz = 12; // "ELF_BUILDID" + note_header.n_type = NT_GNU_BUILD_ID; + memcpy(¬e_section, ¬e_header, sizeof(note_header)); + size_t note_offset = sizeof(note_header); + memcpy(¬e_section[note_offset], "GNU", sizeof("GNU")); + note_offset += sizeof("GNU"); + memcpy(¬e_section[note_offset], "ELF_BUILDID", sizeof("ELF_BUILDID")); + note_offset += sizeof("ELF_BUILDID"); + + Elf32_Shdr shdr = {}; + shdr.sh_type = SHT_NOTE; + shdr.sh_name = 0x500; + shdr.sh_offset = 0xb000; + shdr.sh_size = sizeof(note_section); + offset += ehdr.e_shoff + sizeof(shdr); + ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET)); + ASSERT_EQ(static_cast<ssize_t>(sizeof(shdr)), write(fd, &shdr, sizeof(shdr))); + + // The string data for section header names. + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_STRTAB; + shdr.sh_name = 0x20000; + shdr.sh_offset = 0xf000; + shdr.sh_size = 0x1000; + offset += sizeof(shdr); + ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET)); + ASSERT_EQ(static_cast<ssize_t>(sizeof(shdr)), write(fd, &shdr, sizeof(shdr))); + + offset = 0xf500; + ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET)); + ASSERT_EQ(static_cast<ssize_t>(sizeof(".note.gnu.build-id")), + write(fd, ".note.gnu.build-id", sizeof(".note.gnu.build-id"))); + + offset = 0xb000; + ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET)); + ASSERT_EQ(static_cast<ssize_t>(sizeof(note_section)), + write(fd, note_section, sizeof(note_section))); +} + +TEST_F(MapInfoGetBuildIDTest, from_memory) { + InitElfData(tf_->fd); + + EXPECT_EQ("ELF_BUILDID", map_info_->GetBuildID()); +} + +TEST_F(MapInfoGetBuildIDTest, multiple_thread_elf_exists_in_memory) { + InitElfData(tf_->fd); + + MultipleThreadTest("ELF_BUILDID"); +} + +} // namespace unwindstack diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp index 99f8fa3b0..d7b848544 100644 --- a/libunwindstack/tests/MapInfoGetElfTest.cpp +++ b/libunwindstack/tests/MapInfoGetElfTest.cpp @@ -88,6 +88,12 @@ TEST_F(MapInfoGetElfTest, valid32) { ASSERT_TRUE(elf->valid()); EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type()); EXPECT_EQ(ELFCLASS32, elf->class_type()); + + // Now verify that an empty process memory returns an invalid elf object. + info.elf.reset(); + elf = info.GetElf(std::shared_ptr<Memory>(), ARCH_ARM); + ASSERT_TRUE(elf != nullptr); + ASSERT_FALSE(elf->valid()); } TEST_F(MapInfoGetElfTest, valid64) { |
