Merge "Add caching of build id in MapInfo object."

This commit is contained in:
Christopher Ferris 2019-01-19 00:32:36 +00:00 committed by Gerrit Code Review
commit b26b07d845
14 changed files with 478 additions and 47 deletions

View File

@ -191,6 +191,7 @@ cc_test {
"tests/LocalUnwinderTest.cpp",
"tests/LogFake.cpp",
"tests/MapInfoCreateMemoryTest.cpp",
"tests/MapInfoGetBuildIDTest.cpp",
"tests/MapInfoGetElfTest.cpp",
"tests/MapInfoGetLoadBiasTest.cpp",
"tests/MapInfoTest.cpp",
@ -343,6 +344,7 @@ cc_benchmark {
],
shared_libs: [
"libbase",
"libunwindstack",
],
}

View File

@ -140,8 +140,11 @@ bool Elf::GetGlobalVariable(const std::string& name, uint64_t* memory_address) {
return true;
}
bool Elf::GetBuildID(std::string* build_id) {
return valid_ && interface_->GetBuildID(build_id);
std::string Elf::GetBuildID() {
if (!valid_) {
return "";
}
return interface_->GetBuildID();
}
void Elf::GetLastError(ErrorData* data) {
@ -384,4 +387,22 @@ bool Elf::CacheGet(MapInfo* info) {
return false;
}
std::string Elf::GetBuildID(Memory* memory) {
if (!IsValidElf(memory)) {
return "";
}
uint8_t class_type;
if (!memory->Read(EI_CLASS, &class_type, 1)) {
return "";
}
if (class_type == ELFCLASS32) {
return ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(memory);
} else if (class_type == ELFCLASS64) {
return ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(memory);
}
return "";
}
} // namespace unwindstack

View File

@ -238,31 +238,31 @@ void ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias)
}
template <typename NhdrType>
bool ElfInterface::ReadBuildID(std::string* build_id) {
std::string ElfInterface::ReadBuildID() {
// Ensure there is no overflow in any of the calulations below.
uint64_t tmp;
if (__builtin_add_overflow(gnu_build_id_offset_, gnu_build_id_size_, &tmp)) {
return false;
return "";
}
uint64_t offset = 0;
while (offset < gnu_build_id_size_) {
if (gnu_build_id_size_ - offset < sizeof(NhdrType)) {
return false;
return "";
}
NhdrType hdr;
if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &hdr, sizeof(hdr))) {
return false;
return "";
}
offset += sizeof(hdr);
if (gnu_build_id_size_ - offset < hdr.n_namesz) {
return false;
return "";
}
if (hdr.n_namesz > 0) {
std::string name(hdr.n_namesz, '\0');
if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &(name[0]), hdr.n_namesz)) {
return false;
return "";
}
// Trim trailing \0 as GNU is stored as a C string in the ELF file.
@ -273,18 +273,20 @@ bool ElfInterface::ReadBuildID(std::string* build_id) {
offset += (hdr.n_namesz + 3) & ~3;
if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
if (gnu_build_id_size_ - offset < hdr.n_descsz) {
return false;
if (gnu_build_id_size_ - offset < hdr.n_descsz || hdr.n_descsz == 0) {
return "";
}
build_id->resize(hdr.n_descsz);
return memory_->ReadFully(gnu_build_id_offset_ + offset, &(*build_id)[0],
hdr.n_descsz);
std::string build_id(hdr.n_descsz - 1, '\0');
if (memory_->ReadFully(gnu_build_id_offset_ + offset, &build_id[0], hdr.n_descsz)) {
return build_id;
}
return "";
}
}
// Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
offset += (hdr.n_descsz + 3) & ~3;
}
return false;
return "";
}
template <typename EhdrType, typename ShdrType>
@ -536,6 +538,103 @@ void ElfInterface::GetMaxSizeWithTemplate(Memory* memory, uint64_t* size) {
*size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
}
template <typename EhdrType, typename ShdrType>
bool GetBuildIDInfo(Memory* memory, uint64_t* build_id_offset, uint64_t* build_id_size) {
EhdrType ehdr;
if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
return false;
}
uint64_t offset = ehdr.e_shoff;
uint64_t sec_offset;
uint64_t sec_size;
ShdrType shdr;
if (ehdr.e_shstrndx >= ehdr.e_shnum) {
return false;
}
uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
if (!memory->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
return false;
}
sec_offset = shdr.sh_offset;
sec_size = shdr.sh_size;
// Skip the first header, it's always going to be NULL.
offset += ehdr.e_shentsize;
for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
if (!memory->ReadFully(offset, &shdr, sizeof(shdr))) {
return false;
}
std::string name;
if (shdr.sh_type == SHT_NOTE && shdr.sh_name < sec_size &&
memory->ReadString(sec_offset + shdr.sh_name, &name) && name == ".note.gnu.build-id") {
*build_id_offset = shdr.sh_offset;
*build_id_size = shdr.sh_size;
return true;
}
}
return false;
}
template <typename EhdrType, typename ShdrType, typename NhdrType>
std::string ElfInterface::ReadBuildIDFromMemory(Memory* memory) {
uint64_t note_offset;
uint64_t note_size;
if (!GetBuildIDInfo<EhdrType, ShdrType>(memory, &note_offset, &note_size)) {
return "";
}
// Ensure there is no overflow in any of the calculations below.
uint64_t tmp;
if (__builtin_add_overflow(note_offset, note_size, &tmp)) {
return "";
}
uint64_t offset = 0;
while (offset < note_size) {
if (note_size - offset < sizeof(NhdrType)) {
return "";
}
NhdrType hdr;
if (!memory->ReadFully(note_offset + offset, &hdr, sizeof(hdr))) {
return "";
}
offset += sizeof(hdr);
if (note_size - offset < hdr.n_namesz) {
return "";
}
if (hdr.n_namesz > 0) {
std::string name(hdr.n_namesz, '\0');
if (!memory->ReadFully(note_offset + offset, &(name[0]), hdr.n_namesz)) {
return "";
}
// Trim trailing \0 as GNU is stored as a C string in the ELF file.
if (name.back() == '\0') name.resize(name.size() - 1);
// Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
offset += (hdr.n_namesz + 3) & ~3;
if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
if (note_size - offset < hdr.n_descsz || hdr.n_descsz == 0) {
return "";
}
std::string build_id(hdr.n_descsz - 1, '\0');
if (memory->ReadFully(note_offset + offset, &build_id[0], hdr.n_descsz)) {
return build_id;
}
return "";
}
}
// Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
offset += (hdr.n_descsz + 3) & ~3;
}
return "";
}
// Instantiate all of the needed template functions.
template void ElfInterface::InitHeadersWithTemplate<uint32_t>(uint64_t);
template void ElfInterface::InitHeadersWithTemplate<uint64_t>(uint64_t);
@ -551,8 +650,8 @@ template void ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf
template void ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
template void ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
template bool ElfInterface::ReadBuildID<Elf32_Nhdr>(std::string*);
template bool ElfInterface::ReadBuildID<Elf64_Nhdr>(std::string*);
template std::string ElfInterface::ReadBuildID<Elf32_Nhdr>();
template std::string ElfInterface::ReadBuildID<Elf64_Nhdr>();
template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
@ -571,4 +670,9 @@ template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t
template uint64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
template uint64_t ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(Memory*);
template std::string ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(
Memory*);
template std::string ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(
Memory*);
} // namespace unwindstack

