diff options
| author | 2022-12-21 12:43:56 +0000 | |
|---|---|---|
| committer | 2023-02-10 18:11:41 +0000 | |
| commit | 2061ae2d4558acd8b79256f16f7a8d79a5d396e1 (patch) | |
| tree | 1ab13a4d388193907e03f933e4be34da66823ca1 | |
| parent | cfcf634de88e04748630b37f781b793976e53d81 (diff) | |
Add a function to list all files managed by ART Service.
This is going to be used in the "sweep" phase of the GC.
Bug: 254013425
Test: m test-art-host-gtest-art_libarttools_tests
Test: m test-art-host-gtest-art_artd_tests
Test: atest art_standalone_libarttools_tests
Test: atest atest art_standalone_artd_tests
Ignore-AOSP-First: ART Services.
Change-Id: Ie7a5bd6f805c370aa3c2e3a1ab1d5408e4552f83
| -rw-r--r-- | artd/path_utils.cc | 43 | ||||
| -rw-r--r-- | artd/path_utils.h | 6 | ||||
| -rw-r--r-- | libartbase/base/file_utils.cc | 14 | ||||
| -rw-r--r-- | libartbase/base/file_utils.h | 5 | ||||
| -rw-r--r-- | libarttools/Android.bp | 3 | ||||
| -rw-r--r-- | libarttools/tools/tools.cc | 121 | ||||
| -rw-r--r-- | libarttools/tools/tools.h | 15 | ||||
| -rw-r--r-- | libarttools/tools/tools_test.cc | 93 |
8 files changed, 294 insertions, 6 deletions
diff --git a/artd/path_utils.cc b/artd/path_utils.cc index 295b023923..cac9444b97 100644 --- a/artd/path_utils.cc +++ b/artd/path_utils.cc @@ -17,6 +17,8 @@ #include "path_utils.h" #include <filesystem> +#include <string> +#include <vector> #include "aidl/com/android/server/art/BnArtd.h" #include "android-base/errors.h" @@ -27,6 +29,7 @@ #include "file_utils.h" #include "fmt/format.h" #include "oat_file_assistant.h" +#include "tools/tools.h" namespace art { namespace artd { @@ -99,6 +102,15 @@ Result<std::string> GetAndroidDataOrError() { return result; } +Result<std::string> GetAndroidExpandOrError() { + std::string error_msg; + std::string result = GetAndroidExpandSafe(&error_msg); + if (!error_msg.empty()) { + return Error() << error_msg; + } + return result; +} + Result<std::string> GetArtRootOrError() { std::string error_msg; std::string result = GetArtRootSafe(&error_msg); @@ -110,6 +122,37 @@ Result<std::string> GetArtRootOrError() { } // namespace +Result<std::vector<std::string>> ListManagedFiles() { + std::string android_data = OR_RETURN(GetAndroidDataOrError()); + std::string android_expand = OR_RETURN(GetAndroidExpandOrError()); + + // See `art::tools::Glob` for the syntax. + std::vector<std::string> patterns = { + // Profiles for primary dex files. + android_data + "/misc/profiles/**", + // Artifacts for primary dex files. + android_data + "/dalvik-cache/**", + }; + + for (const std::string& data_root : {android_data, android_expand + "/*"}) { + // Artifacts for primary dex files. + patterns.push_back(data_root + "/app/*/*/oat/**"); + // Profiles and artifacts for secondary dex files. Those files are in app data directories, so + // we use more granular patterns to avoid accidentally deleting apps' files. + for (const char* user_dir : {"/user", "/user_de"}) { + std::string secondary_oat_dir = data_root + user_dir + "/*/*/**/oat"; + for (const char* maybe_tmp_suffix : {"", ".*.tmp"}) { + patterns.push_back(secondary_oat_dir + "/*.prof" + maybe_tmp_suffix); + patterns.push_back(secondary_oat_dir + "/*/*.odex" + maybe_tmp_suffix); + patterns.push_back(secondary_oat_dir + "/*/*.vdex" + maybe_tmp_suffix); + patterns.push_back(secondary_oat_dir + "/*/*.art" + maybe_tmp_suffix); + } + } + } + + return tools::Glob(patterns); +} + Result<void> ValidateDexPath(const std::string& dex_path) { OR_RETURN(ValidateAbsoluteNormalPath(dex_path)); if (!EndsWith(dex_path, ".apk") && !EndsWith(dex_path, ".jar")) { diff --git a/artd/path_utils.h b/artd/path_utils.h index 0cc017ea83..1063f9118e 100644 --- a/artd/path_utils.h +++ b/artd/path_utils.h @@ -17,6 +17,9 @@ #ifndef ART_ARTD_PATH_UTILS_H_ #define ART_ARTD_PATH_UTILS_H_ +#include <string> +#include <vector> + #include "aidl/com/android/server/art/BnArtd.h" #include "android-base/result.h" #include "base/file_utils.h" @@ -24,6 +27,9 @@ namespace art { namespace artd { +// Returns all existing files that are managed by artd. +android::base::Result<std::vector<std::string>> ListManagedFiles(); + android::base::Result<void> ValidateDexPath(const std::string& dex_path); android::base::Result<std::string> BuildArtBinPath(const std::string& binary_name); diff --git a/libartbase/base/file_utils.cc b/libartbase/base/file_utils.cc index 239628978e..d32a54e76b 100644 --- a/libartbase/base/file_utils.cc +++ b/libartbase/base/file_utils.cc @@ -73,6 +73,8 @@ static constexpr const char* kAndroidSystemExtRootEnvVar = "SYSTEM_EXT_ROOT"; static constexpr const char* kAndroidSystemExtRootDefaultPath = "/system_ext"; static constexpr const char* kAndroidDataEnvVar = "ANDROID_DATA"; static constexpr const char* kAndroidDataDefaultPath = "/data"; +static constexpr const char* kAndroidExpandEnvVar = "ANDROID_EXPAND"; +static constexpr const char* kAndroidExpandDefaultPath = "/mnt/expand"; static constexpr const char* kAndroidArtRootEnvVar = "ANDROID_ART_ROOT"; static constexpr const char* kAndroidConscryptRootEnvVar = "ANDROID_CONSCRYPT_ROOT"; static constexpr const char* kAndroidI18nRootEnvVar = "ANDROID_I18N_ROOT"; @@ -282,6 +284,18 @@ std::string GetAndroidDataSafe(std::string* error_msg) { std::string GetAndroidData() { return GetAndroidDir(kAndroidDataEnvVar, kAndroidDataDefaultPath); } +std::string GetAndroidExpandSafe(std::string* error_msg) { + const char* android_dir = GetAndroidDirSafe(kAndroidExpandEnvVar, + kAndroidExpandDefaultPath, + /*must_exist=*/true, + error_msg); + return (android_dir != nullptr) ? android_dir : ""; +} + +std::string GetAndroidExpand() { + return GetAndroidDir(kAndroidExpandEnvVar, kAndroidExpandDefaultPath); +} + std::string GetArtApexData() { return GetAndroidDir(kArtApexDataEnvVar, kArtApexDataDefaultPath, /*must_exist=*/false); } diff --git a/libartbase/base/file_utils.h b/libartbase/base/file_utils.h index f539f5fd55..89ccaf739c 100644 --- a/libartbase/base/file_utils.h +++ b/libartbase/base/file_utils.h @@ -71,6 +71,11 @@ std::string GetAndroidData(); // Find $ANDROID_DATA, /data, or return an empty string. std::string GetAndroidDataSafe(/*out*/ std::string* error_msg); +// Find $ANDROID_EXPAND, /mnt/expand, or abort. +std::string GetAndroidExpand(); +// Find $ANDROID_EXPAND, /mnt/expand, or return an empty string. +std::string GetAndroidExpandSafe(/*out*/ std::string* error_msg); + // Find $ART_APEX_DATA, /data/misc/apexdata/com.android.art, or abort. std::string GetArtApexData(); diff --git a/libarttools/Android.bp b/libarttools/Android.bp index 13fa205fff..db0e707ce5 100644 --- a/libarttools/Android.bp +++ b/libarttools/Android.bp @@ -43,6 +43,9 @@ cc_library { shared_libs: [ "libbase", ], + static_libs: [ + "libc++fs", + ], export_shared_lib_headers: ["libbase"], } diff --git a/libarttools/tools/tools.cc b/libarttools/tools/tools.cc index a3a91e81e2..6c87969c04 100644 --- a/libarttools/tools/tools.cc +++ b/libarttools/tools/tools.cc @@ -16,12 +16,129 @@ #include "tools.h" +#include <errno.h> +#include <fnmatch.h> + +#include <algorithm> +#include <filesystem> +#include <functional> +#include <string> +#include <string_view> +#include <system_error> +#include <vector> + +#include "android-base/logging.h" +#include "fmt/format.h" + namespace art { namespace tools { -std::string getMsg() { - return "hello world!"; +namespace { + +using ::std::placeholders::_1; + +using ::fmt::literals::operator""_format; // NOLINT + +// Returns true if `path_prefix` matches `pattern` or can be a prefix of a path that matches +// `pattern` (i.e., `path_prefix` represents a directory that may contain a file whose path matches +// `pattern`). +bool PartialMatch(const std::filesystem::path& pattern, const std::filesystem::path& path_prefix) { + for (std::filesystem::path::const_iterator pattern_it = pattern.begin(), + path_prefix_it = path_prefix.begin(); + ; // NOLINT + pattern_it++, path_prefix_it++) { + if (path_prefix_it == path_prefix.end()) { + return true; + } + if (pattern_it == pattern.end()) { + return false; + } + if (*pattern_it == "**") { + return true; + } + if (fnmatch(pattern_it->c_str(), path_prefix_it->c_str(), /*flags=*/0) != 0) { + return false; + } + } } +bool FullMatchRecursive(const std::filesystem::path& pattern, + std::filesystem::path::const_iterator pattern_it, + const std::filesystem::path& path, + std::filesystem::path::const_iterator path_it, + bool double_asterisk_visited = false) { + if (pattern_it == pattern.end() && path_it == path.end()) { + return true; + } + if (pattern_it == pattern.end()) { + return false; + } + if (*pattern_it == "**") { + DCHECK(!double_asterisk_visited); + std::filesystem::path::const_iterator next_pattern_it = pattern_it; + return FullMatchRecursive( + pattern, ++next_pattern_it, path, path_it, /*double_asterisk_visited=*/true) || + (path_it != path.end() && FullMatchRecursive(pattern, pattern_it, path, ++path_it)); + } + if (path_it == path.end()) { + return false; + } + if (fnmatch(pattern_it->c_str(), path_it->c_str(), /*flags=*/0) != 0) { + return false; + } + return FullMatchRecursive(pattern, ++pattern_it, path, ++path_it); } + +// Returns true if `path` fully matches `pattern`. +bool FullMatch(const std::filesystem::path& pattern, const std::filesystem::path& path) { + return FullMatchRecursive(pattern, pattern.begin(), path, path.begin()); } + +void MatchGlobRecursive(const std::vector<std::filesystem::path>& patterns, + const std::filesystem::path& root_dir, + /*out*/ std::vector<std::string>* results) { + std::error_code ec; + for (auto it = std::filesystem::recursive_directory_iterator( + root_dir, std::filesystem::directory_options::skip_permission_denied, ec); + it != std::filesystem::end(it); + it++) { + const std::filesystem::directory_entry& entry = *it; + if (std::none_of(patterns.begin(), patterns.end(), std::bind(PartialMatch, _1, entry.path()))) { + // Avoid unnecessary I/O and SELinux denials. + it.disable_recursion_pending(); + continue; + } + std::error_code ec2; + if (entry.is_regular_file(ec2) && + std::any_of(patterns.begin(), patterns.end(), std::bind(FullMatch, _1, entry.path()))) { + results->push_back(entry.path()); + } + if (ec2) { + // It's expected that we don't have permission to stat some dirs/files, and we don't care + // about them. + if (ec2.value() != EACCES) { + LOG(ERROR) << "Unable to lstat '{}': {}"_format(entry.path().string(), ec2.message()); + } + continue; + } + } + if (ec) { + LOG(ERROR) << "Unable to walk through '{}': {}"_format(root_dir.string(), ec.message()); + } +} + +} // namespace + +std::vector<std::string> Glob(const std::vector<std::string>& patterns, std::string_view root_dir) { + std::vector<std::filesystem::path> parsed_patterns; + parsed_patterns.reserve(patterns.size()); + for (std::string_view pattern : patterns) { + parsed_patterns.emplace_back(pattern); + } + std::vector<std::string> results; + MatchGlobRecursive(parsed_patterns, root_dir, &results); + return results; +} + +} // namespace tools +} // namespace art diff --git a/libarttools/tools/tools.h b/libarttools/tools/tools.h index 8231f5f74a..c2bcee77a6 100644 --- a/libarttools/tools/tools.h +++ b/libarttools/tools/tools.h @@ -18,11 +18,24 @@ #define ART_LIBARTTOOLS_TOOLS_TOOLS_H_ #include <string> +#include <string_view> +#include <vector> namespace art { namespace tools { -std::string getMsg(); +// Searches in a filesystem, starting from `root_dir`. Returns all regular files (i.e., excluding +// directories, symlinks, etc.) that match at least one pattern in `patterns`. Each pattern is an +// absolute path that contains zero or more wildcards. The scan does not follow symlinks to +// directories. +// +// Supported wildcards are: +// - Those documented in glob(7) +// - '**': Matches zero or more path elements. This is only recognised by itself as a path segment. +// +// For simplicity and efficiency, at most one '**' is allowed. +std::vector<std::string> Glob(const std::vector<std::string>& patterns, + std::string_view root_dir = "/"); } // namespace tools } // namespace art diff --git a/libarttools/tools/tools_test.cc b/libarttools/tools/tools_test.cc index 6eaa8f60bb..2f61181c73 100644 --- a/libarttools/tools/tools_test.cc +++ b/libarttools/tools/tools_test.cc @@ -15,14 +15,101 @@ */ #include "tools.h" + +#include <algorithm> +#include <filesystem> +#include <iterator> + +#include "android-base/file.h" +#include "base/common_art_test.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" namespace art { +namespace tools { +namespace { + +using ::android::base::WriteStringToFile; +using ::testing::UnorderedElementsAre; + +void CreateFile(const std::string& filename) { + std::filesystem::path path(filename); + std::filesystem::create_directories(path.parent_path()); + ASSERT_TRUE(WriteStringToFile(/*content=*/"", filename)); +} + +class ArtToolsTest : public CommonArtTest { + protected: + void SetUp() override { + CommonArtTest::SetUp(); + scratch_dir_ = std::make_unique<ScratchDir>(); + scratch_path_ = scratch_dir_->GetPath(); + // Remove the trailing '/'; + scratch_path_.resize(scratch_path_.length() - 1); + } + + void TearDown() override { + scratch_dir_.reset(); + CommonArtTest::TearDown(); + } + + std::unique_ptr<ScratchDir> scratch_dir_; + std::string scratch_path_; +}; + +TEST_F(ArtToolsTest, Glob) { + CreateFile(scratch_path_ + "/abc/def/000.txt"); + CreateFile(scratch_path_ + "/abc/def/ghi/123.txt"); + CreateFile(scratch_path_ + "/abc/def/ghi/456.txt"); + CreateFile(scratch_path_ + "/abc/def/ghi/456.pdf"); + CreateFile(scratch_path_ + "/abc/def/ghi/jkl/456.txt"); + CreateFile(scratch_path_ + "/789.txt"); + CreateFile(scratch_path_ + "/abc/789.txt"); + CreateFile(scratch_path_ + "/abc/aaa/789.txt"); + CreateFile(scratch_path_ + "/abc/aaa/bbb/789.txt"); + CreateFile(scratch_path_ + "/abc/mno/123.txt"); + CreateFile(scratch_path_ + "/abc/aaa/mno/123.txt"); + CreateFile(scratch_path_ + "/abc/aaa/bbb/mno/123.txt"); + CreateFile(scratch_path_ + "/abc/aaa/bbb/mno/ccc/123.txt"); + CreateFile(scratch_path_ + "/pqr/123.txt"); + CreateFile(scratch_path_ + "/abc/pqr/123.txt"); + CreateFile(scratch_path_ + "/abc/aaa/pqr/123.txt"); + CreateFile(scratch_path_ + "/abc/aaa/bbb/pqr/123.txt"); + CreateFile(scratch_path_ + "/abc/aaa/bbb/pqr/ccc/123.txt"); + CreateFile(scratch_path_ + "/abc/aaa/bbb/pqr/ccc/ddd/123.txt"); + + // This symlink will cause infinite recursion. It should not be followed. + std::filesystem::create_directory_symlink(scratch_path_ + "/abc/aaa/bbb/pqr", + scratch_path_ + "/abc/aaa/bbb/pqr/lnk"); + + // This is a directory. It should not be included in the results. + std::filesystem::create_directory(scratch_path_ + "/abc/def/ghi/000.txt"); -class ArtToolsTest : public testing::Test {}; + std::vector<std::string> patterns = { + scratch_path_ + "/abc/def/000.txt", + scratch_path_ + "/abc/def/ghi/*.txt", + scratch_path_ + "/abc/**/789.txt", + scratch_path_ + "/abc/**/mno/*.txt", + scratch_path_ + "/abc/**/pqr/**", + }; -TEST_F(ArtToolsTest, Hello) { - EXPECT_EQ("hello world!", art::tools::getMsg()); + EXPECT_THAT(Glob(patterns, scratch_path_), + UnorderedElementsAre(scratch_path_ + "/abc/def/000.txt", + scratch_path_ + "/abc/def/ghi/123.txt", + scratch_path_ + "/abc/def/ghi/456.txt", + scratch_path_ + "/abc/789.txt", + scratch_path_ + "/abc/aaa/789.txt", + scratch_path_ + "/abc/aaa/bbb/789.txt", + scratch_path_ + "/abc/mno/123.txt", + scratch_path_ + "/abc/aaa/mno/123.txt", + scratch_path_ + "/abc/aaa/bbb/mno/123.txt", + scratch_path_ + "/abc/pqr/123.txt", + scratch_path_ + "/abc/aaa/pqr/123.txt", + scratch_path_ + "/abc/aaa/bbb/pqr/123.txt", + scratch_path_ + "/abc/aaa/bbb/pqr/ccc/123.txt", + scratch_path_ + "/abc/aaa/bbb/pqr/ccc/ddd/123.txt")); } +} // namespace +} // namespace tools } // namespace art |