Skip to content

Commit

Permalink
Calculate ART managed file stats.
Browse files Browse the repository at this point in the history
Bug: 317081212
Test: atest ArtServiceTests
Change-Id: Iede2d323995255b2414150b025ef125b9ebbc661
  • Loading branch information
jiakaiz-g committed Jan 8, 2024
1 parent 93f35d3 commit 3208e7e
Show file tree
Hide file tree
Showing 7 changed files with 416 additions and 0 deletions.
38 changes: 38 additions & 0 deletions artd/artd.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1290,6 +1290,44 @@ ScopedAStatus Artd::deleteRuntimeArtifacts(const RuntimeArtifactsPath& in_runtim
return ScopedAStatus::ok();
}

ScopedAStatus Artd::getArtifactsSize(const ArtifactsPath& in_artifactsPath, int64_t* _aidl_return) {
std::string oat_path = OR_RETURN_FATAL(BuildOatPath(in_artifactsPath));
*_aidl_return = 0;
*_aidl_return += GetSize(oat_path).value_or(0);
*_aidl_return += GetSize(OatPathToVdexPath(oat_path)).value_or(0);
*_aidl_return += GetSize(OatPathToArtPath(oat_path)).value_or(0);
return ScopedAStatus::ok();
}

ScopedAStatus Artd::getVdexFileSize(const VdexPath& in_vdexPath, int64_t* _aidl_return) {
std::string vdex_path = OR_RETURN_FATAL(BuildVdexPath(in_vdexPath));
*_aidl_return = GetSize(vdex_path).value_or(0);
return ScopedAStatus::ok();
}

ScopedAStatus Artd::getRuntimeArtifactsSize(const RuntimeArtifactsPath& in_runtimeArtifactsPath,
int64_t* _aidl_return) {
OR_RETURN_FATAL(ValidateRuntimeArtifactsPath(in_runtimeArtifactsPath));
*_aidl_return = 0;
Result<std::string> android_data = GetAndroidDataOrError();
Result<std::string> android_expand = GetAndroidExpandOrError();
if (!android_data.ok() || !android_expand.ok()) {
LOG(ERROR) << "Failed to get the path to ANDROID_DATA or ANDROID_EXPAND";
return ScopedAStatus::ok();
}
for (const std::string& file : ListRuntimeArtifactsFiles(
android_data.value(), android_expand.value(), in_runtimeArtifactsPath)) {
*_aidl_return += GetSize(file).value_or(0);
}
return ScopedAStatus::ok();
}

ScopedAStatus Artd::getProfileSize(const ProfilePath& in_profile, int64_t* _aidl_return) {
std::string profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile));
*_aidl_return = GetSize(profile_path).value_or(0);
return ScopedAStatus::ok();
}

Result<void> Artd::Start() {
OR_RETURN(SetLogVerbosity());
MemMap::Init();
Expand Down
14 changes: 14 additions & 0 deletions artd/artd.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,20 @@ class Artd : public aidl::com::android::server::art::BnArtd {
const aidl::com::android::server::art::RuntimeArtifactsPath& in_runtimeArtifactsPath,
int64_t* _aidl_return) override;

ndk::ScopedAStatus getArtifactsSize(
const aidl::com::android::server::art::ArtifactsPath& in_artifactsPath,
int64_t* _aidl_return) override;

ndk::ScopedAStatus getVdexFileSize(const aidl::com::android::server::art::VdexPath& in_vdexPath,
int64_t* _aidl_return) override;

ndk::ScopedAStatus getRuntimeArtifactsSize(
const aidl::com::android::server::art::RuntimeArtifactsPath& in_runtimeArtifactsPath,
int64_t* _aidl_return) override;

ndk::ScopedAStatus getProfileSize(const aidl::com::android::server::art::ProfilePath& in_profile,
int64_t* _aidl_return) override;

android::base::Result<void> Start();

private:
Expand Down
86 changes: 86 additions & 0 deletions artd/artd_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2331,6 +2331,92 @@ TEST_F(ArtdTest, deleteRuntimeArtifactsSpecialChars) {
}
}

TEST_F(ArtdTest, getArtifactsSize) {
std::string oat_dir = scratch_path_ + "/a/oat/arm64";
CreateFile(oat_dir + "/b.odex", std::string(1, '*'));
CreateFile(oat_dir + "/b.vdex", std::string(2, '*'));
CreateFile(oat_dir + "/b.art", std::string(4, '*'));

// Irrelevant.
CreateFile(oat_dir + "/c.vdex", std::string(8, '*'));

int64_t aidl_return = -1;
ASSERT_TRUE(
artd_
->getArtifactsSize(
{.dexPath = scratch_path_ + "/a/b.apk", .isa = "arm64", .isInDalvikCache = false},
&aidl_return)
.isOk());
EXPECT_EQ(aidl_return, 1 + 2 + 4);
}

