Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b26b07d8 authored by Christopher Ferris's avatar Christopher Ferris Committed by Gerrit Code Review
Browse files

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

parents c39ed35d bf373edc
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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",
    ],
}
+23 −2
Original line number Diff line number Diff line
@@ -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
+118 −14
Original line number Diff line number Diff line
@@ -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 "";
        }
        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;
        }
        build_id->resize(hdr.n_descsz);
        return memory_->ReadFully(gnu_build_id_offset_ + offset, &(*build_id)[0],
                                  hdr.n_descsz);
        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
+48 −0
Original line number Diff line number Diff line
@@ -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
+62 −0
Original line number Diff line number Diff line
@@ -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();
Loading