Unit test for LocalBinaryIndexer

A future commit is going to slightly change the implementation of
LocalBinaryIndexer. This commit adds a simple unit test to make sure
that the behavior will not change.

Bug: 200136875
Change-Id: I5a8b6670248a5db376158855b56292b21fae1db1
diff --git a/src/profiling/symbolizer/elf.h b/src/profiling/symbolizer/elf.h
index e323d46..6b89076 100644
--- a/src/profiling/symbolizer/elf.h
+++ b/src/profiling/symbolizer/elf.h
@@ -38,11 +38,15 @@
 constexpr auto ELFMAG1 = 'E';
 constexpr auto ELFMAG2 = 'L';
 constexpr auto ELFMAG3 = 'F';
+constexpr auto ELFDATA2LSB = 1;
+constexpr auto EV_CURRENT = 1;
 constexpr auto EI_MAG0 = 0;
 constexpr auto EI_MAG1 = 1;
 constexpr auto EI_MAG2 = 2;
 constexpr auto EI_MAG3 = 3;
 constexpr auto EI_CLASS = 4;
+constexpr auto EI_DATA = 5;
+constexpr auto EI_VERSION = 6;
 
 struct Elf32 {
   using Addr = uint32_t;
diff --git a/src/profiling/symbolizer/local_symbolizer_unittest.cc b/src/profiling/symbolizer/local_symbolizer_unittest.cc
index b96d318..d52ed69 100644
--- a/src/profiling/symbolizer/local_symbolizer_unittest.cc
+++ b/src/profiling/symbolizer/local_symbolizer_unittest.cc
@@ -20,7 +20,11 @@
 // This translation unit is built only on Linux and MacOS. See //gn/BUILD.gn.
 #if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
 
+#include <cstddef>
+
+#include "src/base/test/tmp_dir_tree.h"
 #include "src/base/test/utils.h"
+#include "src/profiling/symbolizer/elf.h"
 #include "src/profiling/symbolizer/local_symbolizer.h"
 #include "src/profiling/symbolizer/subprocess.h"
 
@@ -95,6 +99,75 @@
   }
 }
 
+// Creates a very simple ELF file content with the first 20 bytes of `build_id`
+// as build id (if build id is shorter the remainin bytes are zero).
+std::string CreateElfWithBuildId(const std::string& build_id) {
+  struct SimpleElf {
+    Elf64::Ehdr ehdr;
+    Elf64::Shdr shdr;
+    Elf64::Nhdr nhdr;
+    char note_name[4];
+    char note_desc[20];
+  } e;
+  memset(&e, 0, sizeof e);
+
+  e.ehdr.e_ident[EI_MAG0] = ELFMAG0;
+  e.ehdr.e_ident[EI_MAG1] = ELFMAG1;
+  e.ehdr.e_ident[EI_MAG2] = ELFMAG2;
+  e.ehdr.e_ident[EI_MAG3] = ELFMAG3;
+  e.ehdr.e_ident[EI_CLASS] = ELFCLASS64;
+  e.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
+  e.ehdr.e_ident[EI_VERSION] = EV_CURRENT;
+  e.ehdr.e_version = EV_CURRENT;
+  e.ehdr.e_shentsize = sizeof(Elf64::Shdr);
+  e.ehdr.e_shnum = 1;
+  e.ehdr.e_ehsize = sizeof e.ehdr;
+  e.ehdr.e_shoff = offsetof(SimpleElf, shdr);
+
+  e.shdr.sh_type = SHT_NOTE;
+  e.shdr.sh_offset = offsetof(SimpleElf, nhdr);
+
+  e.nhdr.n_type = NT_GNU_BUILD_ID;
+  e.nhdr.n_namesz = sizeof e.note_name;
+  e.nhdr.n_descsz = sizeof e.note_desc;
+  strcpy(e.note_name, "GNU");
+  memcpy(e.note_desc, build_id.c_str(),
+         std::min(build_id.size(), sizeof(e.note_desc)));
+
+  e.shdr.sh_size = offsetof(SimpleElf, note_desc) + sizeof(e.note_desc) -
+                   offsetof(SimpleElf, nhdr);
+
+  return std::string(reinterpret_cast<const char*>(&e), sizeof e);
+}
+
+#if defined(MEMORY_SANITIZER)
+// fts_read() causes some error under msan.
+#define NOMSAN_SimpleTree DISABLED_SimpleTree
+#else
+#define NOMSAN_SimpleTree SimpleTree
+#endif
+TEST(LocalBinaryIndexerTest, NOMSAN_SimpleTree) {
+  base::TmpDirTree tmp;
+  tmp.AddDir("dir1");
+  tmp.AddFile("dir1/elf1", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
+  tmp.AddFile("dir1/nonelf1", "OTHERDATA");
+  tmp.AddDir("dir2");
+  tmp.AddFile("dir2/elf1", CreateElfWithBuildId("BBBBBBBBBBBBBBBBBBBB"));
+  tmp.AddFile("dir2/nonelf1", "other text");
+
+  LocalBinaryIndexer indexer({tmp.path() + "/dir1", tmp.path() + "/dir2"});
+
+  base::Optional<FoundBinary> bin1 =
+      indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
+  ASSERT_TRUE(bin1.has_value());
+  EXPECT_EQ(bin1.value().file_name, tmp.path() + "/dir1/elf1");
+
+  base::Optional<FoundBinary> bin2 =
+      indexer.FindBinary("", "BBBBBBBBBBBBBBBBBBBB");
+  ASSERT_TRUE(bin2.has_value());
+  EXPECT_EQ(bin2.value().file_name, tmp.path() + "/dir2/elf1");
+}
+
 }  // namespace
 }  // namespace profiling
 }  // namespace perfetto