symbolizer: Fix symlink handling
It turns out that the symbolizer used fts_open() for a reason:
base::ListFilesRecursive doesn't handle symlinks. Turns out that
handling symlinks is non-trivial because we have to prevent infinite
loops. fts_open() does all that for us. This commit:
* Reverts a bunch of commits:
* ee90928bb222("Move GetFileSize() from symbolizer to base/file_utils.h")
* ad3da2777b62("Revert "Link against libfts when compiling against musl"")
* 2abbc726fa5a("Avoid using fts_open and fts_read")
* Cherrypicks a fix from base::ListFilesRecursive from commit
63ad74ac51b7 ("Do not use ScopedResource for FindFirstFile handle")
* Adds unittests with symlinks.
Tested by:
* Building and running unittests on windows
* Building and running unintests on Linux
* Building in CI android musl:
https://android-build.googleplex.com/builds/abtd/run/L35900000958749217
Bug: 261712935
Change-Id: Ibdf87f05394f1922430dd5513e5d53848096d7d7
diff --git a/Android.bp b/Android.bp
index 82456b8..212882f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8755,6 +8755,8 @@
srcs: [
"src/profiling/symbolizer/breakpad_parser.cc",
"src/profiling/symbolizer/breakpad_symbolizer.cc",
+ "src/profiling/symbolizer/filesystem_posix.cc",
+ "src/profiling/symbolizer/filesystem_windows.cc",
"src/profiling/symbolizer/local_symbolizer.cc",
"src/profiling/symbolizer/scoped_read_mmap_posix.cc",
"src/profiling/symbolizer/scoped_read_mmap_windows.cc",
@@ -11822,6 +11824,13 @@
"src/traced/probes/filesystem/testdata/**/*",
"src/traced/probes/ftrace/test/data/**/*",
],
+ target: {
+ musl: {
+ static_libs: [
+ "libfts",
+ ],
+ },
+ },
}
// GN: //test/vts:perfetto_vts_deps
@@ -12344,6 +12353,11 @@
],
},
},
+ musl: {
+ static_libs: [
+ "libfts",
+ ],
+ },
},
}
@@ -12528,6 +12542,13 @@
cflags: [
"-DHAVE_HIDDEN",
],
+ target: {
+ musl: {
+ static_libs: [
+ "libfts",
+ ],
+ },
+ },
}
// GN: //src/traced/service:traced
diff --git a/BUILD b/BUILD
index 3d746e3..50e7ace 100644
--- a/BUILD
+++ b/BUILD
@@ -947,6 +947,9 @@
"src/profiling/symbolizer/breakpad_symbolizer.cc",
"src/profiling/symbolizer/breakpad_symbolizer.h",
"src/profiling/symbolizer/elf.h",
+ "src/profiling/symbolizer/filesystem.h",
+ "src/profiling/symbolizer/filesystem_posix.cc",
+ "src/profiling/symbolizer/filesystem_windows.cc",
"src/profiling/symbolizer/local_symbolizer.cc",
"src/profiling/symbolizer/local_symbolizer.h",
"src/profiling/symbolizer/scoped_read_mmap.h",
diff --git a/include/perfetto/ext/base/file_utils.h b/include/perfetto/ext/base/file_utils.h
index 7c7b8e0..d2412d0 100644
--- a/include/perfetto/ext/base/file_utils.h
+++ b/include/perfetto/ext/base/file_utils.h
@@ -27,7 +27,6 @@
#include "perfetto/base/export.h"
#include "perfetto/base/status.h"
#include "perfetto/ext/base/scoped_file.h"
-#include "perfetto/ext/base/optional.h"
#include "perfetto/ext/base/utils.h"
namespace perfetto {
@@ -94,9 +93,6 @@
base::Status ListFilesRecursive(const std::string& dir_path,
std::vector<std::string>& output);
-// Returns the size of the file at `path` or nullopt in case of error.
-Optional<size_t> GetFileSize(const std::string& path);
-
} // namespace base
} // namespace perfetto
diff --git a/src/base/file_utils.cc b/src/base/file_utils.cc
index 66ab5f6..9f70ad7 100644
--- a/src/base/file_utils.cc
+++ b/src/base/file_utils.cc
@@ -346,34 +346,5 @@
return filename.substr(ext_idx);
}
-base::Optional<size_t> GetFileSize(const std::string& file_path) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
- HANDLE file =
- CreateFileA(file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
- if (file == INVALID_HANDLE_VALUE) {
- return nullopt;
- }
- LARGE_INTEGER file_size;
- file_size.QuadPart = 0;
- BOOL ok = GetFileSizeEx(file, &file_size);
- CloseHandle(file);
- if (!ok) {
- return nullopt;
- }
- return static_cast<size_t>(file_size.QuadPart);
-#else
- base::ScopedFile fd(base::OpenFile(file_path, O_RDONLY | O_CLOEXEC));
- if (!fd) {
- return nullopt;
- }
- struct stat buf {};
- if (fstat(*fd, &buf) == -1) {
- return nullopt;
- }
- return static_cast<size_t>(buf.st_size);
-#endif
-}
-
} // namespace base
} // namespace perfetto
diff --git a/src/profiling/symbolizer/BUILD.gn b/src/profiling/symbolizer/BUILD.gn
index 94feb3e..c81dbb1 100644
--- a/src/profiling/symbolizer/BUILD.gn
+++ b/src/profiling/symbolizer/BUILD.gn
@@ -24,6 +24,9 @@
"breakpad_symbolizer.cc",
"breakpad_symbolizer.h",
"elf.h",
+ "filesystem.h",
+ "filesystem_posix.cc",
+ "filesystem_windows.cc",
"local_symbolizer.cc",
"local_symbolizer.h",
"scoped_read_mmap.h",
diff --git a/src/profiling/symbolizer/filesystem.h b/src/profiling/symbolizer/filesystem.h
new file mode 100644
index 0000000..d9cf87c
--- /dev/null
+++ b/src/profiling/symbolizer/filesystem.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef SRC_PROFILING_SYMBOLIZER_FILESYSTEM_H_
+#define SRC_PROFILING_SYMBOLIZER_FILESYSTEM_H_
+
+#include "src/profiling/symbolizer/local_symbolizer.h"
+
+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
+
+#endif // SRC_PROFILING_SYMBOLIZER_FILESYSTEM_H_
diff --git a/src/profiling/symbolizer/filesystem_posix.cc b/src/profiling/symbolizer/filesystem_posix.cc
new file mode 100644
index 0000000..cc983e2
--- /dev/null
+++ b/src/profiling/symbolizer/filesystem_posix.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 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 "src/profiling/symbolizer/filesystem.h"
+
+#include "perfetto/base/build_config.h"
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
+#include <fts.h>
+#include <sys/stat.h>
+#endif
+
+#include <string>
+
+#include "perfetto/ext/base/file_utils.h"
+
+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) {
+ PERFETTO_PLOG("Failed to get file size %s", file_path.c_str());
+ return 0;
+ }
+ struct stat buf;
+ if (fstat(*fd, &buf) == -1) {
+ return 0;
+ }
+ 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;
+}
+#endif
+
+} // namespace profiling
+} // namespace perfetto
+
+#endif // !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
diff --git a/src/profiling/symbolizer/filesystem_windows.cc b/src/profiling/symbolizer/filesystem_windows.cc
new file mode 100644
index 0000000..04cd685
--- /dev/null
+++ b/src/profiling/symbolizer/filesystem_windows.cc
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 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 "src/profiling/symbolizer/filesystem.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+#include <Windows.h>
+
+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));
+ }
+ FindClose(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,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (file == INVALID_HANDLE_VALUE) {
+ PERFETTO_PLOG("Failed to get file size %s", file_path.c_str());
+ return 0;
+ }
+ LARGE_INTEGER file_size;
+ file_size.QuadPart = 0;
+ if (!GetFileSizeEx(file, &file_size)) {
+ PERFETTO_PLOG("Failed to get file size %s", file_path.c_str());
+ }
+ CloseHandle(file);
+ return static_cast<size_t>(file_size.QuadPart);
+}
+
+} // namespace profiling
+} // namespace perfetto
+
+#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
diff --git a/src/profiling/symbolizer/local_symbolizer.cc b/src/profiling/symbolizer/local_symbolizer.cc
index e37cee7..ce11a40 100644
--- a/src/profiling/symbolizer/local_symbolizer.cc
+++ b/src/profiling/symbolizer/local_symbolizer.cc
@@ -32,6 +32,7 @@
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/string_utils.h"
#include "src/profiling/symbolizer/elf.h"
+#include "src/profiling/symbolizer/filesystem.h"
#include "src/profiling/symbolizer/scoped_read_mmap.h"
namespace perfetto {
@@ -216,36 +217,31 @@
uint64_t load_bias;
};
-base::Optional<BuildIdAndLoadBias> GetBuildIdAndLoadBias(
- const std::string& fname) {
- base::Optional<size_t> size = base::GetFileSize(fname);
- if (!size.has_value()) {
- PERFETTO_PLOG("Failed to get file size %s", fname.c_str());
- return base::nullopt;
- }
+base::Optional<BuildIdAndLoadBias> GetBuildIdAndLoadBias(const char* fname,
+ size_t size) {
static_assert(EI_CLASS > EI_MAG3, "mem[EI_MAG?] accesses are in range.");
- if (*size <= EI_CLASS)
+ if (size <= EI_CLASS)
return base::nullopt;
- ScopedReadMmap map(fname.c_str(), *size);
+ ScopedReadMmap map(fname, size);
if (!map.IsValid()) {
PERFETTO_PLOG("mmap");
return base::nullopt;
}
char* mem = static_cast<char*>(*map);
- if (!IsElf(mem, *size))
+ if (!IsElf(mem, size))
return base::nullopt;
base::Optional<std::string> build_id;
base::Optional<uint64_t> load_bias;
switch (mem[EI_CLASS]) {
case ELFCLASS32:
- build_id = GetBuildId<Elf32>(mem, *size);
- load_bias = GetLoadBias<Elf32>(mem, *size);
+ build_id = GetBuildId<Elf32>(mem, size);
+ load_bias = GetLoadBias<Elf32>(mem, size);
break;
case ELFCLASS64:
- build_id = GetBuildId<Elf64>(mem, *size);
- load_bias = GetLoadBias<Elf64>(mem, *size);
+ build_id = GetBuildId<Elf64>(mem, size);
+ load_bias = GetLoadBias<Elf64>(mem, size);
break;
default:
return base::nullopt;
@@ -256,48 +252,35 @@
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;
- 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;
+ 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;
}
- 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});
+ 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, 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;
}
@@ -368,8 +351,15 @@
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);
+ GetBuildIdAndLoadBias(symbol_file.c_str(), size);
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 cb0c11d..919e3ff 100644
--- a/src/profiling/symbolizer/local_symbolizer_unittest.cc
+++ b/src/profiling/symbolizer/local_symbolizer_unittest.cc
@@ -28,6 +28,12 @@
#include "src/profiling/symbolizer/local_symbolizer.h"
#include "src/profiling/symbolizer/subprocess.h"
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#include <unistd.h>
+#endif
+
namespace perfetto {
namespace profiling {
namespace {
@@ -140,7 +146,13 @@
return std::string(reinterpret_cast<const char*>(&e), sizeof e);
}
-TEST(LocalBinaryIndexerTest, SimpleTree) {
+#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"));
@@ -154,14 +166,91 @@
base::Optional<FoundBinary> bin1 =
indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
ASSERT_TRUE(bin1.has_value());
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ EXPECT_EQ(bin1.value().file_name, tmp.path() + "/dir1\\elf1");
+#else
EXPECT_EQ(bin1.value().file_name, tmp.path() + "/dir1/elf1");
+#endif
+ base::Optional<FoundBinary> bin2 =
+ indexer.FindBinary("", "BBBBBBBBBBBBBBBBBBBB");
+ ASSERT_TRUE(bin2.has_value());
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ EXPECT_EQ(bin2.value().file_name, tmp.path() + "/dir2\\elf1");
+#else
+ EXPECT_EQ(bin2.value().file_name, tmp.path() + "/dir2/elf1");
+#endif
+}
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+
+#if defined(MEMORY_SANITIZER)
+// fts_read() causes some error under msan.
+#define NOMSAN_Symlinks DISABLED_Symlinks
+#else
+#define NOMSAN_Symlinks Symlinks
+#endif
+TEST(LocalBinaryIndexerTest, NOMSAN_Symlinks) {
+ base::TmpDirTree tmp;
+ tmp.AddDir("real");
+ tmp.AddFile("real/elf1", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
+ tmp.AddDir("real/dir1");
+ tmp.AddFile("real/dir1/elf2", CreateElfWithBuildId("BBBBBBBBBBBBBBBBBBBB"));
+ tmp.AddFile("real/dir1/elf3", CreateElfWithBuildId("CCCCCCCCCCCCCCCCCCCC"));
+ tmp.AddDir("sym");
+ symlink(tmp.AbsolutePath("real/elf1").c_str(),
+ tmp.AbsolutePath("sym/elf1").c_str());
+ tmp.TrackFile("sym/elf1");
+ symlink(tmp.AbsolutePath("real/dir1").c_str(),
+ tmp.AbsolutePath("sym/dir1").c_str());
+ tmp.TrackFile("sym/dir1");
+
+ LocalBinaryIndexer indexer({tmp.AbsolutePath("sym")});
+
+ base::Optional<FoundBinary> bin1 =
+ indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
+ ASSERT_TRUE(bin1.has_value());
+ EXPECT_EQ(bin1.value().file_name, tmp.AbsolutePath("sym/elf1"));
base::Optional<FoundBinary> bin2 =
indexer.FindBinary("", "BBBBBBBBBBBBBBBBBBBB");
ASSERT_TRUE(bin2.has_value());
- EXPECT_EQ(bin2.value().file_name, tmp.path() + "/dir2/elf1");
+ EXPECT_EQ(bin2.value().file_name, tmp.AbsolutePath("sym/dir1/elf2"));
+
+ base::Optional<FoundBinary> bin3 =
+ indexer.FindBinary("", "CCCCCCCCCCCCCCCCCCCC");
+ ASSERT_TRUE(bin3.has_value());
+ EXPECT_EQ(bin3.value().file_name, tmp.AbsolutePath("sym/dir1/elf3"));
}
+#if defined(MEMORY_SANITIZER)
+// fts_read() causes some error under msan.
+#define NOMSAN_RecursiveSymlinks DISABLED_RecursiveSymlinks
+#else
+#define NOMSAN_RecursiveSymlinks RecursiveSymlinks
+#endif
+TEST(LocalBinaryIndexerTest, NOMSAN_RecursiveSymlinks) {
+ base::TmpDirTree tmp;
+ tmp.AddDir("main");
+ tmp.AddFile("main/elf1", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
+ tmp.AddDir("main/dir1");
+ symlink(tmp.AbsolutePath("main").c_str(),
+ tmp.AbsolutePath("main/dir1/sym").c_str());
+ tmp.TrackFile("main/dir1/sym");
+
+ LocalBinaryIndexer indexer({tmp.AbsolutePath("main")});
+
+ base::Optional<FoundBinary> bin1 =
+ indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
+ ASSERT_TRUE(bin1.has_value());
+ EXPECT_EQ(bin1.value().file_name, tmp.AbsolutePath("main/elf1"));
+}
+
+#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||
+ // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) ||
+ // PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+
TEST(LocalBinaryFinderTest, AbsolutePath) {
base::TmpDirTree tmp;
tmp.AddDir("root");
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index e8a5677..b7da65d 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -117,6 +117,12 @@
],
}
+needs_libfts = [
+ '//:perfetto_unittests',
+ '//src/trace_processor:trace_processor_shell',
+ '//src/traceconv:traceconv',
+]
+
# All module names are prefixed with this string to avoid collisions.
module_prefix = 'perfetto_'
@@ -507,6 +513,7 @@
self.tool_files: Optional[List[str]] = None
self.android = Target('android')
self.host = Target('host')
+ self.musl = Target('musl')
self.lto: Optional[bool] = None
self.stl = None
self.dist = dict()
@@ -568,6 +575,7 @@
target_out = []
self._output_field(target_out, 'android')
self._output_field(target_out, 'host')
+ self._output_field(target_out, 'musl')
if target_out:
output.append(' target: {')
for line in target_out:
@@ -985,6 +993,9 @@
for src in target.sources
if is_supported_source_file(src))
+ if name_without_toolchain in needs_libfts:
+ module.musl.static_libs.add('libfts')
+
if target.type in gn_utils.LINKER_UNIT_TYPES:
module.cflags.update(_get_cflags(target))