diff options
author | 2023-09-08 14:03:53 +0100 | |
---|---|---|
committer | 2023-10-10 19:01:06 +0000 | |
commit | f1b8f88172bd9862ad1651cc03c456508d3ee937 (patch) | |
tree | 16edbe3298fd6cde8a77f2f130b96ee79fb32af4 | |
parent | a824c5b467829b7457a59c443176858af14d02de (diff) |
Update the file GC to cleanup unused runtime images.
Bug: 299442352
Test: atest ArtServiceTests
Test: atest art_standalone_artd_tests
Test: adb shell pm art cleanup
Change-Id: I1e46a442a909d39f01cdf3d843839564115b2ef5
Merged-In: I1e46a442a909d39f01cdf3d843839564115b2ef5
-rw-r--r-- | artd/artd.cc | 16 | ||||
-rw-r--r-- | artd/artd.h | 2 | ||||
-rw-r--r-- | artd/artd_test.cc | 17 | ||||
-rw-r--r-- | artd/binder/com/android/server/art/IArtd.aidl | 3 | ||||
-rw-r--r-- | artd/path_utils.cc | 27 | ||||
-rw-r--r-- | artd/path_utils.h | 13 | ||||
-rw-r--r-- | libartservice/service/java/com/android/server/art/ArtManagerLocal.java | 31 | ||||
-rw-r--r-- | libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java | 23 | ||||
-rw-r--r-- | runtime/oat_file_manager.cc | 3 | ||||
-rw-r--r-- | runtime/runtime_image.cc | 2 | ||||
-rw-r--r-- | runtime/runtime_image.h | 7 |
11 files changed, 108 insertions, 36 deletions
diff --git a/artd/artd.cc b/artd/artd.cc index 89156c28f8..09168a2e24 100644 --- a/artd/artd.cc +++ b/artd/artd.cc @@ -29,6 +29,7 @@ #include <cstring> #include <filesystem> #include <functional> +#include <iterator> #include <map> #include <memory> #include <mutex> @@ -1100,6 +1101,7 @@ ScopedAStatus Artd::createCancellationSignal( ScopedAStatus Artd::cleanup(const std::vector<ProfilePath>& in_profilesToKeep, const std::vector<ArtifactsPath>& in_artifactsToKeep, const std::vector<VdexPath>& in_vdexFilesToKeep, + const std::vector<RuntimeArtifactsPath>& in_runtimeArtifactsToKeep, int64_t* _aidl_return) { std::unordered_set<std::string> files_to_keep; for (const ProfilePath& profile : in_profilesToKeep) { @@ -1114,8 +1116,16 @@ ScopedAStatus Artd::cleanup(const std::vector<ProfilePath>& in_profilesToKeep, for (const VdexPath& vdex : in_vdexFilesToKeep) { files_to_keep.insert(OR_RETURN_FATAL(BuildVdexPath(vdex))); } + std::string android_data = OR_RETURN_NON_FATAL(GetAndroidDataOrError()); + std::string android_expand = OR_RETURN_NON_FATAL(GetAndroidExpandOrError()); + for (const RuntimeArtifactsPath& runtime_image_path : in_runtimeArtifactsToKeep) { + OR_RETURN_FATAL(ValidateRuntimeArtifactsPath(runtime_image_path)); + std::vector<std::string> files = + ListRuntimeArtifactsFiles(android_data, android_expand, runtime_image_path); + std::move(files.begin(), files.end(), std::inserter(files_to_keep, files_to_keep.end())); + } *_aidl_return = 0; - for (const std::string& file : OR_RETURN_NON_FATAL(ListManagedFiles())) { + for (const std::string& file : ListManagedFiles(android_data, android_expand)) { if (files_to_keep.find(file) == files_to_keep.end()) { LOG(INFO) << ART_FORMAT("Cleaning up obsolete file '{}'", file); *_aidl_return += GetSizeAndDeleteFile(file); @@ -1158,8 +1168,10 @@ ScopedAStatus Artd::isInDalvikCache(const std::string& in_dexFile, bool* _aidl_r ScopedAStatus Artd::deleteRuntimeArtifacts(const RuntimeArtifactsPath& in_runtimeArtifactsPath, int64_t* _aidl_return) { OR_RETURN_FATAL(ValidateRuntimeArtifactsPath(in_runtimeArtifactsPath)); + std::string android_data = OR_RETURN_NON_FATAL(GetAndroidDataOrError()); + std::string android_expand = OR_RETURN_NON_FATAL(GetAndroidExpandOrError()); for (const std::string& file : - OR_RETURN_NON_FATAL(ListRuntimeArtifactsFiles(in_runtimeArtifactsPath))) { + ListRuntimeArtifactsFiles(android_data, android_expand, in_runtimeArtifactsPath)) { *_aidl_return += GetSizeAndDeleteFile(file); } return ScopedAStatus::ok(); diff --git a/artd/artd.h b/artd/artd.h index c6bce721a7..8d9fa6f7d3 100644 --- a/artd/artd.h +++ b/artd/artd.h @@ -162,6 +162,8 @@ class Artd : public aidl::com::android::server::art::BnArtd { const std::vector<aidl::com::android::server::art::ProfilePath>& in_profilesToKeep, const std::vector<aidl::com::android::server::art::ArtifactsPath>& in_artifactsToKeep, const std::vector<aidl::com::android::server::art::VdexPath>& in_vdexFilesToKeep, + const std::vector<aidl::com::android::server::art::RuntimeArtifactsPath>& + in_runtimeArtifactsToKeep, int64_t* _aidl_return) override; ndk::ScopedAStatus isInDalvikCache(const std::string& in_dexFile, bool* _aidl_return) override; diff --git a/artd/artd_test.cc b/artd/artd_test.cc index 41c175543c..a1f5d6453a 100644 --- a/artd/artd_test.cc +++ b/artd/artd_test.cc @@ -2016,6 +2016,13 @@ TEST_F(ArtdTest, cleanup) { CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.odex"); CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.vdex"); CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.art"); + CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/cache/oat_primary/arm64/base.art"); + CreateGcKeptFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/arm64/base.art"); + CreateGcKeptFile(android_data_ + "/user/1/com.android.foo/cache/oat_primary/arm64/base.art"); + CreateGcKeptFile(android_expand_ + + "/123456-7890/user/1/com.android.foo/cache/oat_primary/arm64/base.art"); + CreateGcKeptFile(android_data_ + + "/user/0/com.android.foo/cache/not_oat_dir/oat_primary/arm64/base.art"); // Files to remove. CreateGcRemovedFile(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof"); @@ -2050,6 +2057,12 @@ TEST_F(ArtdTest, cleanup) { CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.art.123456.tmp"); CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.bar/aaa/oat/arm64/1.vdex"); + CreateGcRemovedFile(android_data_ + + "/user/0/com.android.different_package/cache/oat_primary/arm64/base.art"); + CreateGcRemovedFile(android_data_ + + "/user/0/com.android.foo/cache/oat_primary/arm64/different_dex.art"); + CreateGcRemovedFile(android_data_ + + "/user/0/com.android.foo/cache/oat_primary/different_isa/base.art"); int64_t aidl_return; ASSERT_TRUE( @@ -2081,6 +2094,10 @@ TEST_F(ArtdTest, cleanup) { .isa = "arm64", .isInDalvikCache = false}}, }, + { + RuntimeArtifactsPath{ + .packageName = "com.android.foo", .isa = "arm64", .dexPath = "/a/b/base.apk"}, + }, &aidl_return) .isOk()); diff --git a/artd/binder/com/android/server/art/IArtd.aidl b/artd/binder/com/android/server/art/IArtd.aidl index f7b2251917..a296337a57 100644 --- a/artd/binder/com/android/server/art/IArtd.aidl +++ b/artd/binder/com/android/server/art/IArtd.aidl @@ -172,7 +172,8 @@ interface IArtd { */ long cleanup(in List<com.android.server.art.ProfilePath> profilesToKeep, in List<com.android.server.art.ArtifactsPath> artifactsToKeep, - in List<com.android.server.art.VdexPath> vdexFilesToKeep); + in List<com.android.server.art.VdexPath> vdexFilesToKeep, + in List<com.android.server.art.RuntimeArtifactsPath> runtimeArtifactsToKeep); /** * Returns whether the artifacts of the primary dex files should be in the global dalvik-cache diff --git a/artd/path_utils.cc b/artd/path_utils.cc index 0a77d9d0c6..0bf50c3b50 100644 --- a/artd/path_utils.cc +++ b/artd/path_utils.cc @@ -97,6 +97,8 @@ Result<void> ValidatePathElement(const std::string& path_element, const std::str return {}; } +} // namespace + Result<std::string> GetAndroidDataOrError() { std::string error_msg; std::string result = GetAndroidDataSafe(&error_msg); @@ -124,12 +126,8 @@ Result<std::string> GetArtRootOrError() { return result; } -} // namespace - -Result<std::vector<std::string>> ListManagedFiles() { - std::string android_data = OR_RETURN(GetAndroidDataOrError()); - std::string android_expand = OR_RETURN(GetAndroidExpandOrError()); - +std::vector<std::string> ListManagedFiles(const std::string& android_data, + const std::string& android_expand) { // See `art::tools::Glob` for the syntax. std::vector<std::string> patterns = { // Profiles for primary dex files. @@ -141,27 +139,30 @@ Result<std::vector<std::string>> ListManagedFiles() { 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"; + std::string data_dir = data_root + user_dir + "/*/*"; + // 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. + std::string secondary_oat_dir = data_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); } + // Runtime image files. + patterns.push_back(RuntimeImage::GetRuntimeImageDir(data_dir) + "**"); } } return tools::Glob(patterns); } -Result<std::vector<std::string>> ListRuntimeArtifactsFiles( +std::vector<std::string> ListRuntimeArtifactsFiles( + const std::string& android_data, + const std::string& android_expand, const RuntimeArtifactsPath& runtime_artifacts_path) { - 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; diff --git a/artd/path_utils.h b/artd/path_utils.h index 48640d0348..bb1597d3bf 100644 --- a/artd/path_utils.h +++ b/artd/path_utils.h @@ -28,10 +28,19 @@ namespace art { namespace artd { +android::base::Result<std::string> GetAndroidDataOrError(); + +android::base::Result<std::string> GetAndroidExpandOrError(); + +android::base::Result<std::string> GetArtRootOrError(); + // Returns all existing files that are managed by artd. -android::base::Result<std::vector<std::string>> ListManagedFiles(); +std::vector<std::string> ListManagedFiles(const std::string& android_data, + const std::string& android_expand); -android::base::Result<std::vector<std::string>> ListRuntimeArtifactsFiles( +std::vector<std::string> ListRuntimeArtifactsFiles( + const std::string& android_data, + const std::string& android_expand, const aidl::com::android::server::art::RuntimeArtifactsPath& runtime_artifacts_path); android::base::Result<void> ValidateRuntimeArtifactsPath( diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java index 1b6c98ecd9..898e6604f8 100644 --- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java +++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java @@ -920,9 +920,14 @@ public final class ArtManagerLocal { // - The dexopt artifacts, if they are up-to-date and the app is not hibernating. // - Only the VDEX part of the dexopt artifacts, if the dexopt artifacts are outdated // but the VDEX part is still usable and the app is not hibernating. + // - The runtime artifacts, if dexopt artifacts are fully or partially usable and the + // usable parts don't contain AOT-compiled code. (This logic must be aligned with the + // one that determines when runtime images can be loaded in + // `OatFileManager::OpenDexFilesFromOat` in `art/runtime/oat_file_manager.cc`.) List<ProfilePath> profilesToKeep = new ArrayList<>(); List<ArtifactsPath> artifactsToKeep = new ArrayList<>(); List<VdexPath> vdexFilesToKeep = new ArrayList<>(); + List<RuntimeArtifactsPath> runtimeArtifactsToKeep = new ArrayList<>(); for (PackageState pkgState : snapshot.getPackageStates().values()) { if (!Utils.canDexoptPackage(pkgState, null /* appHibernationManager */)) { @@ -942,8 +947,9 @@ public final class ArtManagerLocal { mInjector.getUserManager(), pkgState, dexInfo)); if (keepArtifacts) { for (Abi abi : Utils.getAllAbis(pkgState)) { - maybeKeepArtifacts(artifactsToKeep, vdexFilesToKeep, pkgState, dexInfo, - abi, isInDalvikCache); + maybeKeepArtifacts(artifactsToKeep, vdexFilesToKeep, + runtimeArtifactsToKeep, pkgState, dexInfo, abi, + isInDalvikCache); } } } @@ -956,13 +962,15 @@ public final class ArtManagerLocal { AidlUtils.buildProfilePathForSecondaryCur(dexInfo.dexPath())); if (keepArtifacts) { for (Abi abi : Utils.getAllAbisForNames(dexInfo.abiNames(), pkgState)) { - maybeKeepArtifacts(artifactsToKeep, vdexFilesToKeep, pkgState, dexInfo, - abi, false /* isInDalvikCache */); + maybeKeepArtifacts(artifactsToKeep, vdexFilesToKeep, + runtimeArtifactsToKeep, pkgState, dexInfo, abi, + false /* isInDalvikCache */); } } } } - return mInjector.getArtd().cleanup(profilesToKeep, artifactsToKeep, vdexFilesToKeep); + return mInjector.getArtd().cleanup( + profilesToKeep, artifactsToKeep, vdexFilesToKeep, runtimeArtifactsToKeep); } catch (RemoteException e) { Utils.logArtdException(e); return 0; @@ -975,9 +983,10 @@ public final class ArtManagerLocal { */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) private void maybeKeepArtifacts(@NonNull List<ArtifactsPath> artifactsToKeep, - @NonNull List<VdexPath> vdexFilesToKeep, @NonNull PackageState pkgState, - @NonNull DetailedDexInfo dexInfo, @NonNull Abi abi, boolean isInDalvikCache) - throws RemoteException { + @NonNull List<VdexPath> vdexFilesToKeep, + @NonNull List<RuntimeArtifactsPath> runtimeArtifactsToKeep, + @NonNull PackageState pkgState, @NonNull DetailedDexInfo dexInfo, @NonNull Abi abi, + boolean isInDalvikCache) throws RemoteException { try { GetDexoptStatusResult result = mInjector.getArtd().getDexoptStatus( dexInfo.dexPath(), abi.isa(), dexInfo.classLoaderContext()); @@ -994,6 +1003,12 @@ public final class ArtManagerLocal { } else { artifactsToKeep.add(artifacts); } + // Runtime images are only generated for primary dex files. + if (dexInfo instanceof DetailedPrimaryDexInfo + && !DexFile.isOptimizedCompilerFilter(result.compilerFilter)) { + runtimeArtifactsToKeep.add(AidlUtils.buildRuntimeArtifactsPath( + pkgState.getPackageName(), dexInfo.dexPath(), abi.isa())); + } } } catch (ServiceSpecificException e) { // Don't add the artifacts to the lists. They should be cleaned up. diff --git a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java index 7e41f32e2b..900196e82b 100644 --- a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java +++ b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java @@ -942,7 +942,7 @@ public class ArtManagerLocalTest { @Test public void testCleanup() throws Exception { - // It should keep all artifacts. + // It should keep all artifacts, but not runtime images. doReturn(createGetDexoptStatusResult("speed-profile", "bg-dexopt", "location")) .when(mArtd) .getDexoptStatus(eq("/data/app/foo/base.apk"), eq("arm64"), any()); @@ -950,15 +950,17 @@ public class ArtManagerLocalTest { .when(mArtd) .getDexoptStatus(eq("/data/user/0/foo/1.apk"), eq("arm64"), any()); - // It should only keep VDEX files. - doReturn(createGetDexoptStatusResult("verify", "vdex", "location")) + // It should keep all artifacts and runtime images. + doReturn(createGetDexoptStatusResult("verify", "bg-dexopt", "location")) .when(mArtd) .getDexoptStatus(eq("/data/app/foo/split_0.apk"), eq("arm64"), any()); + + // It should only keep VDEX files and runtime images. doReturn(createGetDexoptStatusResult("verify", "vdex", "location")) .when(mArtd) .getDexoptStatus(eq("/data/app/foo/split_0.apk"), eq("arm"), any()); - // It should not keep any artifacts. + // It should not keep any artifacts or runtime images. doReturn(createGetDexoptStatusResult("run-from-apk", "unknown", "unknown")) .when(mArtd) .getDexoptStatus(eq("/data/app/foo/base.apk"), eq("arm"), any()); @@ -982,12 +984,15 @@ public class ArtManagerLocalTest { inAnyOrderDeepEquals(AidlUtils.buildArtifactsPath( "/data/app/foo/base.apk", "arm64", mIsInDalvikCache), AidlUtils.buildArtifactsPath( - "/data/user/0/foo/1.apk", "arm64", false /* isInDalvikCache */)), - inAnyOrderDeepEquals( - VdexPath.artifactsPath(AidlUtils.buildArtifactsPath( + "/data/user/0/foo/1.apk", "arm64", false /* isInDalvikCache */), + AidlUtils.buildArtifactsPath( "/data/app/foo/split_0.apk", "arm64", mIsInDalvikCache)), - VdexPath.artifactsPath(AidlUtils.buildArtifactsPath( - "/data/app/foo/split_0.apk", "arm", mIsInDalvikCache)))); + inAnyOrderDeepEquals(VdexPath.artifactsPath(AidlUtils.buildArtifactsPath( + "/data/app/foo/split_0.apk", "arm", mIsInDalvikCache))), + inAnyOrderDeepEquals(AidlUtils.buildRuntimeArtifactsPath( + PKG_NAME_1, "/data/app/foo/split_0.apk", "arm64"), + AidlUtils.buildRuntimeArtifactsPath( + PKG_NAME_1, "/data/app/foo/split_0.apk", "arm"))); } private AndroidPackage createPackage(boolean multiSplit) { diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index bd3165146d..5c97db20ba 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -289,6 +289,9 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( // is executable. image_space = oat_file_assistant->OpenImageSpace(oat_file.get()); } + // Load the runtime image. This logic must be aligned with the one that determines when to + // keep runtime images in `ArtManagerLocal.cleanup` in + // `art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java`. if (kEnableRuntimeAppImage && image_space == nullptr && !compilation_enabled) { std::string art_file = RuntimeImage::GetRuntimeImagePath(dex_location); std::string error_msg; diff --git a/runtime/runtime_image.cc b/runtime/runtime_image.cc index 23b7354744..5fb27a9f0d 100644 --- a/runtime/runtime_image.cc +++ b/runtime/runtime_image.cc @@ -1812,7 +1812,7 @@ class RuntimeImageHelper { friend class NativePointerVisitor; }; -static std::string GetRuntimeImageDir(const std::string& app_data_dir) { +std::string RuntimeImage::GetRuntimeImageDir(const std::string& app_data_dir) { if (app_data_dir.empty()) { // The data directory is empty for tests. return ""; diff --git a/runtime/runtime_image.h b/runtime/runtime_image.h index ed891b4995..cea72a2479 100644 --- a/runtime/runtime_image.h +++ b/runtime/runtime_image.h @@ -36,6 +36,13 @@ class RuntimeImage { // Same as above, but takes data dir and ISA from the runtime. static std::string GetRuntimeImagePath(const std::string& dex_location); + + // Gets the directory that stores runtime-generated app images. Note that the return value + // contains a trailing '/'. + // + // If the argument is a valid glob (a pattern that contains '**' or those documented in glob(7)), + // returns a valid glob. + static std::string GetRuntimeImageDir(const std::string& app_data_dir); }; } // namespace art |