TEST_F(ArtdTest, getVdexFileSize) {
std::string oat_dir = scratch_path_ + "/a/oat/arm64";
CreateFile(oat_dir + "/b.vdex", std::string(1, '*'));

// Irrelevant.
CreateFile(oat_dir + "/b.odex", std::string(2, '*'));
CreateFile(oat_dir + "/b.art", std::string(4, '*'));
CreateFile(oat_dir + "/c.vdex", std::string(8, '*'));

int64_t aidl_return = -1;
ASSERT_TRUE(artd_
->getVdexFileSize(ArtifactsPath{.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, '*'));
CreateFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/arm64/base.art",
std::string(2, '*'));
CreateFile(android_data_ + "/user/1/com.android.foo/cache/oat_primary/arm64/base.art",
std::string(4, '*'));
CreateFile(
android_expand_ + "/123456-7890/user/1/com.android.foo/cache/oat_primary/arm64/base.art",
std::string(8, '*'));

// Irrelevant.
CreateFile(android_expand_ + "/user/0/com.android.foo/cache/oat_primary/arm64/different_dex.art",
std::string(16, '*'));

int64_t aidl_return = -1;
ASSERT_TRUE(
artd_
->getRuntimeArtifactsSize(
{.packageName = "com.android.foo", .dexPath = "/a/b/base.apk", .isa = "arm64"},
&aidl_return)
.isOk());

EXPECT_EQ(aidl_return, 1 + 2 + 4 + 8);
}

TEST_F(ArtdTest, getProfileSize) {
CreateFile(android_data_ + "/misc/profiles/cur/0/com.android.foo/primary.prof",
std::string(1, '*'));

// Irrelevant.
CreateFile(android_data_ + "/misc/profiles/cur/0/com.android.foo/split_0.split.prof",
std::string(2, '*'));
CreateFile(android_data_ + "/misc/profiles/cur/0/com.android.bar/primary.prof",
std::string(4, '*'));
CreateFile(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof",
std::string(8, '*'));

int64_t aidl_return = -1;
ASSERT_TRUE(artd_
->getProfileSize(
PrimaryCurProfilePath{
.userId = 0, .packageName = "com.android.foo", .profileName = "primary"},
&aidl_return)
.isOk());
EXPECT_EQ(aidl_return, 1);
}

} // namespace
} // namespace artd
} // namespace art
35 changes: 35 additions & 0 deletions artd/binder/com/android/server/art/IArtd.aidl
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,39 @@ interface IArtd {
*/
long deleteRuntimeArtifacts(
in com.android.server.art.RuntimeArtifactsPath runtimeArtifactsPath);

/**
* Returns the size of the dexopt artifacts, in bytes, or 0 if they don't exist or a non-fatal
* error occurred.
*
* Throws fatal errors. Logs and ignores non-fatal errors.
*/
long getArtifactsSize(in com.android.server.art.ArtifactsPath artifactsPath);

/**
* Returns the size of the vdex file, in bytes, or 0 if it doesn't exist or a non-fatal error
* occurred.
*
* Throws fatal errors. Logs and ignores non-fatal errors.
*/
long getVdexFileSize(in com.android.server.art.VdexPath vdexPath);

/**
* Returns the size of the runtime artifacts, in bytes, or 0 if they don't exist or a non-fatal
* error occurred.
*
* Throws fatal errors. Logs and ignores non-fatal errors.
*/
long getRuntimeArtifactsSize(
in com.android.server.art.RuntimeArtifactsPath runtimeArtifactsPath);

/**
* Returns the size of the profile, in bytes, or 0 if it doesn't exist or a non-fatal error
* occurred.
*
* Operates on the whole DM file if given one.
*
* Throws fatal errors. Logs and ignores non-fatal errors.
*/
long getProfileSize(in com.android.server.art.ProfilePath profile);
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalManagerRegistry;
import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.ArtManagedFileStats;
import com.android.server.art.model.BatchDexoptParams;
import com.android.server.art.model.Config;
import com.android.server.art.model.DeleteResult;
Expand Down Expand Up @@ -873,6 +874,57 @@ public void dumpPackage(@NonNull PrintWriter pw,
pw, snapshot, Utils.getPackageStateOrThrow(snapshot, packageName));
}

