diff options
author | 2025-03-14 16:54:03 +0000 | |
---|---|---|
committer | 2025-03-21 09:37:10 -0700 | |
commit | 0f7d94bd99bb554d663a4a527155eee6192780ee (patch) | |
tree | 4c3a8211872d9a0ed91cebe00136b07c3ca877e4 | |
parent | 0e235bd9b79c82e3962792eecb17ceb4bbc24089 (diff) |
Count SDM files into TYPE_DEXOPT_ARTIFACT in ART-managed file stats.
SDM files contain ODEX and ART files, which are dexopt artifacts.
We don't need to count the DM files containing VDEX files because the
storage manager counts them itself.
This CL implements the extension to getUsableArtifacts to achieve this,
which is also necessary for the file GC in the next CL.
Also:
- Fix a bug that the runtime images were not counted if the artifact
location is ArtifactsLocation.DM.
- Replace @AutoValue classes with records, to simplify the code.
Bug: 377474232
Test: atest ArtServiceTests
Test: atest art_standalone_artd_tests
Change-Id: If1d05d69051d09ebd7f10e6f487cd7892590921c
-rw-r--r-- | artd/artd.cc | 9 | ||||
-rw-r--r-- | artd/artd.h | 5 | ||||
-rw-r--r-- | artd/artd_test.cc | 13 | ||||
-rw-r--r-- | artd/binder/com/android/server/art/IArtd.aidl | 10 | ||||
-rw-r--r-- | libartservice/service/java/com/android/server/art/ArtFileManager.java | 100 | ||||
-rw-r--r-- | libartservice/service/java/com/android/server/art/ArtManagerLocal.java | 4 | ||||
-rw-r--r-- | libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java | 58 |
7 files changed, 132 insertions, 67 deletions
diff --git a/artd/artd.cc b/artd/artd.cc index 974712eccf..68574b2ce8 100644 --- a/artd/artd.cc +++ b/artd/artd.cc @@ -120,6 +120,7 @@ using ::aidl::com::android::server::art::OutputSecureDexMetadataCompanion; using ::aidl::com::android::server::art::PriorityClass; using ::aidl::com::android::server::art::ProfilePath; using ::aidl::com::android::server::art::RuntimeArtifactsPath; +using ::aidl::com::android::server::art::SecureDexMetadataWithCompanionPaths; using ::aidl::com::android::server::art::VdexPath; using ::android::base::Basename; using ::android::base::Dirname; @@ -1454,6 +1455,14 @@ ScopedAStatus Artd::getVdexFileSize(const VdexPath& in_vdexPath, int64_t* _aidl_ return ScopedAStatus::ok(); } +ndk::ScopedAStatus Artd::getSdmFileSize(const SecureDexMetadataWithCompanionPaths& in_sdmPath, + int64_t* _aidl_return) { + RETURN_FATAL_IF_PRE_REBOOT(options_); + std::string sdm_path = OR_RETURN_FATAL(BuildSdmPath(in_sdmPath)); + *_aidl_return = GetSize(sdm_path).value_or(0); + return ScopedAStatus::ok(); +} + ScopedAStatus Artd::getRuntimeArtifactsSize(const RuntimeArtifactsPath& in_runtimeArtifactsPath, int64_t* _aidl_return) { RETURN_FATAL_IF_PRE_REBOOT(options_); diff --git a/artd/artd.h b/artd/artd.h index ba34ac4ebf..affb180b08 100644 --- a/artd/artd.h +++ b/artd/artd.h @@ -38,6 +38,7 @@ #include "aidl/com/android/server/art/BnArtd.h" #include "aidl/com/android/server/art/BnArtdCancellationSignal.h" #include "aidl/com/android/server/art/BnArtdNotification.h" +#include "aidl/com/android/server/art/SecureDexMetadataWithCompanionPaths.h" #include "android-base/result.h" #include "android-base/thread_annotations.h" #include "android-base/unique_fd.h" @@ -264,6 +265,10 @@ class Artd : public aidl::com::android::server::art::BnArtd { ndk::ScopedAStatus getVdexFileSize(const aidl::com::android::server::art::VdexPath& in_vdexPath, int64_t* _aidl_return) override; + ndk::ScopedAStatus getSdmFileSize( + const aidl::com::android::server::art::SecureDexMetadataWithCompanionPaths& in_sdmPath, + int64_t* _aidl_return) override; + ndk::ScopedAStatus getRuntimeArtifactsSize( const aidl::com::android::server::art::RuntimeArtifactsPath& in_runtimeArtifactsPath, int64_t* _aidl_return) override; diff --git a/artd/artd_test.cc b/artd/artd_test.cc index 10116c7994..d6e383daf3 100644 --- a/artd/artd_test.cc +++ b/artd/artd_test.cc @@ -2579,6 +2579,19 @@ TEST_F(ArtdTest, getVdexFileSize) { EXPECT_EQ(aidl_return, 1); } +TEST_F(ArtdTest, getSdmFileSize) { + CreateFile(scratch_path_ + "/a/b.arm64.sdm", std::string(1, '*')); + + int64_t aidl_return = -1; + ASSERT_TRUE( + artd_ + ->getSdmFileSize( + {.dexPath = scratch_path_ + "/a/b.apk", .isa = "arm64", .isInDalvikCache = false}, + &aidl_return) + .isOk()); + EXPECT_EQ(aidl_return, 1); +} + TEST_F(ArtdTest, getRuntimeArtifactsSize) { CreateFile(android_data_ + "/user_de/0/com.android.foo/cache/oat_primary/arm64/base.art", std::string(1, '*')); diff --git a/artd/binder/com/android/server/art/IArtd.aidl b/artd/binder/com/android/server/art/IArtd.aidl index 12932a345b..8b6ba8b94c 100644 --- a/artd/binder/com/android/server/art/IArtd.aidl +++ b/artd/binder/com/android/server/art/IArtd.aidl @@ -260,6 +260,16 @@ interface IArtd { long getVdexFileSize(in com.android.server.art.VdexPath vdexPath); /** + * Returns the size of the SDM file, in bytes, or 0 if it doesn't exist or a non-fatal error + * occurred. + * + * Not supported in Pre-reboot Dexopt mode. + * + * Throws fatal errors. Logs and ignores non-fatal errors. + */ + long getSdmFileSize(in com.android.server.art.SecureDexMetadataWithCompanionPaths sdmPath); + + /** * Returns the size of the runtime artifacts, in bytes, or 0 if they don't exist or a non-fatal * error occurred. * diff --git a/libartservice/service/java/com/android/server/art/ArtFileManager.java b/libartservice/service/java/com/android/server/art/ArtFileManager.java index 754b9ec1dd..8e382aa1d8 100644 --- a/libartservice/service/java/com/android/server/art/ArtFileManager.java +++ b/libartservice/service/java/com/android/server/art/ArtFileManager.java @@ -45,7 +45,6 @@ import dalvik.system.DexFile; import com.google.auto.value.AutoValue; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; @@ -126,7 +125,7 @@ public class ArtFileManager { } } - return WritableArtifactLists.create(artifacts, runtimeArtifacts); + return new WritableArtifactLists(artifacts, runtimeArtifacts); } /** Returns artifacts that are usable, regardless of whether they are writable. */ @@ -135,6 +134,7 @@ public class ArtFileManager { @NonNull PackageState pkgState, @NonNull AndroidPackage pkg) throws RemoteException { List<ArtifactsPath> artifacts = new ArrayList<>(); List<VdexPath> vdexFiles = new ArrayList<>(); + List<SecureDexMetadataWithCompanionPaths> sdmFiles = new ArrayList<>(); List<RuntimeArtifactsPath> runtimeArtifacts = new ArrayList<>(); var options = ArtFileManager.Options.builder() @@ -159,9 +159,30 @@ public class ArtFileManager { } else { artifacts.add(thisArtifacts); } + } else if (result.artifactsLocation == ArtifactsLocation.SDM_DALVIK_CACHE + || result.artifactsLocation == ArtifactsLocation.SDM_NEXT_TO_DEX) { + sdmFiles.add(AidlUtils.buildSecureDexMetadataWithCompanionPaths( + dexInfo.dexPath(), abi.isa(), + result.artifactsLocation == ArtifactsLocation.SDM_DALVIK_CACHE)); + } + + if (result.artifactsLocation != ArtifactsLocation.NONE_OR_ERROR) { // Runtime images are only generated for primary dex files. if (dexInfo instanceof DetailedPrimaryDexInfo && !DexFile.isOptimizedCompilerFilter(result.compilerFilter)) { + // Those not added to the list are definitely unusable, but those added to + // the list are not necessarily usable. For example, runtime artifacts can + // be outdated when the corresponding dex file is updated, but they may + // still show up in this list. + // + // However, this is not a severe problem. For `ArtManagerLocal.cleanup`, the + // worst result is only that we are keeping more runtime artifacts than + // needed. For `ArtManagerLocal.getArtManagedFileStats`, this is an edge + // case because the API call is transitively initiated by the app itself, + // and the runtime refreshes unusable runtime artifacts as soon as the app + // starts. + // + // TODO(jiakaiz): Improve this. runtimeArtifacts.add(AidlUtils.buildRuntimeArtifactsPath( pkgState.getPackageName(), dexInfo.dexPath(), abi.isa())); } @@ -176,7 +197,7 @@ public class ArtFileManager { } } - return UsableArtifactLists.create(artifacts, vdexFiles, runtimeArtifacts); + return new UsableArtifactLists(artifacts, vdexFiles, sdmFiles, runtimeArtifacts); } @NonNull @@ -209,7 +230,7 @@ public class ArtFileManager { } } - return ProfileLists.create(refProfiles, curProfiles); + return new ProfileLists(refProfiles, curProfiles); } @NonNull @@ -221,71 +242,16 @@ public class ArtFileManager { : mInjector.getDexUseManager().getSecondaryDexInfo(pkgState.getPackageName()); } - @Immutable - @AutoValue - @SuppressWarnings("AutoValueImmutableFields") // Can't use ImmutableList because it's in Guava. - public abstract static class WritableArtifactLists { - protected WritableArtifactLists() {} - - public static @NonNull WritableArtifactLists create(@NonNull List<ArtifactsPath> artifacts, - @NonNull List<RuntimeArtifactsPath> runtimeArtifacts) { - return new AutoValue_ArtFileManager_WritableArtifactLists( - Collections.unmodifiableList(artifacts), - Collections.unmodifiableList(runtimeArtifacts)); - } - - public abstract @NonNull List<ArtifactsPath> artifacts(); - public abstract @NonNull List<RuntimeArtifactsPath> runtimeArtifacts(); - } - - @Immutable - @AutoValue - @SuppressWarnings("AutoValueImmutableFields") // Can't use ImmutableList because it's in Guava. - public abstract static class UsableArtifactLists { - protected UsableArtifactLists() {} - - public static @NonNull UsableArtifactLists create(@NonNull List<ArtifactsPath> artifacts, - @NonNull List<VdexPath> vdexFiles, - @NonNull List<RuntimeArtifactsPath> runtimeArtifacts) { - return new AutoValue_ArtFileManager_UsableArtifactLists( - Collections.unmodifiableList(artifacts), - Collections.unmodifiableList(vdexFiles), - Collections.unmodifiableList(runtimeArtifacts)); - } - - public abstract @NonNull List<ArtifactsPath> artifacts(); - public abstract @NonNull List<VdexPath> vdexFiles(); - - // Those not added to the list are definitely unusable, but those added to the list are not - // necessarily usable. For example, runtime artifacts can be outdated when the corresponding - // dex file is updated, but they may still show up in this list. - // - // However, this is not a severe problem. For `ArtManagerLocal.cleanup`, the worst result is - // only that we are keeping more runtime artifacts than needed. For - // `ArtManagerLocal.getArtManagedFileStats`, this is an edge case because the API call is - // transitively initiated by the app itself, and the runtime refreshes unusable runtime - // artifacts as soon as the app starts. - // - // TODO(jiakaiz): Improve this. - public abstract @NonNull List<RuntimeArtifactsPath> runtimeArtifacts(); - } - - @Immutable - @AutoValue - @SuppressWarnings("AutoValueImmutableFields") // Can't use ImmutableList because it's in Guava. - public abstract static class ProfileLists { - protected ProfileLists() {} - - public static @NonNull ProfileLists create( - @NonNull List<ProfilePath> refProfiles, @NonNull List<ProfilePath> curProfiles) { - return new AutoValue_ArtFileManager_ProfileLists( - Collections.unmodifiableList(refProfiles), - Collections.unmodifiableList(curProfiles)); - } + public record WritableArtifactLists(@NonNull List<ArtifactsPath> artifacts, + @NonNull List<RuntimeArtifactsPath> runtimeArtifacts) {} - public abstract @NonNull List<ProfilePath> refProfiles(); - public abstract @NonNull List<ProfilePath> curProfiles(); + public record UsableArtifactLists(@NonNull List<ArtifactsPath> artifacts, + @NonNull List<VdexPath> vdexFiles, + @NonNull List<SecureDexMetadataWithCompanionPaths> sdmFiles, + @NonNull List<RuntimeArtifactsPath> runtimeArtifacts) {} + public record ProfileLists( + @NonNull List<ProfilePath> refProfiles, @NonNull List<ProfilePath> curProfiles) { public @NonNull List<ProfilePath> allProfiles() { List<ProfilePath> profiles = new ArrayList<>(); profiles.addAll(refProfiles()); diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java index 735da25fbb..acd6063f11 100644 --- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java +++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java @@ -1056,6 +1056,10 @@ public final class ArtManagerLocal { for (RuntimeArtifactsPath runtimeArtifacts : artifactLists.runtimeArtifacts()) { artifactsSize += artd.getRuntimeArtifactsSize(runtimeArtifacts); } + for (SecureDexMetadataWithCompanionPaths sdmFile : artifactLists.sdmFiles()) { + // We don't count SDC files because they are presumed to be tiny. + artifactsSize += artd.getSdmFileSize(sdmFile); + } ProfileLists profileLists = mInjector.getArtFileManager().getProfiles(pkgState, pkg, ArtFileManager.Options.builder() diff --git a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java index a791211705..b17475480e 100644 --- a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java +++ b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java @@ -1426,6 +1426,64 @@ public class ArtManagerLocalTest { } @Test + public void testGetArtManagedFileStatsDmAndSdm() throws Exception { + // It should count the SDM file, but not runtime images. + doReturn(createGetDexoptStatusResult( + "speed-profile", "cloud", "location", ArtifactsLocation.SDM_NEXT_TO_DEX)) + .when(mArtd) + .getDexoptStatus(eq("/somewhere/app/foo/base.apk"), eq("arm64"), any()); + + // It should count the SDM file, but not runtime images. + doReturn(createGetDexoptStatusResult( + "speed-profile", "cloud", "location", ArtifactsLocation.SDM_DALVIK_CACHE)) + .when(mArtd) + .getDexoptStatus(eq("/somewhere/app/foo/base.apk"), eq("arm"), any()); + + // It should count the SDM file and runtime images. + doReturn(createGetDexoptStatusResult( + "verify", "cloud", "location", ArtifactsLocation.SDM_NEXT_TO_DEX)) + .when(mArtd) + .getDexoptStatus(eq("/somewhere/app/foo/split_0.apk"), eq("arm64"), any()); + + // It should only count runtime images. + doReturn(createGetDexoptStatusResult("verify", "vdex", "location", ArtifactsLocation.DM)) + .when(mArtd) + .getDexoptStatus(eq("/somewhere/app/foo/split_0.apk"), eq("arm"), any()); + + // This file is uninteresting in this test. + doReturn(createGetDexoptStatusResult( + "run-from-apk", "unknown", "unknown", ArtifactsLocation.NONE_OR_ERROR)) + .when(mArtd) + .getDexoptStatus(eq("/data/user/0/foo/1.apk"), eq("arm64"), any()); + + // These are counted as TYPE_DEXOPT_ARTIFACT. + doReturn(1l << 0).when(mArtd).getSdmFileSize( + deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths( + "/somewhere/app/foo/base.apk", "arm64", false /* isInDalvikCache */))); + doReturn(1l << 1).when(mArtd).getSdmFileSize( + deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths( + "/somewhere/app/foo/base.apk", "arm", true /* isInDalvikCache */))); + doReturn(1l << 2).when(mArtd).getSdmFileSize( + deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths( + "/somewhere/app/foo/split_0.apk", "arm64", false /* isInDalvikCache */))); + doReturn(1l << 3).when(mArtd).getRuntimeArtifactsSize( + deepEq(AidlUtils.buildRuntimeArtifactsPath( + PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm64"))); + doReturn(1l << 4).when(mArtd).getRuntimeArtifactsSize( + deepEq(AidlUtils.buildRuntimeArtifactsPath( + PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm"))); + + ArtManagedFileStats stats = mArtManagerLocal.getArtManagedFileStats(mSnapshot, PKG_NAME_1); + assertThat(stats.getTotalSizeBytesByType(ArtManagedFileStats.TYPE_DEXOPT_ARTIFACT)) + .isEqualTo((1l << 0) + (1l << 1) + (1l << 2) + (1l << 3) + (1l << 4)); + + verify(mArtd, never()).getArtifactsSize(any()); + verify(mArtd, never()).getVdexFileSize(any()); + verify(mArtd, times(3)).getSdmFileSize(any()); + verify(mArtd, times(2)).getRuntimeArtifactsSize(any()); + } + + @Test public void testCommitPreRebootStagedFiles() throws Exception { when(mSnapshot.getPackageStates()).thenReturn(Map.of(PKG_NAME_1, mPkgState1)); |