Avoid using fts_open and fts_read
b39ae485a46b("Link against libfts when compiling against musl") had to
introduce an additional dependency on libfts when building with musl as
libc.
Turns out that base::ListFilesRecursive() can be used instead. This has
two benefits:
* Doesn't require libfts when building with musl.
* Reduces platform dependend duplicated code.
perfetto::profiling::WalkDirectories() used a callback, while
base::ListFilesRecursive() returns a vector, so there might be a
performance difference with very large trees. If this turns out to be a
problem, base::ListFilesRecursive() can be changed to use a callback (or
return an iterator instead).
Bug: 200136875
Change-Id: I420e7b0ad7fe10666da9c39baf1ca046649588fd
diff --git a/src/profiling/symbolizer/filesystem.h b/src/profiling/symbolizer/filesystem.h
index d9cf87c..9d11233 100644
--- a/src/profiling/symbolizer/filesystem.h
+++ b/src/profiling/symbolizer/filesystem.h
@@ -22,9 +22,7 @@
namespace perfetto {
namespace profiling {
-using FileCallback = std::function<void(const char*, size_t)>;
size_t GetFileSize(const std::string& file_path);
-bool WalkDirectories(std::vector<std::string> dirs, FileCallback fn);
} // namespace profiling
} // namespace perfetto
diff --git a/src/profiling/symbolizer/filesystem_posix.cc b/src/profiling/symbolizer/filesystem_posix.cc
index cc983e2..311f444 100644
--- a/src/profiling/symbolizer/filesystem_posix.cc
+++ b/src/profiling/symbolizer/filesystem_posix.cc
@@ -20,7 +20,6 @@
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
#if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
-#include <fts.h>
#include <sys/stat.h>
#endif
@@ -31,26 +30,6 @@
namespace perfetto {
namespace profiling {
#if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
-bool WalkDirectories(std::vector<std::string> dirs, FileCallback fn) {
- std::vector<char*> dir_cstrs;
- dir_cstrs.reserve(dirs.size());
- for (std::string& dir : dirs)
- dir_cstrs.emplace_back(&dir[0]);
- dir_cstrs.push_back(nullptr);
- base::ScopedResource<FTS*, fts_close, nullptr> fts(
- fts_open(&dir_cstrs[0], FTS_LOGICAL | FTS_NOCHDIR, nullptr));
- if (!fts) {
- PERFETTO_PLOG("fts_open");
- return false;
- }
- FTSENT* ent;
- while ((ent = fts_read(*fts))) {
- if (ent->fts_info & FTS_F)
- fn(ent->fts_path, static_cast<size_t>(ent->fts_statp->st_size));
- }
- return true;
-}
-
size_t GetFileSize(const std::string& file_path) {
base::ScopedFile fd(base::OpenFile(file_path, O_RDONLY | O_CLOEXEC));
if (!fd) {
@@ -64,9 +43,6 @@
return static_cast<size_t>(buf.st_size);
}
#else
-bool WalkDirectories(std::vector<std::string>, FileCallback) {
- return false;
-}
size_t GetFileSize(const std::string&) {
return 0;
}
diff --git a/src/profiling/symbolizer/filesystem_windows.cc b/src/profiling/symbolizer/filesystem_windows.cc
index bb2652c..f7a381e 100644
--- a/src/profiling/symbolizer/filesystem_windows.cc
+++ b/src/profiling/symbolizer/filesystem_windows.cc
@@ -23,35 +23,6 @@
namespace perfetto {
namespace profiling {
-bool WalkDirectories(std::vector<std::string> dirs, FileCallback fn) {
- std::vector<std::string> sub_dirs;
- for (const std::string& dir : dirs) {
- WIN32_FIND_DATAA file;
- HANDLE fh = FindFirstFileA((dir + "\\*").c_str(), &file);
- if (fh != INVALID_HANDLE_VALUE) {
- do {
- std::string file_path = dir + "\\" + file.cFileName;
- if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
- if (strcmp(file.cFileName, ".") != 0 &&
- strcmp(file.cFileName, "..") != 0) {
- sub_dirs.push_back(file_path);
- }
- } else {
- ULARGE_INTEGER size;
- size.HighPart = file.nFileSizeHigh;
- size.LowPart = file.nFileSizeLow;
- fn(file_path.c_str(), size.QuadPart);
- }
- } while (FindNextFileA(fh, &file));
- }
- CloseHandle(fh);
- }
- if (!sub_dirs.empty()) {
- WalkDirectories(sub_dirs, fn);
- }
- return true;
-}
-
size_t GetFileSize(const std::string& file_path) {
HANDLE file =
CreateFileA(file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
diff --git a/src/profiling/symbolizer/local_symbolizer.cc b/src/profiling/symbolizer/local_symbolizer.cc
index 3375837..8970cca 100644
--- a/src/profiling/symbolizer/local_symbolizer.cc
+++ b/src/profiling/symbolizer/local_symbolizer.cc
@@ -217,12 +217,12 @@
uint64_t load_bias;
};
-base::Optional<BuildIdAndLoadBias> GetBuildIdAndLoadBias(const char* fname,
- size_t size) {
+base::Optional<BuildIdAndLoadBias> GetBuildIdAndLoadBias(const std::string& fname) {
+ size_t size = GetFileSize(fname);
static_assert(EI_CLASS > EI_MAG3, "mem[EI_MAG?] accesses are in range.");
if (size <= EI_CLASS)
return base::nullopt;
- ScopedReadMmap map(fname, size);
+ ScopedReadMmap map(fname.c_str(), size);
if (!map.IsValid()) {
PERFETTO_PLOG("mmap");
return base::nullopt;
@@ -252,35 +252,48 @@
return base::nullopt;
}
+bool StartsWithElfMagic(const std::string& fname) {
+ base::ScopedFile fd(base::OpenFile(fname, O_RDONLY));
+ char magic[EI_MAG3 + 1];
+ if (!fd) {
+ PERFETTO_PLOG("Failed to open %s", fname.c_str());
+ return false;
+ }
+ ssize_t rd = base::Read(*fd, &magic, sizeof(magic));
+ if (rd != sizeof(magic)) {
+ PERFETTO_PLOG("Failed to read %s", fname.c_str());
+ return false;
+ }
+ if (!IsElf(magic, static_cast<size_t>(rd))) {
+ PERFETTO_DLOG("%s not an ELF.", fname.c_str());
+ return false;
+ }
+ return true;
+}
+
std::map<std::string, FoundBinary> BuildIdIndex(std::vector<std::string> dirs) {
std::map<std::string, FoundBinary> result;
- WalkDirectories(std::move(dirs), [&result](const char* fname, size_t size) {
- char magic[EI_MAG3 + 1];
- // Scope file access. On windows OpenFile opens an exclusive lock.
- // This lock needs to be released before mapping the file.
- {
- base::ScopedFile fd(base::OpenFile(fname, O_RDONLY));
- if (!fd) {
- PERFETTO_PLOG("Failed to open %s", fname);
- return;
+ for (const std::string& dir : dirs) {
+ std::vector<std::string> files;
+ base::Status status = base::ListFilesRecursive(dir, files);
+ if (!status.ok()) {
+ PERFETTO_PLOG("Failed to list directory %s", dir.c_str());
+ continue;
+ }
+ for (const std::string& basename : files) {
+ std::string fname = dir + "/" + basename;
+ if (!StartsWithElfMagic(fname)) {
+ continue;
}
- ssize_t rd = base::Read(*fd, &magic, sizeof(magic));
- if (rd != sizeof(magic)) {
- PERFETTO_PLOG("Failed to read %s", fname);
- return;
- }
- if (!IsElf(magic, static_cast<size_t>(rd))) {
- PERFETTO_DLOG("%s not an ELF.", fname);
- return;
+ base::Optional<BuildIdAndLoadBias> build_id_and_load_bias =
+ GetBuildIdAndLoadBias(fname);
+ if (build_id_and_load_bias) {
+ result.emplace(build_id_and_load_bias->build_id,
+ FoundBinary{fname, build_id_and_load_bias->load_bias});
}
}
- base::Optional<BuildIdAndLoadBias> build_id_and_load_bias =
- GetBuildIdAndLoadBias(fname, size);
- if (build_id_and_load_bias) {
- result.emplace(build_id_and_load_bias->build_id,
- FoundBinary{fname, build_id_and_load_bias->load_bias});
- }
- });
+ }
+
return result;
}
@@ -351,15 +364,8 @@
if (!base::FileExists(symbol_file)) {
return base::nullopt;
}
- // Openfile opens the file with an exclusive lock on windows.
- size_t size = GetFileSize(symbol_file);
-
- if (size == 0) {
- return base::nullopt;
- }
-
base::Optional<BuildIdAndLoadBias> build_id_and_load_bias =
- GetBuildIdAndLoadBias(symbol_file.c_str(), size);
+ GetBuildIdAndLoadBias(symbol_file);
if (!build_id_and_load_bias)
return base::nullopt;
if (build_id_and_load_bias->build_id != build_id) {
diff --git a/src/profiling/symbolizer/local_symbolizer_unittest.cc b/src/profiling/symbolizer/local_symbolizer_unittest.cc
index d52ed69..921c878 100644
--- a/src/profiling/symbolizer/local_symbolizer_unittest.cc
+++ b/src/profiling/symbolizer/local_symbolizer_unittest.cc
@@ -140,13 +140,7 @@
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) {
+TEST(LocalBinaryIndexerTest, SimpleTree) {
base::TmpDirTree tmp;
tmp.AddDir("dir1");
tmp.AddFile("dir1/elf1", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));