/**
* Returns the statistics of the files managed by ART of a package.
*
* @throws IllegalArgumentException if the package is not found
* @throws IllegalStateException if the operation encounters an error that should never happen
* (e.g., an internal logic error).
*
* @hide
*/
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@NonNull
public ArtManagedFileStats getArtManagedFileStats(
@NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) {
PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName);
AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);

try {
long artifactsSize = 0;
long refProfilesSize = 0;
long curProfilesSize = 0;
IArtd artd = mInjector.getArtd();

UsableArtifactLists artifactLists =
mInjector.getArtFileManager().getUsableArtifacts(pkgState, pkg);
for (ArtifactsPath artifacts : artifactLists.artifacts()) {
artifactsSize += artd.getArtifactsSize(artifacts);
}
for (VdexPath vdexFile : artifactLists.vdexFiles()) {
artifactsSize += artd.getVdexFileSize(vdexFile);
}
for (RuntimeArtifactsPath runtimeArtifacts : artifactLists.runtimeArtifacts()) {
artifactsSize += artd.getRuntimeArtifactsSize(runtimeArtifacts);
}

ProfileLists profileLists = mInjector.getArtFileManager().getProfiles(
pkgState, pkg, true /* alsoForSecondaryDex */, true /* excludeDexNotFound */);
for (ProfilePath profile : profileLists.refProfiles()) {
refProfilesSize += artd.getProfileSize(profile);
}
for (ProfilePath profile : profileLists.curProfiles()) {
curProfilesSize += artd.getProfileSize(profile);
}

return ArtManagedFileStats.create(artifactsSize, refProfilesSize, curProfilesSize);
} catch (RemoteException e) {
Utils.logArtdException(e);
return ArtManagedFileStats.create(
0 /* artifactsSize */, 0 /* refProfilesSize */, 0 /* curProfilesSize */);
}
}

/**
* Cleans up obsolete profiles and artifacts.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.android.server.art.model;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;

import com.android.internal.annotations.Immutable;

import com.google.auto.value.AutoValue;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.Map;

/**
* Statistics of the files managed by ART of a package.
*
* @hide
*/
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
@Immutable
@AutoValue
@SuppressWarnings("AutoValueImmutableFields") // Can't use ImmutableMap because it's in Guava.
public abstract class ArtManagedFileStats {
/**
* Dexopt artifacts and runtime artifacts, excluding stale ones.
*
* Dexopt artifacts become stale when one of their dependencies (such as bootclasspath jars and
* share libraries) has changed. Those stale files are excluded from the statistics. They may be
* cleaned up or replaced by ART Service at any time.
*
* For a preload app, this type includes dexopt artifacts on readonly partitions if they are
* up-to-date.
*/
public static final int TYPE_DEXOPT_ARTIFACT = 0;
/**
* Reference profiles.
*
* They are the ones used during the last profile-guided dexopt.
*/
public static final int TYPE_REF_PROFILE = 1;
/**
* Current profiles.
*
* They may be used during the next profile-guided dexopt.
*/
public static final int TYPE_CUR_PROFILE = 2;

/** @hide */
// clang-format off
@IntDef(prefix = {"TYPE_"}, value = {
TYPE_DEXOPT_ARTIFACT,
TYPE_REF_PROFILE,
TYPE_CUR_PROFILE,
})
// clang-format on
@Retention(RetentionPolicy.SOURCE)
public @interface FileTypes {}

/** @hide */
protected ArtManagedFileStats() {}

/** @hide */
public static @NonNull ArtManagedFileStats create(
long artifactsSize, long refProfilesSize, long curProfilesSize) {
return new AutoValue_ArtManagedFileStats(Map.of(TYPE_DEXOPT_ARTIFACT, artifactsSize,
TYPE_REF_PROFILE, refProfilesSize, TYPE_CUR_PROFILE, curProfilesSize));
}

/** @hide */
public abstract @NonNull Map<Integer, Long> getTotalSizesBytes();

/**
* Returns the total size, in bytes, of the files of the given type.
*
* @throws IllegalArgumentException if {@code fileType} is not one of those defined in {@link
* FileTypes}.
*
* @hide
*/
public long getTotalSizeBytesByType(@FileTypes int fileType) {
Long value = getTotalSizesBytes().get(fileType);
if (value == null) {
throw new IllegalArgumentException("Unknown file type " + fileType);
}
return value;
}
}
Loading

0 comments on commit 3208e7e

Please sign in to comment.