View File

@ -146,6 +146,10 @@ Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
}
}
if (process_memory == nullptr) {
return nullptr;
}
// Need to verify that this elf is valid. It's possible that
// only part of the elf file to be mapped into memory is in the executable
// map. In this case, there will be another read-only map that includes the
@ -263,4 +267,48 @@ uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
return cur_load_bias;
}
MapInfo::~MapInfo() {
uintptr_t id = build_id.load();
if (id != 0) {
delete reinterpret_cast<std::string*>(id);
}
}
std::string MapInfo::GetBuildID() {
uintptr_t id = build_id.load();
if (build_id != 0) {
return *reinterpret_cast<std::string*>(id);
}
// No need to lock, at worst if multiple threads do this at the same
// time it should be detected and only one thread should win and
// save the data.
std::unique_ptr<std::string> cur_build_id(new std::string);
// Now need to see if the elf object exists.
// Make sure no other thread is trying to add the elf to this map.
mutex_.lock();
Elf* elf_obj = elf.get();
mutex_.unlock();
if (elf_obj != nullptr) {
*cur_build_id = elf_obj->GetBuildID();
} else {
// This will only work if we can get the file associated with this memory.
// If this is only available in memory, then the section name information
// is not present and we will not be able to find the build id info.
std::unique_ptr<Memory> memory(GetFileMemory());
if (memory != nullptr) {
*cur_build_id = Elf::GetBuildID(memory.get());
}
}
id = reinterpret_cast<uintptr_t>(cur_build_id.get());
uintptr_t expected_id = 0;
if (build_id.compare_exchange_weak(expected_id, id)) {
// Value saved, so make sure the memory is not freed.
cur_build_id.release();
}
return *reinterpret_cast<std::string*>(id);
}
} // namespace unwindstack

