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
diff --git a/artd/artd.cc b/artd/artd.cc
index 89156c2..09168a2 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::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 @@
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::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 c6bce72..8d9fa6f 100644
--- a/artd/artd.h
+++ b/artd/artd.h
@@ -162,6 +162,8 @@
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 41c1755..a1f5d64 100644
--- a/artd/artd_test.cc
+++ b/artd/artd_test.cc
@@ -2016,6 +2016,13 @@
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 @@
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 @@
.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 f7b2251..a296337 100644
--- a/artd/binder/com/android/server/art/IArtd.aidl
+++ b/artd/binder/com/android/server/art/IArtd.aidl
@@ -172,7 +172,8 @@
*/
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 0a77d9d..0bf50c3 100644
--- a/artd/path_utils.cc
+++ b/artd/path_utils.cc
@@ -97,6 +97,8 @@
return {};
}
+} // namespace
+
Result<std::string> GetAndroidDataOrError() {
std::string error_msg;
std::string result = GetAndroidDataSafe(&error_msg);
@@ -124,12 +126,8 @@
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 @@
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 48640d0..bb1597d 100644
--- a/artd/path_utils.h
+++ b/artd/path_utils.h
@@ -28,10 +28,19 @@
namespace art {
namespace artd {
-// Returns all existing files that are managed by artd.
-android::base::Result<std::vector<std::string>> ListManagedFiles();
+android::base::Result<std::string> GetAndroidDataOrError();
-android::base::Result<std::vector<std::string>> ListRuntimeArtifactsFiles(
+android::base::Result<std::string> GetAndroidExpandOrError();
+
+android::base::Result<std::string> GetArtRootOrError();
+
+// Returns all existing files that are managed by artd.
+std::vector<std::string> ListManagedFiles(const std::string& android_data,
+ const std::string& android_expand);
+
+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 1b6c98e..898e660 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 @@
// - 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 @@
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 @@
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 @@
*/
@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 @@
} 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 7e41f32..900196e 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 @@
@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 @@
.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 @@
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 bd31651..5c97db2 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -289,6 +289,9 @@
// 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 23b7354..5fb27a9 100644
--- a/runtime/runtime_image.cc
+++ b/runtime/runtime_image.cc
@@ -1812,7 +1812,7 @@
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 ed891b4..cea72a2 100644
--- a/runtime/runtime_image.h
+++ b/runtime/runtime_image.h
@@ -36,6 +36,13 @@
// 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