utils: Refactor findFiles()
The findFiles() iterates the directory recursively. For CPU sensor it
was using depth 6 to iterate the PECI devices, and it results in
iterating all the devices in /sys/bus/platform because it contains a
symlink of `subsystem` to `/sys/bus/platform`.
Refactor this function and make sure it skips iterating if a subdir does
not match the regex.
Tested: On g220a, the iterated files reduce from about 40000 to 400,
and the function execution time reduces from 30s+ to about 0.5s.
Signed-off-by: Lei YU <yulei.sh@bytedance.com>
Change-Id: I7eada897a0f2c76c405a4efc0a081e00b48070e1
diff --git a/src/Utils.cpp b/src/Utils.cpp
index 20f4c4e..6d017ec 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -192,28 +192,102 @@
return true;
}
-bool findFiles(const fs::path& dirPath, const std::string& matchString,
+bool findFiles(const fs::path& dirPath, std::string_view matchString,
std::vector<fs::path>& foundPaths, int symlinkDepth)
{
- if (!fs::exists(dirPath))
+ std::error_code ec;
+ if (!fs::exists(dirPath, ec))
{
return false;
}
- std::regex search(matchString);
- std::smatch match;
+ std::vector<std::regex> matchPieces;
+
+ size_t pos = 0;
+ std::string token;
+ // Generate the regex expressions list from the match we were given
+ while ((pos = matchString.find('/')) != std::string::npos)
+ {
+ token = matchString.substr(0, pos);
+ matchPieces.emplace_back(token);
+ matchString.remove_prefix(pos + 1);
+ }
+ matchPieces.emplace_back(std::string{matchString});
+
+ // Check if the match string contains directories, and skip the match of
+ // subdirectory if not
+ if (matchPieces.size() <= 1)
+ {
+ std::regex search(std::string{matchString});
+ std::smatch match;
+ for (auto p = fs::recursive_directory_iterator(
+ dirPath, fs::directory_options::follow_directory_symlink);
+ p != fs::recursive_directory_iterator(); ++p)
+ {
+ std::string path = p->path().string();
+ if (!is_directory(*p))
+ {
+ if (std::regex_search(path, match, search))
+ {
+ foundPaths.emplace_back(p->path());
+ }
+ }
+ if (p.depth() >= symlinkDepth)
+ {
+ p.disable_recursion_pending();
+ }
+ }
+ return true;
+ }
+
+ // The match string contains directories, verify each level of sub
+ // directories
for (auto p = fs::recursive_directory_iterator(
dirPath, fs::directory_options::follow_directory_symlink);
p != fs::recursive_directory_iterator(); ++p)
{
- std::string path = p->path().string();
+ std::vector<std::regex>::iterator matchPiece = matchPieces.begin();
+ fs::path::iterator pathIt = p->path().begin();
+ for (const fs::path& dir : dirPath)
+ {
+ if (dir.empty())
+ {
+ // When the path ends with '/', it gets am empty path
+ // skip such case.
+ break;
+ }
+ pathIt++;
+ }
+
+ while (pathIt != p->path().end())
+ {
+ // Found a path deeper than match.
+ if (matchPiece == matchPieces.end())
+ {
+ p.disable_recursion_pending();
+ break;
+ }
+ std::smatch match;
+ std::string component = pathIt->string();
+ std::regex regexPiece(*matchPiece);
+ if (!std::regex_match(component, match, regexPiece))
+ {
+ // path prefix doesn't match, no need to iterate further
+ p.disable_recursion_pending();
+ break;
+ }
+ matchPiece++;
+ pathIt++;
+ }
+
if (!is_directory(*p))
{
- if (std::regex_search(path, match, search))
+ if (matchPiece == matchPieces.end())
{
foundPaths.emplace_back(p->path());
}
}
+
if (p.depth() >= symlinkDepth)
{
p.disable_recursion_pending();
@@ -417,7 +491,7 @@
{
if (association)
{
- std::filesystem::path p(path);
+ fs::path p(path);
std::vector<Association> associations;
associations.emplace_back("chassis", "all_sensors",
@@ -434,7 +508,7 @@
{
if (association)
{
- std::filesystem::path p(path);
+ fs::path p(path);
std::vector<Association> associations;
std::string objPath(p.parent_path().string());
@@ -502,7 +576,7 @@
}
std::optional<std::tuple<std::string, std::string, std::string>>
- splitFileName(const std::filesystem::path& filePath)
+ splitFileName(const fs::path& filePath)
{
if (filePath.has_filename())
{