View File

@ -20,6 +20,9 @@
#include <benchmark/benchmark.h>
#include <android-base/strings.h>
#include <unwindstack/Elf.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
@ -80,4 +83,63 @@ static void BM_cached_unwind(benchmark::State& state) {
}
BENCHMARK(BM_cached_unwind);
static void Initialize(benchmark::State& state, unwindstack::Maps& maps,
unwindstack::MapInfo** build_id_map_info) {
if (!maps.Parse()) {
state.SkipWithError("Failed to parse local maps.");
return;
}
// Find the libc.so share library and use that for benchmark purposes.
*build_id_map_info = nullptr;
for (unwindstack::MapInfo* map_info : maps) {
if (map_info->offset == 0 && map_info->GetBuildID() != "") {
*build_id_map_info = map_info;
break;
}
}
if (*build_id_map_info == nullptr) {
state.SkipWithError("Failed to find a map with a BuildID.");
}
}
static void BM_get_build_id_from_elf(benchmark::State& state) {
unwindstack::LocalMaps maps;
unwindstack::MapInfo* build_id_map_info;
Initialize(state, maps, &build_id_map_info);
unwindstack::Elf* elf = build_id_map_info->GetElf(std::shared_ptr<unwindstack::Memory>(),
unwindstack::Regs::CurrentArch());
if (!elf->valid()) {
state.SkipWithError("Cannot get valid elf from map.");
}
for (auto _ : state) {
uintptr_t id = build_id_map_info->build_id;
if (id != 0) {
delete reinterpret_cast<std::string*>(id);
build_id_map_info->build_id = 0;
}
benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
}
}
BENCHMARK(BM_get_build_id_from_elf);
static void BM_get_build_id_from_file(benchmark::State& state) {
unwindstack::LocalMaps maps;
unwindstack::MapInfo* build_id_map_info;
Initialize(state, maps, &build_id_map_info);
for (auto _ : state) {
uintptr_t id = build_id_map_info->build_id;
if (id != 0) {
delete reinterpret_cast<std::string*>(id);
build_id_map_info->build_id = 0;
}
benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
}
}
BENCHMARK(BM_get_build_id_from_file);
BENCHMARK_MAIN();

View File

@ -65,8 +65,6 @@ class Elf {
bool GetGlobalVariable(const std::string& name, uint64_t* memory_address);
bool GetBuildID(std::string* build_id);
uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
@ -74,6 +72,8 @@ class Elf {
ElfInterface* CreateInterfaceFromMemory(Memory* memory);
std::string GetBuildID();
uint64_t GetLoadBias() { return load_bias_; }
bool IsValidPc(uint64_t pc);
@ -102,6 +102,8 @@ class Elf {
static uint64_t GetLoadBias(Memory* memory);
static std::string GetBuildID(Memory* memory);
static void SetCachingEnabled(bool enable);
static bool CachingEnabled() { return cache_enabled_; }

View File

@ -62,7 +62,7 @@ class ElfInterface {
virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0;
virtual bool GetBuildID(std::string* build_id) = 0;
virtual std::string GetBuildID() = 0;
virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
@ -100,6 +100,9 @@ class ElfInterface {
template <typename EhdrType, typename PhdrType>
static uint64_t GetLoadBias(Memory* memory);
template <typename EhdrType, typename ShdrType, typename NhdrType>
static std::string ReadBuildIDFromMemory(Memory* memory);
protected:
template <typename AddressType>
void InitHeadersWithTemplate(uint64_t load_bias);
@ -128,7 +131,7 @@ class ElfInterface {
static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
template <typename NhdrType>
bool ReadBuildID(std::string* build_id);
std::string ReadBuildID();
Memory* memory_;
std::unordered_map<uint64_t, LoadInfo> pt_loads_;
@ -192,9 +195,7 @@ class ElfInterface32 : public ElfInterface {
return ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(name, memory_address);
}
bool GetBuildID(std::string* build_id) {
return ElfInterface::ReadBuildID<Elf32_Nhdr>(build_id);
}
std::string GetBuildID() override { return ElfInterface::ReadBuildID<Elf32_Nhdr>(); }
static void GetMaxSize(Memory* memory, uint64_t* size) {
GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
@ -226,9 +227,7 @@ class ElfInterface64 : public ElfInterface {
return ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(name, memory_address);
}
bool GetBuildID(std::string* build_id) {
return ElfInterface::ReadBuildID<Elf64_Nhdr>(build_id);
}
std::string GetBuildID() override { return ElfInterface::ReadBuildID<Elf64_Nhdr>(); }
static void GetMaxSize(Memory* memory, uint64_t* size) {
GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);

View File

@ -38,7 +38,8 @@ struct MapInfo {
flags(flags),
name(name),
prev_map(map_info),
load_bias(static_cast<uint64_t>(-1)) {}
load_bias(static_cast<uint64_t>(-1)),
build_id(0) {}
MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const std::string& name)
: start(start),
@ -47,8 +48,9 @@ struct MapInfo {
flags(flags),
name(name),
prev_map(map_info),
load_bias(static_cast<uint64_t>(-1)) {}
~MapInfo() = default;
load_bias(static_cast<uint64_t>(-1)),
build_id(0) {}
~MapInfo();
uint64_t start = 0;
uint64_t end = 0;
@ -68,6 +70,11 @@ struct MapInfo {
std::atomic_uint64_t load_bias;
// This is a pointer to a new'd std::string.
// Using an atomic value means that we don't need to lock and will
// make it easier to move to a fine grained lock in the future.
std::atomic_uintptr_t build_id;
// This function guarantees it will never return nullptr.
Elf* GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch);
@ -77,6 +84,8 @@ struct MapInfo {
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
std::string GetBuildID();
private:
MapInfo(const MapInfo&) = delete;
void operator=(const MapInfo&) = delete;

View File

@ -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_;

View File

@ -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) {

View File

@ -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*));

View File

@ -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(&note_section, &note_header, sizeof(note_header));
size_t note_offset = sizeof(note_header);
memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
note_offset += sizeof("GNU");
memcpy(&note_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

View File

@ -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) {

View File

@ -123,8 +123,8 @@ int GetElfInfo(const char* file, uint64_t offset) {
printf("Soname: %s\n", soname.c_str());
}
std::string build_id;
if (elf.GetBuildID(&build_id)) {
std::string build_id = elf.GetBuildID();
if (!build_id.empty()) {
printf("Build ID: ");
for (size_t i = 0; i < build_id.size(); ++i) {
printf("%02hhx", build_id[i]);