summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-12-01 04:13:30 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-12-01 04:13:30 +0000
commit36a76cd7b5f32cd1af7fdde5dc4a2d44d8a2eddf (patch)
treee40dec14cd56f811124d13c8408e527e6851ffcd
parent263216dd389cd074427e4e7e4fcf6b2d6b910e2e (diff)
parent674e1a3690a240f8fabcd162ff7bfe4248177c35 (diff)
downloadart-android14-mainline-cellbroadcast-release.tar.gz
Snap for 11164065 from 674e1a3690a240f8fabcd162ff7bfe4248177c35 to mainline-cellbroadcast-releaseaml_cbr_341410010android14-mainline-cellbroadcast-release
Change-Id: I2d0647a0147ceadaaedf690cd4e94693698e5b61
-rw-r--r--TEST_MAPPING9
-rw-r--r--artd/artd.cc12
-rw-r--r--artd/artd_test.cc17
-rw-r--r--artd/binder/com/android/server/art/MergeProfileOptions.aidl2
-rw-r--r--compiler/optimizing/write_barrier_elimination.cc9
-rw-r--r--libartservice/service/api/system-server-current.txt6
-rw-r--r--libartservice/service/java/com/android/server/art/ArtManagerLocal.java103
-rw-r--r--libartservice/service/java/com/android/server/art/ArtShellCommand.java68
-rw-r--r--libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java65
-rw-r--r--libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java59
-rw-r--r--libartservice/service/java/com/android/server/art/Dexopter.java5
-rw-r--r--libartservice/service/java/com/android/server/art/model/ArtFlags.java57
-rw-r--r--libartservice/service/java/com/android/server/art/model/DexoptParams.java8
-rw-r--r--libartservice/service/java/com/android/server/art/model/DexoptResult.java21
-rw-r--r--libartservice/service/java/com/android/server/art/model/OperationProgress.java2
-rw-r--r--libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java87
-rw-r--r--libartservice/service/javatests/com/android/server/art/BackgroundDexoptJobTest.java42
-rw-r--r--libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java36
-rw-r--r--libartservice/service/javatests/com/android/server/art/model/DexoptParamsTest.java34
-rw-r--r--libnativeloader/library_namespaces.cpp5
-rw-r--r--libnativeloader/native_loader_test.cpp18
-rw-r--r--libnativeloader/public_libraries.cpp61
-rw-r--r--libnativeloader/public_libraries.h10
-rw-r--r--libprofile/profile/profile_compilation_info.cc11
-rw-r--r--libprofile/profile/profile_compilation_info_test.cc22
-rw-r--r--profman/include/profman/profman_result.h10
-rw-r--r--profman/profile_assistant.cc37
-rw-r--r--profman/profile_assistant.h16
-rw-r--r--profman/profile_assistant_test.cc209
-rw-r--r--profman/profman.cc13
-rw-r--r--runtime/gc/collector/mark_compact.cc37
-rw-r--r--runtime/gc/heap-inl.h3
-rw-r--r--runtime/gc/heap.cc8
-rw-r--r--runtime/gc/heap.h5
-rw-r--r--runtime/gc/space/bump_pointer_space.cc12
-rw-r--r--runtime/gc/space/bump_pointer_space.h9
-rw-r--r--runtime/javaheapprof/javaheapsampler.cc4
-rw-r--r--runtime/javaheapprof/javaheapsampler.h4
-rw-r--r--runtime/jit/jit.cc2
-rw-r--r--runtime/native/dalvik_system_ZygoteHooks.cc10
-rw-r--r--test/2247-checker-write-barrier-elimination/Android.bp2
-rw-r--r--test/knownfailures.json5
-rwxr-xr-xtest/utils/regen-test-files4
-rwxr-xr-xtools/buildbot-build.sh2
44 files changed, 903 insertions, 258 deletions
diff --git a/TEST_MAPPING b/TEST_MAPPING
index d9d5431e3e..084f3d7386 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -455,9 +455,6 @@
"name": "art-run-test-2244-checker-remove-try-boundary[com.google.android.art.apex]"
},
{
- "name": "art-run-test-2247-checker-write-barrier-elimination[com.google.android.art.apex]"
- },
- {
"name": "art-run-test-2249-checker-return-try-boundary-exit-in-loop[com.google.android.art.apex]"
},
{
@@ -1880,9 +1877,6 @@
"name": "art-run-test-2244-checker-remove-try-boundary"
},
{
- "name": "art-run-test-2247-checker-write-barrier-elimination"
- },
- {
"name": "art-run-test-2249-checker-return-try-boundary-exit-in-loop"
},
{
@@ -3289,9 +3283,6 @@
"name": "art-run-test-2244-checker-remove-try-boundary"
},
{
- "name": "art-run-test-2247-checker-write-barrier-elimination"
- },
- {
"name": "art-run-test-2249-checker-return-try-boundary-exit-in-loop"
},
{
diff --git a/artd/artd.cc b/artd/artd.cc
index 6132a22eab..04a01a9ca4 100644
--- a/artd/artd.cc
+++ b/artd/artd.cc
@@ -719,10 +719,9 @@ ndk::ScopedAStatus Artd::mergeProfiles(const std::vector<ProfilePath>& in_profil
OR_RETURN_NON_FATAL(NewFile::Create(output_profile_path, in_outputProfile->fsPermission));
if (in_referenceProfile.has_value()) {
- if (in_options.forceMerge || in_options.dumpOnly || in_options.dumpClassesAndMethods) {
+ if (in_options.dumpOnly || in_options.dumpClassesAndMethods) {
return Fatal(
- "Reference profile must not be set when 'forceMerge', 'dumpOnly', or "
- "'dumpClassesAndMethods' is set");
+ "Reference profile must not be set when 'dumpOnly' or 'dumpClassesAndMethods' is set");
}
std::string reference_profile_path =
OR_RETURN_FATAL(BuildProfileOrDmPath(*in_referenceProfile));
@@ -755,7 +754,7 @@ ndk::ScopedAStatus Artd::mergeProfiles(const std::vector<ProfilePath>& in_profil
props_->GetOrEmpty("dalvik.vm.bgdexopt.new-classes-percent"))
.AddIfNonEmpty("--min-new-methods-percent-change=%s",
props_->GetOrEmpty("dalvik.vm.bgdexopt.new-methods-percent"))
- .AddIf(in_options.forceMerge, "--force-merge")
+ .AddIf(in_options.forceMerge, "--force-merge-and-analyze")
.AddIf(in_options.forBootImage, "--boot-image-merge");
}
@@ -778,9 +777,8 @@ ndk::ScopedAStatus Artd::mergeProfiles(const std::vector<ProfilePath>& in_profil
}
ProfmanResult::ProcessingResult expected_result =
- (in_options.forceMerge || in_options.dumpOnly || in_options.dumpClassesAndMethods) ?
- ProfmanResult::kSuccess :
- ProfmanResult::kCompile;
+ (in_options.dumpOnly || in_options.dumpClassesAndMethods) ? ProfmanResult::kSuccess :
+ ProfmanResult::kCompile;
if (result.value() != expected_result) {
return NonFatal(ART_FORMAT("profman returned an unexpected code: {}", result.value()));
}
diff --git a/artd/artd_test.cc b/artd/artd_test.cc
index 5b6c5b2767..f1806e1c2d 100644
--- a/artd/artd_test.cc
+++ b/artd/artd_test.cc
@@ -1756,7 +1756,7 @@ TEST_F(ArtdTest, mergeProfiles) {
Contains(Flag("--reference-profile-file-fd=", FdHasContent("abc"))),
Contains(Flag("--apk-fd=", FdOf(dex_file_1))),
Contains(Flag("--apk-fd=", FdOf(dex_file_2))),
- Not(Contains("--force-merge")),
+ Not(Contains("--force-merge-and-analyze")),
Not(Contains("--boot-image-merge")))),
HasKeepFdsFor("--profile-file-fd=", "--reference-profile-file-fd=", "--apk-fd=")),
_,
@@ -1872,13 +1872,14 @@ TEST_F(ArtdTest, mergeProfilesWithOptionsForceMerge) {
CreateFile(dex_file_);
- EXPECT_CALL(
- *mock_exec_utils_,
- DoExecAndReturnCode(
- WhenSplitBy("--", _, AllOf(Contains("--force-merge"), Contains("--boot-image-merge"))),
- _,
- _))
- .WillOnce(Return(ProfmanResult::kSuccess));
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(WhenSplitBy("--",
+ _,
+ AllOf(Contains("--force-merge-and-analyze"),
+ Contains("--boot-image-merge"))),
+ _,
+ _))
+ .WillOnce(Return(ProfmanResult::kCompile));
bool result;
EXPECT_TRUE(artd_
diff --git a/artd/binder/com/android/server/art/MergeProfileOptions.aidl b/artd/binder/com/android/server/art/MergeProfileOptions.aidl
index 2d007f9406..5d3591ccf2 100644
--- a/artd/binder/com/android/server/art/MergeProfileOptions.aidl
+++ b/artd/binder/com/android/server/art/MergeProfileOptions.aidl
@@ -28,7 +28,7 @@ package com.android.server.art;
* @hide
*/
parcelable MergeProfileOptions {
- /** --force-merge */
+ /** --force-merge-and-analyze */
boolean forceMerge;
/** --boot-image-merge */
boolean forBootImage;
diff --git a/compiler/optimizing/write_barrier_elimination.cc b/compiler/optimizing/write_barrier_elimination.cc
index eb70b670fe..6182125b74 100644
--- a/compiler/optimizing/write_barrier_elimination.cc
+++ b/compiler/optimizing/write_barrier_elimination.cc
@@ -21,6 +21,9 @@
#include "base/scoped_arena_containers.h"
#include "optimizing/nodes.h"
+// TODO(b/310755375, solanes): Disable WBE while we investigate crashes.
+constexpr bool kWBEEnabled = false;
+
namespace art HIDDEN {
class WBEVisitor final : public HGraphVisitor {
@@ -153,8 +156,10 @@ class WBEVisitor final : public HGraphVisitor {
};
bool WriteBarrierElimination::Run() {
- WBEVisitor wbe_visitor(graph_, stats_);
- wbe_visitor.VisitReversePostOrder();
+ if (kWBEEnabled) {
+ WBEVisitor wbe_visitor(graph_, stats_);
+ wbe_visitor.VisitReversePostOrder();
+ }
return true;
}
diff --git a/libartservice/service/api/system-server-current.txt b/libartservice/service/api/system-server-current.txt
index ec1eab2306..a9cd65a44d 100644
--- a/libartservice/service/api/system-server-current.txt
+++ b/libartservice/service/api/system-server-current.txt
@@ -80,7 +80,6 @@ package com.android.server.art.model {
field public static final int FLAG_FOR_PRIMARY_DEX = 1; // 0x1
field public static final int FLAG_FOR_SECONDARY_DEX = 2; // 0x2
field public static final int FLAG_FOR_SINGLE_SPLIT = 32; // 0x20
- field public static final int FLAG_IGNORE_PROFILE = 128; // 0x80
field public static final int FLAG_SHOULD_DOWNGRADE = 8; // 0x8
field public static final int FLAG_SHOULD_INCLUDE_DEPENDENCIES = 4; // 0x4
field public static final int FLAG_SKIP_IF_STORAGE_LOW = 64; // 0x40
@@ -143,9 +142,6 @@ package com.android.server.art.model {
field public static final int DEXOPT_FAILED = 30; // 0x1e
field public static final int DEXOPT_PERFORMED = 20; // 0x14
field public static final int DEXOPT_SKIPPED = 10; // 0xa
- field public static final int EXTENDED_BAD_EXTERNAL_PROFILE = 4; // 0x4
- field public static final int EXTENDED_SKIPPED_NO_DEX_CODE = 2; // 0x2
- field public static final int EXTENDED_SKIPPED_STORAGE_LOW = 1; // 0x1
}
public abstract static class DexoptResult.DexContainerFileDexoptResult {
@@ -154,8 +150,6 @@ package com.android.server.art.model {
method public abstract long getDex2oatCpuTimeMillis();
method public abstract long getDex2oatWallTimeMillis();
method @NonNull public abstract String getDexContainerFile();
- method public abstract int getExtendedStatusFlags();
- method @NonNull public abstract java.util.List<java.lang.String> getExternalProfileErrors();
method public abstract long getSizeBeforeBytes();
method public abstract long getSizeBytes();
method public abstract int getStatus();
diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
index d04962c58f..bbc3f6330f 100644
--- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
+++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
@@ -84,8 +84,10 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@@ -456,7 +458,9 @@ public final class ArtManagerLocal {
* @param reason determines the default list of packages and options
* @param cancellationSignal provides the ability to cancel this operation
* @param processCallbackExecutor the executor to call {@code progressCallback}
- * @param progressCallback called repeatedly whenever there is an update on the progress
+ * @param progressCallbacks a mapping from an integer, in {@link ArtFlags.BatchDexoptPass}, to
+ * the callback that is called repeatedly whenever there is an update on the progress
+ * @return a mapping from an integer, in {@link ArtFlags.BatchDexoptPass}, to the dexopt result.
* @throws IllegalStateException if the operation encounters an error that should never happen
* (e.g., an internal logic error), or the callback set by {@link
* #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)} provides invalid
@@ -466,11 +470,12 @@ public final class ArtManagerLocal {
*/
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@NonNull
- public DexoptResult dexoptPackages(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ public Map<Integer, DexoptResult> dexoptPackages(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot,
@NonNull @BatchDexoptReason String reason,
@NonNull CancellationSignal cancellationSignal,
@Nullable @CallbackExecutor Executor progressCallbackExecutor,
- @Nullable Consumer<OperationProgress> progressCallback) {
+ @Nullable Map<Integer, Consumer<OperationProgress>> progressCallbacks) {
List<String> defaultPackages =
Collections.unmodifiableList(getDefaultPackages(snapshot, reason));
DexoptParams defaultDexoptParams = new DexoptParams.Builder(reason).build();
@@ -488,16 +493,37 @@ public final class ArtManagerLocal {
ExecutorService dexoptExecutor =
Executors.newFixedThreadPool(ReasonMapping.getConcurrencyForReason(reason));
+ Map<Integer, DexoptResult> dexoptResults = new HashMap<>();
try {
if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) {
- maybeDowngradePackages(snapshot,
+ DexoptResult downgradeResult = maybeDowngradePackages(snapshot,
new HashSet<>(params.getPackages()) /* excludedPackages */,
- cancellationSignal, dexoptExecutor);
+ cancellationSignal, dexoptExecutor, progressCallbackExecutor,
+ progressCallbacks != null ? progressCallbacks.get(ArtFlags.PASS_DOWNGRADE)
+ : null);
+ if (downgradeResult != null) {
+ dexoptResults.put(ArtFlags.PASS_DOWNGRADE, downgradeResult);
+ }
+ }
+ Log.i(TAG,
+ "Dexopting " + params.getPackages().size() + " packages with reason=" + reason);
+ DexoptResult mainResult = mInjector.getDexoptHelper().dexopt(snapshot,
+ params.getPackages(), params.getDexoptParams(), cancellationSignal,
+ dexoptExecutor, progressCallbackExecutor,
+ progressCallbacks != null ? progressCallbacks.get(ArtFlags.PASS_MAIN) : null);
+ dexoptResults.put(ArtFlags.PASS_MAIN, mainResult);
+ if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) {
+ DexoptResult supplementaryResult = maybeDexoptPackagesSupplementaryPass(snapshot,
+ mainResult, params.getDexoptParams(), cancellationSignal, dexoptExecutor,
+ progressCallbackExecutor,
+ progressCallbacks != null
+ ? progressCallbacks.get(ArtFlags.PASS_SUPPLEMENTARY)
+ : null);
+ if (supplementaryResult != null) {
+ dexoptResults.put(ArtFlags.PASS_SUPPLEMENTARY, supplementaryResult);
+ }
}
- Log.i(TAG, "Dexopting packages");
- return mInjector.getDexoptHelper().dexopt(snapshot, params.getPackages(),
- params.getDexoptParams(), cancellationSignal, dexoptExecutor,
- progressCallbackExecutor, progressCallback);
+ return dexoptResults;
} finally {
dexoptExecutor.shutdown();
}
@@ -857,7 +883,7 @@ public final class ArtManagerLocal {
@Nullable Consumer<OperationProgress> progressCallback) {
try (var snapshot = mInjector.getPackageManagerLocal().withFilteredSnapshot()) {
dexoptPackages(snapshot, bootReason, new CancellationSignal(), progressCallbackExecutor,
- progressCallback);
+ Map.of(ArtFlags.PASS_MAIN, progressCallback));
}
}
@@ -1009,26 +1035,31 @@ public final class ArtManagerLocal {
}
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- private void maybeDowngradePackages(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @Nullable
+ private DexoptResult maybeDowngradePackages(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot,
@NonNull Set<String> excludedPackages, @NonNull CancellationSignal cancellationSignal,
- @NonNull Executor executor) {
+ @NonNull Executor executor,
+ @Nullable @CallbackExecutor Executor progressCallbackExecutor,
+ @Nullable Consumer<OperationProgress> progressCallback) {
if (shouldDowngrade()) {
List<String> packages = getDefaultPackages(snapshot, ReasonMapping.REASON_INACTIVE)
.stream()
.filter(pkg -> !excludedPackages.contains(pkg))
.collect(Collectors.toList());
if (!packages.isEmpty()) {
- Log.i(TAG, "Storage is low. Downgrading inactive packages");
+ Log.i(TAG, "Storage is low. Downgrading " + packages.size() + " inactive packages");
DexoptParams params =
new DexoptParams.Builder(ReasonMapping.REASON_INACTIVE).build();
- mInjector.getDexoptHelper().dexopt(snapshot, packages, params, cancellationSignal,
- executor, null /* processCallbackExecutor */, null /* progressCallback */);
+ return mInjector.getDexoptHelper().dexopt(snapshot, packages, params,
+ cancellationSignal, executor, progressCallbackExecutor, progressCallback);
} else {
Log.i(TAG,
"Storage is low, but downgrading is disabled or there's nothing to "
+ "downgrade");
}
}
+ return null;
}
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@@ -1042,6 +1073,48 @@ public final class ArtManagerLocal {
}
}
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Nullable
+ private DexoptResult maybeDexoptPackagesSupplementaryPass(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull DexoptResult mainResult, @NonNull DexoptParams mainParams,
+ @NonNull CancellationSignal cancellationSignal, @NonNull Executor dexoptExecutor,
+ @Nullable @CallbackExecutor Executor progressCallbackExecutor,
+ @Nullable Consumer<OperationProgress> progressCallback) {
+ if ((mainParams.getFlags() & ArtFlags.FLAG_FORCE_MERGE_PROFILE) != 0) {
+ return null;
+ }
+
+ // Only pick packages that used a profile-guided filter and were skipped in the main pass.
+ // This is a very coarse filter to reduce unnecessary iterations on a best-effort basis.
+ // Packages included in the list may still be skipped by dexopter if the profiles don't have
+ // any change.
+ List<String> packageNames =
+ mainResult.getPackageDexoptResults()
+ .stream()
+ .filter(packageResult
+ -> packageResult.getDexContainerFileDexoptResults()
+ .stream()
+ .anyMatch(fileResult
+ -> DexFile.isProfileGuidedCompilerFilter(
+ fileResult.getActualCompilerFilter())
+ && fileResult.getStatus()
+ == DexoptResult.DEXOPT_SKIPPED))
+ .map(packageResult -> packageResult.getPackageName())
+ .collect(Collectors.toList());
+
+ DexoptParams dexoptParams = mainParams.toBuilder()
+ .setFlags(ArtFlags.FLAG_FORCE_MERGE_PROFILE,
+ ArtFlags.FLAG_FORCE_MERGE_PROFILE)
+ .build();
+
+ Log.i(TAG,
+ "Dexopting " + packageNames.size() + " packages with reason="
+ + dexoptParams.getReason() + " (supplementary pass)");
+ return mInjector.getDexoptHelper().dexopt(snapshot, packageNames, dexoptParams,
+ cancellationSignal, dexoptExecutor, progressCallbackExecutor, progressCallback);
+ }
+
/** Returns the list of packages to process for the given reason. */
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@NonNull
diff --git a/libartservice/service/java/com/android/server/art/ArtShellCommand.java b/libartservice/service/java/com/android/server/art/ArtShellCommand.java
index b83f4ab25d..f9e610c124 100644
--- a/libartservice/service/java/com/android/server/art/ArtShellCommand.java
+++ b/libartservice/service/java/com/android/server/art/ArtShellCommand.java
@@ -20,6 +20,8 @@ import static android.os.ParcelFileDescriptor.AutoCloseInputStream;
import static com.android.server.art.ArtManagerLocal.SnapshotProfileException;
import static com.android.server.art.PrimaryDexUtils.PrimaryDexInfo;
+import static com.android.server.art.ReasonMapping.BatchDexoptReason;
+import static com.android.server.art.model.ArtFlags.BatchDexoptPass;
import static com.android.server.art.model.ArtFlags.DexoptFlags;
import static com.android.server.art.model.ArtFlags.PriorityClassApi;
import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
@@ -66,11 +68,14 @@ import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -195,6 +200,7 @@ public final class ArtShellCommand extends BasicShellCommandHandler {
boolean forAllPackages = false;
boolean legacyClearProfile = false;
boolean verbose = false;
+ boolean forceMergeProfile = false;
String opt;
while ((opt = getNextOption()) != null) {
@@ -259,6 +265,9 @@ public final class ArtShellCommand extends BasicShellCommandHandler {
case "-v":
verbose = true;
break;
+ case "--force-merge-profile":
+ forceMergeProfile = true;
+ break;
default:
pw.println("Error: Unknown option: " + opt);
return 1;
@@ -290,6 +299,10 @@ public final class ArtShellCommand extends BasicShellCommandHandler {
if (force) {
paramsBuilder.setFlags(ArtFlags.FLAG_FORCE, ArtFlags.FLAG_FORCE);
}
+ if (forceMergeProfile) {
+ paramsBuilder.setFlags(
+ ArtFlags.FLAG_FORCE_MERGE_PROFILE, ArtFlags.FLAG_FORCE_MERGE_PROFILE);
+ }
if (splitArg != null) {
if (scopeFlags != 0) {
pw.println("Error: '--primary-dex', '--secondary-dex', "
@@ -382,8 +395,7 @@ public final class ArtShellCommand extends BasicShellCommandHandler {
BackgroundDexoptJob.Result result = Utils.getFuture(future);
if (result instanceof BackgroundDexoptJob.CompletedResult) {
var completedResult = (BackgroundDexoptJob.CompletedResult) result;
- if (completedResult.dexoptResult().getFinalStatus()
- == DexoptResult.DEXOPT_CANCELLED) {
+ if (completedResult.isCancelled()) {
pw.println("Job cancelled. See logs for details");
} else {
pw.println("Job finished. See logs for details");
@@ -584,20 +596,41 @@ public final class ArtShellCommand extends BasicShellCommandHandler {
ReasonMapping.BATCH_DEXOPT_REASONS);
return 1;
}
- DexoptResult result;
+
+ final String finalReason = reason;
+
+ // Create callbacks to print the progress.
+ Map<Integer, Consumer<OperationProgress>> progressCallbacks = new HashMap<>();
+ for (@BatchDexoptPass int pass : ArtFlags.BATCH_DEXOPT_PASSES) {
+ progressCallbacks.put(pass, progress -> {
+ pw.println(String.format(Locale.US, "%s: %d%%",
+ getProgressMessageForBatchDexoptPass(pass, finalReason),
+ progress.getPercentage()));
+ pw.flush();
+ });
+ }
+
ExecutorService progressCallbackExecutor = Executors.newSingleThreadExecutor();
try (var signal = new WithCancellationSignal(pw, true /* verbose */)) {
- result = mArtManagerLocal.dexoptPackages(
- snapshot, reason, signal.get(), progressCallbackExecutor, progress -> {
- pw.println(String.format("Dexopting apps: %d%%", progress.getPercentage()));
- pw.flush();
- });
+ Map<Integer, DexoptResult> results = mArtManagerLocal.dexoptPackages(snapshot,
+ finalReason, signal.get(), progressCallbackExecutor, progressCallbacks);
+
Utils.executeAndWait(progressCallbackExecutor, () -> {
- printDexoptResult(pw, result, true /* verbose */, true /* multiPackage */);
+ for (@BatchDexoptPass int pass : ArtFlags.BATCH_DEXOPT_PASSES) {
+ if (results.containsKey(pass)) {
+ pw.println("Result of "
+ + getProgressMessageForBatchDexoptPass(pass, finalReason)
+ .toLowerCase(Locale.US)
+ + ":");
+ printDexoptResult(
+ pw, results.get(pass), true /* verbose */, true /* multiPackage */);
+ }
+ }
});
} finally {
progressCallbackExecutor.shutdown();
}
+
return 0;
}
@@ -642,6 +675,8 @@ public final class ArtShellCommand extends BasicShellCommandHandler {
pw.println(" For secondary dex files, it also clears all dexopt artifacts.");
pw.println(" When this flag is set, all the other flags are ignored.");
pw.println(" -v Verbose mode. This mode prints detailed results.");
+ pw.println(" --force-merge-profile Force merge profiles even if the difference between");
+ pw.println(" before and after the merge is not significant.");
pw.println(" Scope options:");
pw.println(" --primary-dex Dexopt primary dex files only (all APKs that are installed");
pw.println(" as part of the package, including the base APK and all other split");
@@ -906,6 +941,21 @@ public final class ArtShellCommand extends BasicShellCommandHandler {
}
}
+ @NonNull
+ private String getProgressMessageForBatchDexoptPass(
+ @BatchDexoptPass int pass, @NonNull @BatchDexoptReason String reason) {
+ switch (pass) {
+ case ArtFlags.PASS_DOWNGRADE:
+ return "Downgrading apps";
+ case ArtFlags.PASS_MAIN:
+ return reason.equals(ReasonMapping.REASON_BG_DEXOPT) ? "Dexopting apps (main pass)"
+ : "Dexopting apps";
+ case ArtFlags.PASS_SUPPLEMENTARY:
+ return "Dexopting apps (supplementary pass)";
+ }
+ throw new IllegalArgumentException("Unknown batch dexopt pass " + pass);
+ }
+
private static class WithCancellationSignal implements AutoCloseable {
@NonNull private final CancellationSignal mSignal = new CancellationSignal();
@NonNull private final String mJobId;
diff --git a/libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java b/libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java
index 9944e3fb1f..46e3a9f77c 100644
--- a/libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java
+++ b/libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java
@@ -17,6 +17,7 @@
package com.android.server.art;
import static com.android.server.art.ArtManagerLocal.ScheduleBackgroundDexoptJobCallback;
+import static com.android.server.art.model.ArtFlags.BatchDexoptPass;
import static com.android.server.art.model.ArtFlags.ScheduleStatus;
import static com.android.server.art.model.Config.Callback;
@@ -32,6 +33,7 @@ import android.os.CancellationSignal;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Log;
+import android.util.Slog;
import androidx.annotation.RequiresApi;
@@ -41,14 +43,19 @@ import com.android.server.LocalManagerRegistry;
import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.Config;
import com.android.server.art.model.DexoptResult;
+import com.android.server.art.model.OperationProgress;
import com.android.server.pm.PackageManagerLocal;
import com.google.auto.value.AutoValue;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/** @hide */
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@@ -85,15 +92,20 @@ public class BackgroundDexoptJob {
public boolean onStartJob(
@NonNull BackgroundDexoptJobService jobService, @NonNull JobParameters params) {
start().thenAcceptAsync(result -> {
- writeStats(result);
+ try {
+ writeStats(result);
+ } catch (RuntimeException e) {
+ // Not expected. Log wtf to surface it.
+ Slog.wtf(TAG, "Failed to write stats", e);
+ }
+
// This is a periodic job, where the interval is specified in the `JobInfo`. "true"
// means to execute again during a future idle maintenance window in the same
// interval, while "false" means not to execute again during a future idle maintenance
// window in the same interval but to execute again in the next interval.
// This call will be ignored if `onStopJob` is called.
- boolean wantsReschedule = result instanceof CompletedResult
- && ((CompletedResult) result).dexoptResult().getFinalStatus()
- == DexoptResult.DEXOPT_CANCELLED;
+ boolean wantsReschedule =
+ result instanceof CompletedResult && ((CompletedResult) result).isCancelled();
jobService.jobFinished(params, wantsReschedule);
});
// "true" means the job will continue running until `jobFinished` is called.
@@ -202,12 +214,28 @@ public class BackgroundDexoptJob {
@NonNull
private CompletedResult run(@NonNull CancellationSignal cancellationSignal) {
- long startTimeMs = SystemClock.uptimeMillis();
- DexoptResult dexoptResult;
+ // Create callbacks to time each pass.
+ Map<Integer, Long> startTimeMsByPass = new HashMap<>();
+ Map<Integer, Long> durationMsByPass = new HashMap<>();
+ Map<Integer, Consumer<OperationProgress>> progressCallbacks = new HashMap<>();
+ for (@BatchDexoptPass int pass : ArtFlags.BATCH_DEXOPT_PASSES) {
+ progressCallbacks.put(pass, progress -> {
+ if (progress.getTotal() == 0) {
+ durationMsByPass.put(pass, 0l);
+ } else if (progress.getCurrent() == 0) {
+ startTimeMsByPass.put(pass, SystemClock.uptimeMillis());
+ } else if (progress.getCurrent() == progress.getTotal()) {
+ durationMsByPass.put(
+ pass, SystemClock.uptimeMillis() - startTimeMsByPass.get(pass));
+ }
+ });
+ }
+
+ Map<Integer, DexoptResult> dexoptResultByPass;
try (var snapshot = mInjector.getPackageManagerLocal().withFilteredSnapshot()) {
- dexoptResult = mInjector.getArtManagerLocal().dexoptPackages(snapshot,
- ReasonMapping.REASON_BG_DEXOPT, cancellationSignal,
- null /* processCallbackExecutor */, null /* processCallback */);
+ dexoptResultByPass = mInjector.getArtManagerLocal().dexoptPackages(snapshot,
+ ReasonMapping.REASON_BG_DEXOPT, cancellationSignal, Runnable::run,
+ progressCallbacks);
// For simplicity, we don't support cancelling the following operation in the middle.
// This is fine because it typically takes only a few seconds.
@@ -220,7 +248,7 @@ public class BackgroundDexoptJob {
Log.i(TAG, String.format("Freed %d bytes", freedBytes));
}
}
- return CompletedResult.create(dexoptResult, SystemClock.uptimeMillis() - startTimeMs);
+ return CompletedResult.create(dexoptResultByPass, durationMsByPass);
}
private void writeStats(@NonNull Result result) {
@@ -239,13 +267,22 @@ public class BackgroundDexoptJob {
static class FatalErrorResult extends Result {}
@AutoValue
+ @SuppressWarnings("AutoValueImmutableFields") // Can't use ImmutableMap because it's in Guava.
static abstract class CompletedResult extends Result {
- abstract @NonNull DexoptResult dexoptResult();
- abstract long durationMs();
+ abstract @NonNull Map<Integer, DexoptResult> dexoptResultByPass();
+ abstract @NonNull Map<Integer, Long> durationMsByPass();
@NonNull
- static CompletedResult create(@NonNull DexoptResult dexoptResult, long durationMs) {
- return new AutoValue_BackgroundDexoptJob_CompletedResult(dexoptResult, durationMs);
+ static CompletedResult create(@NonNull Map<Integer, DexoptResult> dexoptResultByPass,
+ @NonNull Map<Integer, Long> durationMsByPass) {
+ return new AutoValue_BackgroundDexoptJob_CompletedResult(
+ Collections.unmodifiableMap(dexoptResultByPass),
+ Collections.unmodifiableMap(durationMsByPass));
+ }
+
+ public boolean isCancelled() {
+ return dexoptResultByPass().values().stream().anyMatch(
+ result -> result.getFinalStatus() == DexoptResult.DEXOPT_CANCELLED);
}
}
diff --git a/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java b/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java
index c6cc73af19..dc98914292 100644
--- a/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java
+++ b/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java
@@ -1,11 +1,14 @@
package com.android.server.art;
+import static com.android.server.art.model.ArtFlags.BatchDexoptPass;
+
import android.annotation.NonNull;
import android.app.job.JobParameters;
import android.os.Build;
import androidx.annotation.RequiresApi;
+import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.DexoptResult;
import dalvik.system.DexFile;
@@ -22,31 +25,48 @@ import java.util.stream.Collectors;
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public class BackgroundDexoptJobStatsReporter {
public static void reportFailure() {
+ // The fatal error can occur during any pass, but we attribute it to the main pass for
+ // simplicity.
ArtStatsLog.write(ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED,
ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_FATAL_ERROR,
JobParameters.STOP_REASON_UNDEFINED, 0 /* durationMs */, 0 /* deprecated */,
0 /* optimizedPackagesCount */, 0 /* packagesDependingOnBootClasspathCount */,
- 0 /* totalPackagesCount */);
+ 0 /* totalPackagesCount */,
+ ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__PASS__PASS_MAIN);
}
public static void reportSuccess(@NonNull BackgroundDexoptJob.CompletedResult completedResult,
Optional<Integer> stopReason) {
+ for (var entry : completedResult.dexoptResultByPass().entrySet()) {
+ reportPass(entry.getKey(), entry.getValue(),
+ completedResult.durationMsByPass().getOrDefault(entry.getKey(), 0l),
+ stopReason);
+ }
+ }
+
+ public static void reportPass(@BatchDexoptPass int pass, @NonNull DexoptResult dexoptResult,
+ long durationMs, Optional<Integer> stopReason) {
+ // The job contains multiple passes, so the stop reason may not be for the current pass. We
+ // shouldn't report the stop reason if the current pass finished before the job was
+ // cancelled.
+ int reportedStopReason = dexoptResult.getFinalStatus() == DexoptResult.DEXOPT_CANCELLED
+ ? stopReason.orElse(JobParameters.STOP_REASON_UNDEFINED)
+ : JobParameters.STOP_REASON_UNDEFINED;
+
List<DexoptResult.PackageDexoptResult> packageDexoptResults =
- getFilteredPackageResults(completedResult);
+ getFilteredPackageResults(dexoptResult);
+
ArtStatsLog.write(ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED,
- getStatusForStats(completedResult, stopReason),
- stopReason.orElse(JobParameters.STOP_REASON_UNDEFINED),
- completedResult.durationMs(), 0 /* deprecated */,
- getDexoptedPackagesCount(packageDexoptResults),
+ getStatusForStats(dexoptResult, stopReason), reportedStopReason, durationMs,
+ 0 /* deprecated */, getDexoptedPackagesCount(packageDexoptResults),
getPackagesDependingOnBootClasspathCount(packageDexoptResults),
- packageDexoptResults.size());
+ packageDexoptResults.size(), toStatsdPassEnum(pass));
}
@NonNull
private static List<DexoptResult.PackageDexoptResult> getFilteredPackageResults(
- @NonNull BackgroundDexoptJob.CompletedResult completedResult) {
- return completedResult.dexoptResult()
- .getPackageDexoptResults()
+ @NonNull DexoptResult dexoptResult) {
+ return dexoptResult.getPackageDexoptResults()
.stream()
.filter(packageResult
-> packageResult.getDexContainerFileDexoptResults().stream().anyMatch(
@@ -58,8 +78,8 @@ public class BackgroundDexoptJobStatsReporter {
}
private static int getStatusForStats(
- @NonNull BackgroundDexoptJob.CompletedResult result, Optional<Integer> stopReason) {
- if (result.dexoptResult().getFinalStatus() == DexoptResult.DEXOPT_CANCELLED) {
+ @NonNull DexoptResult dexoptResult, Optional<Integer> stopReason) {
+ if (dexoptResult.getFinalStatus() == DexoptResult.DEXOPT_CANCELLED) {
if (stopReason.isPresent()) {
return ArtStatsLog
.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_ABORT_BY_CANCELLATION;
@@ -69,8 +89,7 @@ public class BackgroundDexoptJobStatsReporter {
}
boolean isSkippedDueToStorageLow =
- result.dexoptResult()
- .getPackageDexoptResults()
+ dexoptResult.getPackageDexoptResults()
.stream()
.flatMap(packageResult
-> packageResult.getDexContainerFileDexoptResults().stream())
@@ -106,4 +125,16 @@ public class BackgroundDexoptJobStatsReporter {
.map(DexoptResult.DexContainerFileDexoptResult::getActualCompilerFilter)
.anyMatch(DexFile::isOptimizedCompilerFilter);
}
+
+ private static int toStatsdPassEnum(@BatchDexoptPass int pass) {
+ switch (pass) {
+ case ArtFlags.PASS_DOWNGRADE:
+ return ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__PASS__PASS_DOWNGRADE;
+ case ArtFlags.PASS_MAIN:
+ return ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__PASS__PASS_MAIN;
+ case ArtFlags.PASS_SUPPLEMENTARY:
+ return ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__PASS__PASS_SUPPLEMENTARY;
+ }
+ throw new IllegalArgumentException("Unknown batch dexopt pass " + pass);
+ }
}
diff --git a/libartservice/service/java/com/android/server/art/Dexopter.java b/libartservice/service/java/com/android/server/art/Dexopter.java
index 5420c44689..2f15166ce7 100644
--- a/libartservice/service/java/com/android/server/art/Dexopter.java
+++ b/libartservice/service/java/com/android/server/art/Dexopter.java
@@ -541,9 +541,12 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> {
@Nullable ProfilePath referenceProfile) throws RemoteException {
OutputProfile output = buildOutputProfile(dexInfo, false /* isPublic */);
+ var options = new MergeProfileOptions();
+ options.forceMerge = (mParams.getFlags() & ArtFlags.FLAG_FORCE_MERGE_PROFILE) != 0;
+
try {
if (mInjector.getArtd().mergeProfiles(getCurProfiles(dexInfo), referenceProfile, output,
- List.of(dexInfo.dexPath()), new MergeProfileOptions())) {
+ List.of(dexInfo.dexPath()), options)) {
return ProfilePath.tmpProfilePath(output.profilePath);
}
} catch (ServiceSpecificException e) {
diff --git a/libartservice/service/java/com/android/server/art/model/ArtFlags.java b/libartservice/service/java/com/android/server/art/model/ArtFlags.java
index a310f2c1e4..6de14f0f11 100644
--- a/libartservice/service/java/com/android/server/art/model/ArtFlags.java
+++ b/libartservice/service/java/com/android/server/art/model/ArtFlags.java
@@ -29,6 +29,7 @@ import com.android.server.pm.PackageManagerLocal;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
/** @hide */
@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
@@ -84,9 +85,18 @@ public class ArtFlags {
* one, such as "speed-profile", it will be adjusted to "verify". This option is especially
* useful when the compiler filter is not explicitly specified (i.e., is inferred from the
* compilation reason).
+ *
+ * @hide
*/
@SuppressLint("UnflaggedApi") // Flag support for mainline is not available.
public static final int FLAG_IGNORE_PROFILE = 1 << 7;
+ /**
+ * Whether to force merge profiles even if the difference between before and after the merge
+ * is not significant.
+ *
+ * @hide
+ */
+ public static final int FLAG_FORCE_MERGE_PROFILE = 1 << 8;
/**
* Flags for {@link
@@ -128,6 +138,7 @@ public class ArtFlags {
FLAG_FOR_SINGLE_SPLIT,
FLAG_SKIP_IF_STORAGE_LOW,
FLAG_IGNORE_PROFILE,
+ FLAG_FORCE_MERGE_PROFILE,
})
// clang-format on
@Retention(RetentionPolicy.SOURCE)
@@ -228,5 +239,51 @@ public class ArtFlags {
@Retention(RetentionPolicy.SOURCE)
public @interface ScheduleStatus {}
+ /**
+ * The downgrade pass, run before the main pass. Only applicable to bg-dexopt.
+ *
+ * @hide
+ */
+ public static final int PASS_DOWNGRADE = 0;
+
+ /**
+ * The main pass.
+ *
+ * @hide
+ */
+ public static final int PASS_MAIN = 1;
+
+ /**
+ * The supplementary pass, run after the main pass, to take the opportunity to dexopt more
+ * packages. Compared to the main pass, it uses different criteria to determine whether dexopt
+ * is needed or not, but iterates over the same packages in the same order as the main pass (so
+ * the logic in {@link ArtManagerLocal#getDefaultPackages} and {@link
+ * ArtManagerLocal.BatchDexoptStartCallback} controls the packages here too.)
+ *
+ * Only applicable to bg-dexopt.
+ *
+ * @hide
+ */
+ public static final int PASS_SUPPLEMENTARY = 2;
+
+ /**
+ * Indicates the pass of a batch dexopt run.
+ *
+ * @hide
+ */
+ // clang-format off
+ @IntDef(prefix = "PASS_", value = {
+ PASS_DOWNGRADE,
+ PASS_MAIN,
+ PASS_SUPPLEMENTARY,
+ })
+ // clang-format on
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BatchDexoptPass {}
+
+ /** @hide */
+ public static final List<Integer> BATCH_DEXOPT_PASSES =
+ List.of(PASS_DOWNGRADE, PASS_MAIN, PASS_SUPPLEMENTARY);
+
private ArtFlags() {}
}
diff --git a/libartservice/service/java/com/android/server/art/model/DexoptParams.java b/libartservice/service/java/com/android/server/art/model/DexoptParams.java
index 45616d536d..455d4cdead 100644
--- a/libartservice/service/java/com/android/server/art/model/DexoptParams.java
+++ b/libartservice/service/java/com/android/server/art/model/DexoptParams.java
@@ -223,4 +223,12 @@ public class DexoptParams {
public @Nullable String getSplitName() {
return mSplitName;
}
+
+ /** @hide */
+ public @NonNull Builder toBuilder() {
+ return new Builder(mReason, mFlags)
+ .setCompilerFilter(mCompilerFilter)
+ .setPriorityClass(mPriorityClass)
+ .setSplitName(mSplitName);
+ }
}
diff --git a/libartservice/service/java/com/android/server/art/model/DexoptResult.java b/libartservice/service/java/com/android/server/art/model/DexoptResult.java
index ff3399e5bb..64cdb1ca78 100644
--- a/libartservice/service/java/com/android/server/art/model/DexoptResult.java
+++ b/libartservice/service/java/com/android/server/art/model/DexoptResult.java
@@ -63,7 +63,11 @@ public abstract class DexoptResult {
public @interface DexoptResultStatus {}
// Possible values of {@link #DexoptResultExtendedStatusFlags}.
- /** Dexopt is skipped because the remaining storage space is low. */
+ /**
+ * Dexopt is skipped because the remaining storage space is low.
+ *
+ * @hide
+ */
public static final int EXTENDED_SKIPPED_STORAGE_LOW = 1 << 0;
/**
* Dexopt is skipped because the dex container file has no dex code while the manifest declares
@@ -72,6 +76,8 @@ public abstract class DexoptResult {
* Note that this flag doesn't apply to dex container files that are not declared to have code.
* Instead, those files are not listed in {@link
* PackageDexoptResult#getDexContainerFileDexoptResults} in the first place.
+ *
+ * @hide
*/
public static final int EXTENDED_SKIPPED_NO_DEX_CODE = 1 << 1;
/**
@@ -82,6 +88,8 @@ public abstract class DexoptResult {
*
* This is not a critical error. Dexopt may still have succeeded after ignoring the bad external
* profiles.
+ *
+ * @hide
*/
public static final int EXTENDED_BAD_EXTERNAL_PROFILE = 1 << 2;
@@ -105,6 +113,13 @@ public abstract class DexoptResult {
return new AutoValue_DexoptResult(requestedCompilerFilter, reason, packageDexoptResult);
}
+ /** @hide */
+ @VisibleForTesting
+ public static @NonNull DexoptResult create() {
+ return new AutoValue_DexoptResult(
+ "compiler-filter", "reason", List.of() /* packageDexoptResult */);
+ }
+
/**
* The requested compiler filter. Note that the compiler filter might be adjusted before the
* execution based on factors like dexopt flags, whether the profile is available, or whether
@@ -324,6 +339,8 @@ public abstract class DexoptResult {
* skipped. Note that they don't cover all possible reasons. At most one `EXTENDED_SKIPPED_`
* flag will be set, even if the situation meets multiple `EXTENDED_SKIPPED_` flags. The
* order of precedence of those flags is undefined.
+ *
+ * @hide
*/
public abstract @DexoptResultExtendedStatusFlags int getExtendedStatusFlags();
@@ -339,6 +356,8 @@ public abstract class DexoptResult {
* that caused the errors.
*
* @see #EXTENDED_BAD_EXTERNAL_PROFILE.
+ *
+ * @hide
*/
public abstract @NonNull List<String> getExternalProfileErrors();
diff --git a/libartservice/service/java/com/android/server/art/model/OperationProgress.java b/libartservice/service/java/com/android/server/art/model/OperationProgress.java
index a47a556bdc..a0a7ddc4fc 100644
--- a/libartservice/service/java/com/android/server/art/model/OperationProgress.java
+++ b/libartservice/service/java/com/android/server/art/model/OperationProgress.java
@@ -38,7 +38,7 @@ public abstract class OperationProgress {
/** The overall progress, in the range of [0, 100]. */
public int getPercentage() {
- return 100 * getCurrent() / getTotal();
+ return getTotal() == 0 ? 100 : 100 * getCurrent() / getTotal();
}
/**
diff --git a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
index 5a153e4564..6b46318718 100644
--- a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
+++ b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
@@ -19,6 +19,8 @@ package com.android.server.art;
import static android.os.ParcelFileDescriptor.AutoCloseInputStream;
import static com.android.server.art.DexUseManagerLocal.DetailedSecondaryDexInfo;
+import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
+import static com.android.server.art.model.DexoptResult.PackageDexoptResult;
import static com.android.server.art.model.DexoptStatus.DexContainerFileDexoptStatus;
import static com.android.server.art.testing.TestingUtils.deepEq;
import static com.android.server.art.testing.TestingUtils.inAnyOrder;
@@ -58,6 +60,7 @@ import android.os.storage.StorageManager;
import androidx.test.filters.SmallTest;
import com.android.modules.utils.pm.PackageStateModulesUtils;
+import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.Config;
import com.android.server.art.model.DeleteResult;
import com.android.server.art.model.DexoptParams;
@@ -408,7 +411,7 @@ public class ArtManagerLocalTest {
@Test
public void testDexoptPackage() throws Exception {
var params = new DexoptParams.Builder("install").build();
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
when(mDexoptHelper.dexopt(any(), deepEq(List.of(PKG_NAME_1)), same(params),
@@ -422,7 +425,7 @@ public class ArtManagerLocalTest {
@Test
public void testResetDexoptStatus() throws Exception {
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
when(mDexoptHelper.dexopt(
@@ -459,7 +462,7 @@ public class ArtManagerLocalTest {
@Test
public void testDexoptPackages() throws Exception {
- var dexoptResult = mock(DexoptResult.class);
+ var dexoptResult = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_2)).thenReturn(CURRENT_TIME_MS);
simulateStorageLow();
@@ -474,7 +477,7 @@ public class ArtManagerLocalTest {
assertThat(mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
null /* processCallbackExecutor */, null /* processCallback */))
- .isSameInstanceAs(dexoptResult);
+ .isEqualTo(Map.of(ArtFlags.PASS_MAIN, dexoptResult));
// Nothing to downgrade.
verify(mDexoptHelper, never())
@@ -490,7 +493,7 @@ public class ArtManagerLocalTest {
when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1)).thenReturn(0l);
simulateStorageLow();
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
// PKG_NAME_1 should be dexopted.
@@ -517,25 +520,28 @@ public class ArtManagerLocalTest {
when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1)).thenReturn(NOT_RECENT_TIME_MS);
simulateStorageLow();
- var result = mock(DexoptResult.class);
+ var mainResult = DexoptResult.create();
+ var downgradeResult = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
// PKG_NAME_1 should not be dexopted.
- doReturn(result)
+ doReturn(mainResult)
.when(mDexoptHelper)
.dexopt(any(), deepEq(List.of(PKG_NAME_2)),
argThat(params -> params.getReason().equals("bg-dexopt")), any(), any(),
any(), any());
// PKG_NAME_1 should be downgraded.
- doReturn(result)
+ doReturn(downgradeResult)
.when(mDexoptHelper)
.dexopt(any(), deepEq(List.of(PKG_NAME_1)),
argThat(params -> params.getReason().equals("inactive")), any(), any(),
any(), any());
- mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
- null /* processCallbackExecutor */, null /* processCallback */);
+ assertThat(mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
+ null /* processCallbackExecutor */, null /* processCallback */))
+ .isEqualTo(Map.of(
+ ArtFlags.PASS_DOWNGRADE, downgradeResult, ArtFlags.PASS_MAIN, mainResult));
}
@Test
@@ -545,7 +551,7 @@ public class ArtManagerLocalTest {
when(userState.getFirstInstallTimeMillis()).thenReturn(NOT_RECENT_TIME_MS);
when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1)).thenReturn(NOT_RECENT_TIME_MS);
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
// PKG_NAME_1 should not be dexopted.
@@ -566,7 +572,7 @@ public class ArtManagerLocalTest {
@Test
public void testDexoptPackagesBootAfterMainlineUpdate() throws Exception {
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
lenient().when(mInjector.isSystemUiPackage(PKG_NAME_1)).thenReturn(true);
@@ -583,7 +589,7 @@ public class ArtManagerLocalTest {
@Test
public void testDexoptPackagesBootAfterMainlineUpdatePackagesNotFound() throws Exception {
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
// PKG_NAME_1 is neither recently installed nor recently used.
PackageUserState userState = mPkgState1.getStateForUser(UserHandle.of(1));
@@ -615,7 +621,7 @@ public class ArtManagerLocalTest {
simulateStorageLow();
var params = new DexoptParams.Builder("bg-dexopt").build();
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
mArtManagerLocal.setBatchDexoptStartCallback(ForkJoinPool.commonPool(),
@@ -645,7 +651,7 @@ public class ArtManagerLocalTest {
@Test
public void testDexoptPackagesOverrideCleared() throws Exception {
var params = new DexoptParams.Builder("bg-dexopt").build();
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
mArtManagerLocal.setBatchDexoptStartCallback(ForkJoinPool.commonPool(),
@@ -661,7 +667,56 @@ public class ArtManagerLocalTest {
assertThat(mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
null /* processCallbackExecutor */, null /* processCallback */))
- .isSameInstanceAs(result);
+ .isEqualTo(Map.of(ArtFlags.PASS_MAIN, result));
+ }
+
+ @Test
+ public void testDexoptPackagesSupplementaryPass() throws Exception {
+ // The supplementary pass should only try dexopting PKG_NAME_2.
+ var mainResult = DexoptResult.create("speed-profile", "bg-dexopt",
+ List.of(PackageDexoptResult.create(PKG_NAME_1,
+ List.of(DexContainerFileDexoptResult.create("dex-file-1",
+ true /* isPrimaryAbi */, "arm64", "speed-profile",
+ DexoptResult.DEXOPT_PERFORMED),
+ DexContainerFileDexoptResult.create("dex-file-2",
+ true /* isPrimaryAbi */, "arm64", "speed",
+ DexoptResult.DEXOPT_SKIPPED)),
+ null /* packageLevelStatus */),
+ PackageDexoptResult.create(PKG_NAME_2,
+ List.of(DexContainerFileDexoptResult.create("dex-file-1",
+ true /* isPrimaryAbi */, "arm64", "speed-profile",
+ DexoptResult.DEXOPT_PERFORMED),
+ DexContainerFileDexoptResult.create("dex-file-2",
+ true /* isPrimaryAbi */, "arm64", "speed-profile",
+ DexoptResult.DEXOPT_SKIPPED)),
+ null /* packageLevelStatus */)));
+ var supplementaryResult = DexoptResult.create();
+ var cancellationSignal = new CancellationSignal();
+
+ // The main pass.
+ doReturn(mainResult)
+ .when(mDexoptHelper)
+ .dexopt(any(), inAnyOrder(PKG_NAME_1, PKG_NAME_2),
+ argThat(params
+ -> params.getReason().equals("bg-dexopt")
+ && (params.getFlags() & ArtFlags.FLAG_FORCE_MERGE_PROFILE)
+ == 0),
+ any(), any(), any(), any());
+
+ // The supplementary pass.
+ doReturn(supplementaryResult)
+ .when(mDexoptHelper)
+ .dexopt(any(), inAnyOrder(PKG_NAME_2),
+ argThat(params
+ -> params.getReason().equals("bg-dexopt")
+ && (params.getFlags() & ArtFlags.FLAG_FORCE_MERGE_PROFILE)
+ != 0),
+ any(), any(), any(), any());
+
+ assertThat(mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
+ null /* processCallbackExecutor */, null /* processCallback */))
+ .isEqualTo(Map.of(ArtFlags.PASS_MAIN, mainResult, ArtFlags.PASS_SUPPLEMENTARY,
+ supplementaryResult));
}
@Test(expected = IllegalStateException.class)
diff --git a/libartservice/service/javatests/com/android/server/art/BackgroundDexoptJobTest.java b/libartservice/service/javatests/com/android/server/art/BackgroundDexoptJobTest.java
index 3528caf3c3..c61461d0c9 100644
--- a/libartservice/service/javatests/com/android/server/art/BackgroundDexoptJobTest.java
+++ b/libartservice/service/javatests/com/android/server/art/BackgroundDexoptJobTest.java
@@ -17,6 +17,8 @@
package com.android.server.art;
import static com.android.server.art.model.Config.Callback;
+import static com.android.server.art.model.DexoptResult.DexoptResultStatus;
+import static com.android.server.art.model.DexoptResult.PackageDexoptResult;
import static com.google.common.truth.Truth.assertThat;
@@ -25,6 +27,7 @@ import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.times;
@@ -56,6 +59,9 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@@ -74,12 +80,12 @@ public class BackgroundDexoptJobTest {
@Mock private PackageManagerLocal mPackageManagerLocal;
@Mock private PackageManagerLocal.FilteredSnapshot mSnapshot;
@Mock private JobScheduler mJobScheduler;
- @Mock private DexoptResult mDexoptResult;
@Mock private BackgroundDexoptJobService mJobService;
@Mock private JobParameters mJobParameters;
private Config mConfig;
private BackgroundDexoptJob mBackgroundDexoptJob;
private Semaphore mJobFinishedCalled = new Semaphore(0);
+ private Map<Integer, DexoptResult> mDexoptResultByPass;
@Before
public void setUp() throws Exception {
@@ -110,17 +116,19 @@ public class BackgroundDexoptJobTest {
lenient()
.when(mJobParameters.getStopReason())
.thenReturn(JobParameters.STOP_REASON_UNDEFINED);
+
+ mDexoptResultByPass = new HashMap<>();
}
@Test
public void testStart() {
when(mArtManagerLocal.dexoptPackages(
same(mSnapshot), eq(ReasonMapping.REASON_BG_DEXOPT), any(), any(), any()))
- .thenReturn(mDexoptResult);
+ .thenReturn(mDexoptResultByPass);
Result result = Utils.getFuture(mBackgroundDexoptJob.start());
assertThat(result).isInstanceOf(CompletedResult.class);
- assertThat(((CompletedResult) result).dexoptResult()).isSameInstanceAs(mDexoptResult);
+ assertThat(((CompletedResult) result).dexoptResultByPass()).isEqualTo(mDexoptResultByPass);
verify(mArtManagerLocal).cleanup(same(mSnapshot));
}
@@ -131,7 +139,7 @@ public class BackgroundDexoptJobTest {
when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
.thenAnswer(invocation -> {
assertThat(dexoptDone.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue();
- return mDexoptResult;
+ return mDexoptResultByPass;
});
Future<Result> future1 = mBackgroundDexoptJob.start();
@@ -147,7 +155,7 @@ public class BackgroundDexoptJobTest {
@Test
public void testStartAnother() {
when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
- .thenReturn(mDexoptResult);
+ .thenReturn(mDexoptResultByPass);
Future<Result> future1 = mBackgroundDexoptJob.start();
Utils.getFuture(future1);
@@ -172,7 +180,7 @@ public class BackgroundDexoptJobTest {
.thenReturn(true);
when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
- .thenReturn(mDexoptResult);
+ .thenReturn(mDexoptResultByPass);
// The `start` method should ignore the system property. The system property is for
// `schedule`.
@@ -187,7 +195,7 @@ public class BackgroundDexoptJobTest {
assertThat(dexoptCancelled.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue();
var cancellationSignal = invocation.<CancellationSignal>getArgument(2);
assertThat(cancellationSignal.isCanceled()).isTrue();
- return mDexoptResult;
+ return mDexoptResultByPass;
});
Future<Result> future = mBackgroundDexoptJob.start();
@@ -273,9 +281,12 @@ public class BackgroundDexoptJobTest {
@Test
public void testWantsRescheduleFalsePerformed() throws Exception {
- when(mDexoptResult.getFinalStatus()).thenReturn(DexoptResult.DEXOPT_PERFORMED);
+ DexoptResult downgradeResult = createDexoptResultWithStatus(DexoptResult.DEXOPT_PERFORMED);
+ mDexoptResultByPass.put(ArtFlags.PASS_DOWNGRADE, downgradeResult);
+ DexoptResult mainResult = createDexoptResultWithStatus(DexoptResult.DEXOPT_PERFORMED);
+ mDexoptResultByPass.put(ArtFlags.PASS_MAIN, mainResult);
when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
- .thenReturn(mDexoptResult);
+ .thenReturn(mDexoptResultByPass);
mBackgroundDexoptJob.onStartJob(mJobService, mJobParameters);
assertThat(mJobFinishedCalled.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue();
@@ -296,13 +307,22 @@ public class BackgroundDexoptJobTest {
@Test
public void testWantsRescheduleTrue() throws Exception {
- when(mDexoptResult.getFinalStatus()).thenReturn(DexoptResult.DEXOPT_CANCELLED);
+ DexoptResult downgradeResult = createDexoptResultWithStatus(DexoptResult.DEXOPT_PERFORMED);
+ mDexoptResultByPass.put(ArtFlags.PASS_DOWNGRADE, downgradeResult);
+ DexoptResult mainResult = createDexoptResultWithStatus(DexoptResult.DEXOPT_CANCELLED);
+ mDexoptResultByPass.put(ArtFlags.PASS_MAIN, mainResult);
when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
- .thenReturn(mDexoptResult);
+ .thenReturn(mDexoptResultByPass);
mBackgroundDexoptJob.onStartJob(mJobService, mJobParameters);
assertThat(mJobFinishedCalled.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue();
verify(mJobService).jobFinished(any(), eq(true) /* wantsReschedule */);
}
+
+ private DexoptResult createDexoptResultWithStatus(@DexoptResultStatus int status) {
+ return DexoptResult.create("compiler-filter", "reason",
+ List.of(PackageDexoptResult.create(
+ "package-name", List.of() /* dexContainerFileDexoptResults */, status)));
+ }
}
diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java
index 2d4cdc0cf3..cdabcf341c 100644
--- a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java
+++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java
@@ -291,8 +291,7 @@ public class PrimaryDexopterTest extends PrimaryDexopterTestBase {
@Test
public void testDexoptMergesProfiles() throws Exception {
- when(mPkgState.getStateForUser(eq(UserHandle.of(0)))).thenReturn(mPkgUserStateInstalled);
- when(mPkgState.getStateForUser(eq(UserHandle.of(2)))).thenReturn(mPkgUserStateInstalled);
+ setPackageInstalledForUserIds(0, 2);
when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(true);
@@ -336,8 +335,7 @@ public class PrimaryDexopterTest extends PrimaryDexopterTestBase {
@Test
public void testDexoptMergesProfilesMergeFailed() throws Exception {
- when(mPkgState.getStateForUser(eq(UserHandle.of(0)))).thenReturn(mPkgUserStateInstalled);
- when(mPkgState.getStateForUser(eq(UserHandle.of(2)))).thenReturn(mPkgUserStateInstalled);
+ setPackageInstalledForUserIds(0, 2);
when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(false);
@@ -364,6 +362,28 @@ public class PrimaryDexopterTest extends PrimaryDexopterTestBase {
}
@Test
+ public void testDexoptMergesProfilesForceMerge() throws Exception {
+ mDexoptParams = mDexoptParams.toBuilder()
+ .setFlags(ArtFlags.FLAG_FORCE_MERGE_PROFILE,
+ ArtFlags.FLAG_FORCE_MERGE_PROFILE)
+ .build();
+ mPrimaryDexopter =
+ new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal);
+
+ setPackageInstalledForUserIds(0, 2);
+
+ mMergeProfileOptions.forceMerge = true;
+ when(mArtd.mergeProfiles(any(), any(), any(), any(), deepEq(mMergeProfileOptions)))
+ .thenReturn(true);
+
+ makeProfileUsable(mRefProfile);
+ when(mArtd.getProfileVisibility(deepEq(mRefProfile)))
+ .thenReturn(FileVisibility.OTHER_READABLE);
+
+ mPrimaryDexopter.dexopt();
+ }
+
+ @Test
public void testDexoptUsesDmProfile() throws Exception {
makeProfileNotUsable(mRefProfile);
makeProfileNotUsable(mPrebuiltProfile);
@@ -758,4 +778,12 @@ public class PrimaryDexopterTest extends PrimaryDexopterTestBase {
assertThat(result.getExternalProfileErrors()).isEmpty();
}
}
+
+ /** Dexopter relies on this information to determine which current profiles to check. */
+ private void setPackageInstalledForUserIds(int... userIds) {
+ for (int userId : userIds) {
+ when(mPkgState.getStateForUser(eq(UserHandle.of(userId))))
+ .thenReturn(mPkgUserStateInstalled);
+ }
+ }
}
diff --git a/libartservice/service/javatests/com/android/server/art/model/DexoptParamsTest.java b/libartservice/service/javatests/com/android/server/art/model/DexoptParamsTest.java
index 60986414fa..d98340ee17 100644
--- a/libartservice/service/javatests/com/android/server/art/model/DexoptParamsTest.java
+++ b/libartservice/service/javatests/com/android/server/art/model/DexoptParamsTest.java
@@ -16,12 +16,19 @@
package com.android.server.art.model;
+import static com.google.common.truth.Truth.assertThat;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DexoptParamsTest {
@@ -106,4 +113,31 @@ public class DexoptParamsTest {
.setSplitName("split_0")
.build();
}
+
+ @Test
+ public void testToBuilder() {
+ // Update this test with new fields if this assertion fails.
+ assertThat(Arrays.stream(DexoptParams.class.getDeclaredFields())
+ .filter(field -> !Modifier.isStatic(field.getModifiers()))
+ .map(Field::getName)
+ .collect(Collectors.toList()))
+ .containsExactly(
+ "mFlags", "mCompilerFilter", "mPriorityClass", "mReason", "mSplitName");
+
+ DexoptParams params1 =
+ new DexoptParams.Builder("install")
+ .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SINGLE_SPLIT)
+ .setCompilerFilter("speed")
+ .setPriorityClass(90)
+ .setSplitName("split_0")
+ .build();
+
+ DexoptParams params2 = params1.toBuilder().build();
+
+ assertThat(params1.getFlags()).isEqualTo(params2.getFlags());
+ assertThat(params1.getCompilerFilter()).isEqualTo(params2.getCompilerFilter());
+ assertThat(params1.getPriorityClass()).isEqualTo(params2.getPriorityClass());
+ assertThat(params1.getReason()).isEqualTo(params2.getReason());
+ assertThat(params1.getSplitName()).isEqualTo(params2.getSplitName());
+ }
}
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
index 9aeebf38ad..1e29f4e457 100644
--- a/libnativeloader/library_namespaces.cpp
+++ b/libnativeloader/library_namespaces.cpp
@@ -255,7 +255,7 @@ Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t t
// Different name is useful for debugging
namespace_name = kVendorClassloaderNamespaceName;
- } else if (apk_origin == APK_ORIGIN_PRODUCT && is_product_vndk_version_defined()) {
+ } else if (apk_origin == APK_ORIGIN_PRODUCT && is_product_treblelized()) {
unbundled_app_origin = APK_ORIGIN_PRODUCT;
apk_origin_msg = "unbundled product apk";
@@ -406,8 +406,7 @@ Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t t
product_public_libraries());
if (!product_libs.empty()) {
auto target_ns = system_ns;
- if (is_product_vndk_version_defined()) {
- // If ro.product.vndk.version is defined, product namespace provides the product libraries.
+ if (is_product_treblelized()) {
target_ns = NativeLoaderNamespace::GetExportedNamespace(kProductNamespaceName, is_bridged);
}
if (target_ns.ok()) {
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
index 6c0c8b17a7..72348ed364 100644
--- a/libnativeloader/native_loader_test.cpp
+++ b/libnativeloader/native_loader_test.cpp
@@ -18,9 +18,9 @@
#include "native_loader_test.h"
-#include <dlfcn.h>
-
+#include <android-base/properties.h>
#include <android-base/strings.h>
+#include <dlfcn.h>
#include <gtest/gtest.h>
#include "nativehelper/scoped_utf_chars.h"
@@ -348,7 +348,9 @@ TEST_P(NativeLoaderTest_Create, UnbundledVendorApp) {
expected_permitted_path = expected_permitted_path + ":/vendor/" LIB_DIR;
expected_shared_libs_to_platform_ns =
default_public_libraries() + ":" + llndk_libraries_vendor();
- expected_link_with_vndk_ns = true;
+ if (android::base::GetProperty("ro.vndk.version", "") != "") {
+ expected_link_with_vndk_ns = true;
+ }
SetExpectations();
RunTest();
}
@@ -378,15 +380,19 @@ TEST_P(NativeLoaderTest_Create, UnbundledProductApp) {
dex_path = "/product/app/foo/foo.apk";
is_shared = false;
- if (is_product_vndk_version_defined()) {
+ if (is_product_treblelized()) {
expected_namespace_prefix = "product-clns";
- expected_library_path = expected_library_path + ":/product/" LIB_DIR ":/system/product/" LIB_DIR;
+ expected_library_path =
+ expected_library_path + ":/product/" LIB_DIR ":/system/product/" LIB_DIR;
expected_permitted_path =
expected_permitted_path + ":/product/" LIB_DIR ":/system/product/" LIB_DIR;
expected_shared_libs_to_platform_ns =
append_extended_libraries(default_public_libraries() + ":" + llndk_libraries_product());
- expected_link_with_vndk_product_ns = true;
+ if (android::base::GetProperty("ro.product.vndk.version", "") != "") {
+ expected_link_with_vndk_product_ns = true;
+ }
}
+
SetExpectations();
RunTest();
}
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
index 896c5c7106..87210c8f14 100644
--- a/libnativeloader/public_libraries.cpp
+++ b/libnativeloader/public_libraries.cpp
@@ -55,6 +55,7 @@ constexpr const char* kExtendedPublicLibrariesFileSuffix = ".txt";
constexpr const char* kApexLibrariesConfigFile = "/linkerconfig/apex.libraries.config.txt";
constexpr const char* kVendorPublicLibrariesFile = "/vendor/etc/public.libraries.txt";
constexpr const char* kLlndkLibrariesFile = "/apex/com.android.vndk.v{}/etc/llndk.libraries.{}.txt";
+constexpr const char* kLlndkLibrariesNoVndkFile = "/system/etc/llndk.libraries.txt";
constexpr const char* kVndkLibrariesFile = "/apex/com.android.vndk.v{}/etc/vndksp.libraries.{}.txt";
@@ -200,7 +201,7 @@ static std::string InitVendorPublicLibraries() {
// contains the extended public libraries that are loaded from the system namespace.
static std::string InitProductPublicLibraries() {
std::vector<std::string> sonames;
- if (is_product_vndk_version_defined()) {
+ if (is_product_treblelized()) {
ReadExtensionLibraries("/product/etc", &sonames);
}
std::string libs = android::base::Join(sonames, ':');
@@ -217,7 +218,7 @@ static std::string InitExtendedPublicLibraries() {
std::vector<std::string> sonames;
ReadExtensionLibraries("/system/etc", &sonames);
ReadExtensionLibraries("/system_ext/etc", &sonames);
- if (!is_product_vndk_version_defined()) {
+ if (!is_product_treblelized()) {
ReadExtensionLibraries("/product/etc", &sonames);
}
std::string libs = android::base::Join(sonames, ':');
@@ -225,9 +226,30 @@ static std::string InitExtendedPublicLibraries() {
return libs;
}
+bool IsVendorVndkEnabled() {
+#if defined(ART_TARGET_ANDROID)
+ return android::base::GetProperty("ro.vndk.version", "") != "";
+#else
+ return true;
+#endif
+}
+
+bool IsProductVndkEnabled() {
+#if defined(ART_TARGET_ANDROID)
+ return android::base::GetProperty("ro.product.vndk.version", "") != "";
+#else
+ return true;
+#endif
+}
+
static std::string InitLlndkLibrariesVendor() {
- std::string config_file = kLlndkLibrariesFile;
- InsertVndkVersionStr(&config_file, false);
+ std::string config_file;
+ if (IsVendorVndkEnabled()) {
+ config_file = kLlndkLibrariesFile;
+ InsertVndkVersionStr(&config_file, false);
+ } else {
+ config_file = kLlndkLibrariesNoVndkFile;
+ }
auto sonames = ReadConfig(config_file, always_true);
if (!sonames.ok()) {
LOG_ALWAYS_FATAL("%s: %s", config_file.c_str(), sonames.error().message().c_str());
@@ -239,12 +261,17 @@ static std::string InitLlndkLibrariesVendor() {
}
static std::string InitLlndkLibrariesProduct() {
- if (!is_product_vndk_version_defined()) {
- ALOGD("InitLlndkLibrariesProduct: No product VNDK version defined");
+ if (!is_product_treblelized()) {
+ ALOGD("InitLlndkLibrariesProduct: Product is not treblelized");
return "";
}
- std::string config_file = kLlndkLibrariesFile;
- InsertVndkVersionStr(&config_file, true);
+ std::string config_file;
+ if (IsProductVndkEnabled()) {
+ config_file = kLlndkLibrariesFile;
+ InsertVndkVersionStr(&config_file, true);
+ } else {
+ config_file = kLlndkLibrariesNoVndkFile;
+ }
auto sonames = ReadConfig(config_file, always_true);
if (!sonames.ok()) {
LOG_ALWAYS_FATAL("%s: %s", config_file.c_str(), sonames.error().message().c_str());
@@ -256,6 +283,11 @@ static std::string InitLlndkLibrariesProduct() {
}
static std::string InitVndkspLibrariesVendor() {
+ if (!IsVendorVndkEnabled()) {
+ ALOGD("InitVndkspLibrariesVendor: VNDK is deprecated with vendor");
+ return "";
+ }
+
std::string config_file = kVndkLibrariesFile;
InsertVndkVersionStr(&config_file, false);
auto sonames = ReadConfig(config_file, always_true);
@@ -269,8 +301,8 @@ static std::string InitVndkspLibrariesVendor() {
}
static std::string InitVndkspLibrariesProduct() {
- if (!is_product_vndk_version_defined()) {
- ALOGD("InitVndkspLibrariesProduct: No product VNDK version defined");
+ if (!IsProductVndkEnabled()) {
+ ALOGD("InitVndkspLibrariesProduct: VNDK is deprecated with product");
return "";
}
std::string config_file = kVndkLibrariesFile;
@@ -393,9 +425,14 @@ const std::map<std::string, std::string>& apex_public_libraries() {
return public_libraries;
}
-bool is_product_vndk_version_defined() {
+bool is_product_treblelized() {
#if defined(ART_TARGET_ANDROID)
- return android::sysprop::VndkProperties::product_vndk_version().has_value();
+ // Product is not treblelized iff launching version is prior to R and
+ // ro.product.vndk.version is not defined
+ static bool product_treblelized =
+ !(android::base::GetIntProperty("ro.product.first_api_level", 0) < __ANDROID_API_R__ &&
+ !android::sysprop::VndkProperties::product_vndk_version().has_value());
+ return product_treblelized;
#else
return false;
#endif
diff --git a/libnativeloader/public_libraries.h b/libnativeloader/public_libraries.h
index 6f5a13c9b3..1830824704 100644
--- a/libnativeloader/public_libraries.h
+++ b/libnativeloader/public_libraries.h
@@ -47,12 +47,14 @@ const std::string& apex_jni_libraries(const std::string& apex_name);
// but provided by com.android.foo APEX.
const std::map<std::string, std::string>& apex_public_libraries();
-// Returns true if libnativeloader is running on devices and the device has
-// ro.product.vndk.version property. It returns false for host.
-bool is_product_vndk_version_defined();
-
std::string get_vndk_version(bool is_product_vndk);
+// Returnes true if libnativeloader is running on devices and the device has
+// treblelized product partition. It returns false for host.
+// TODO: Remove this function and assume it is always true once when Mainline does not support any
+// devices launched with Q or below.
+bool is_product_treblelized();
+
// These are exported for testing
namespace internal {
diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc
index 814e0b4179..1dc63a0ef9 100644
--- a/libprofile/profile/profile_compilation_info.cc
+++ b/libprofile/profile/profile_compilation_info.cc
@@ -2481,6 +2481,17 @@ bool ProfileCompilationInfo::IsProfileFile(int fd) {
bool ProfileCompilationInfo::UpdateProfileKeys(
const std::vector<std::unique_ptr<const DexFile>>& dex_files, /*out*/ bool* matched) {
+ // This check aligns with when dex2oat falls back from "speed-profile" to "verify".
+ //
+ // ART Service relies on the exit code of profman, which is determined by the value of `matched`,
+ // to judge whether it should re-dexopt for "speed-profile". Therefore, a misalignment will cause
+ // repeated dexopt.
+ if (IsEmpty()) {
+ *matched = false;
+ return true;
+ }
+ DCHECK(!info_.empty());
+
*matched = true;
// A map from the old base key to the new base key.
diff --git a/libprofile/profile/profile_compilation_info_test.cc b/libprofile/profile/profile_compilation_info_test.cc
index b4302663f7..627d05d7dd 100644
--- a/libprofile/profile/profile_compilation_info_test.cc
+++ b/libprofile/profile/profile_compilation_info_test.cc
@@ -1058,6 +1058,28 @@ TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOkButNoMatch) {
}
}
+TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOkButEmpty) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files.push_back(std::unique_ptr<const DexFile>(dex1_renamed));
+ dex_files.push_back(std::unique_ptr<const DexFile>(dex2_renamed));
+
+ // Empty profile.
+ ProfileCompilationInfo info;
+
+ // Update the profile keys based on the original dex files.
+ bool matched = false;
+ ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &matched));
+ ASSERT_FALSE(matched);
+
+ // Verify that the updated profile is still empty.
+ EXPECT_TRUE(info.IsEmpty());
+
+ // Release the ownership as this is held by the test class;
+ for (std::unique_ptr<const DexFile>& dex : dex_files) {
+ UNUSED(dex.release());
+ }
+}
+
TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyFail) {
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files.push_back(std::unique_ptr<const DexFile>(dex1_renamed));
diff --git a/profman/include/profman/profman_result.h b/profman/include/profman/profman_result.h
index 9c9aca9e05..c4ca98828d 100644
--- a/profman/include/profman/profman_result.h
+++ b/profman/include/profman/profman_result.h
@@ -40,9 +40,13 @@ class ProfmanResult {
kSuccess = 0,
// A merge has been performed, meaning the reference profile has been changed.
kCompile = 1,
- // `--profile-file(-fd)` is not specified, or the specified profiles are outdated (i.e., APK
- // filename or checksum mismatch), empty, or don't contain enough number of new classes and
- // methods that meets the threshold to trigger a merge.
+ // One of the following conditions is met:
+ // - `--profile-file(-fd)` is not specified.
+ // - The specified profiles are outdated (i.e., APK filename or checksum mismatch).
+ // - The specified profiles are empty.
+ // - The specified profiles don't contain any new class or method.
+ // - The specified profiles don't contain enough number of new classes and methods that meets
+ // the threshold to trigger a merge, and `--force-merge-and-analyze` is not set.
kSkipCompilationSmallDelta = 2,
// All the input profiles (including the reference profile) are either outdated (i.e., APK
// filename or checksum mismatch) or empty.
diff --git a/profman/profile_assistant.cc b/profman/profile_assistant.cc
index abbde2d527..4d98e4927d 100644
--- a/profman/profile_assistant.cc
+++ b/profman/profile_assistant.cc
@@ -54,7 +54,7 @@ ProfmanResult::ProcessingResult ProfileAssistant::ProcessProfilesInternal(
ProfileCompilationInfo cur_info(options.IsBootImageMerge());
if (!cur_info.Load(profile_files[i]->Fd(), /*merge_classes=*/ true, filter_fn)) {
LOG(WARNING) << "Could not load profile file at index " << i;
- if (options.IsForceMerge()) {
+ if (options.IsForceMerge() || options.IsForceMergeAndAnalyze()) {
// If we have to merge forcefully, ignore load failures.
// This is useful for boot image profiles to ignore stale profiles which are
// cleared lazily.
@@ -79,17 +79,30 @@ ProfmanResult::ProcessingResult ProfileAssistant::ProcessProfilesInternal(
if (info.IsEmpty()) {
return ProfmanResult::kSkipCompilationEmptyProfiles;
}
- uint32_t min_change_in_methods_for_compilation = std::max(
- (options.GetMinNewMethodsPercentChangeForCompilation() * number_of_methods) / 100,
- kMinNewMethodsForCompilation);
- uint32_t min_change_in_classes_for_compilation = std::max(
- (options.GetMinNewClassesPercentChangeForCompilation() * number_of_classes) / 100,
- kMinNewClassesForCompilation);
- // Check if there is enough new information added by the current profiles.
- if (((info.GetNumberOfMethods() - number_of_methods) < min_change_in_methods_for_compilation) &&
- ((info.GetNumberOfResolvedClasses() - number_of_classes)
- < min_change_in_classes_for_compilation)) {
- return ProfmanResult::kSkipCompilationSmallDelta;
+
+ if (options.IsForceMergeAndAnalyze()) {
+ // When we force merge and analyze, we want to always recompile unless there is absolutely no
+ // difference between before and after the merge (i.e., the classes and methods in the
+ // reference profile were already a superset of those in all current profiles before the
+ // merge.)
+ if (info.GetNumberOfMethods() == number_of_methods &&
+ info.GetNumberOfResolvedClasses() == number_of_classes) {
+ return ProfmanResult::kSkipCompilationSmallDelta;
+ }
+ } else {
+ uint32_t min_change_in_methods_for_compilation = std::max(
+ (options.GetMinNewMethodsPercentChangeForCompilation() * number_of_methods) / 100,
+ kMinNewMethodsForCompilation);
+ uint32_t min_change_in_classes_for_compilation = std::max(
+ (options.GetMinNewClassesPercentChangeForCompilation() * number_of_classes) / 100,
+ kMinNewClassesForCompilation);
+ // Check if there is enough new information added by the current profiles.
+ if (((info.GetNumberOfMethods() - number_of_methods) <
+ min_change_in_methods_for_compilation) &&
+ ((info.GetNumberOfResolvedClasses() - number_of_classes) <
+ min_change_in_classes_for_compilation)) {
+ return ProfmanResult::kSkipCompilationSmallDelta;
+ }
}
}
diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h
index 6b7a7a67f5..5ef37b4137 100644
--- a/profman/profile_assistant.h
+++ b/profman/profile_assistant.h
@@ -32,8 +32,8 @@ class ProfileAssistant {
public:
static constexpr bool kForceMergeDefault = false;
static constexpr bool kBootImageMergeDefault = false;
- static constexpr uint32_t kMinNewMethodsPercentChangeForCompilation = 20;
- static constexpr uint32_t kMinNewClassesPercentChangeForCompilation = 20;
+ static constexpr uint32_t kMinNewMethodsPercentChangeForCompilation = 2;
+ static constexpr uint32_t kMinNewClassesPercentChangeForCompilation = 2;
Options()
: force_merge_(kForceMergeDefault),
@@ -44,7 +44,9 @@ class ProfileAssistant {
kMinNewClassesPercentChangeForCompilation) {
}
+ // Only for S and T uses. U+ should use `IsForceMergeAndAnalyze`.
bool IsForceMerge() const { return force_merge_; }
+ bool IsForceMergeAndAnalyze() const { return force_merge_and_analyze_; }
bool IsBootImageMerge() const { return boot_image_merge_; }
uint32_t GetMinNewMethodsPercentChangeForCompilation() const {
return min_new_methods_percent_change_for_compilation_;
@@ -54,6 +56,7 @@ class ProfileAssistant {
}
void SetForceMerge(bool value) { force_merge_ = value; }
+ void SetForceMergeAndAnalyze(bool value) { force_merge_and_analyze_ = value; }
void SetBootImageMerge(bool value) { boot_image_merge_ = value; }
void SetMinNewMethodsPercentChangeForCompilation(uint32_t value) {
min_new_methods_percent_change_for_compilation_ = value;
@@ -63,10 +66,15 @@ class ProfileAssistant {
}
private:
- // If true, performs a forced merge, without analyzing if there is a
- // significant difference between the current profile and the reference profile.
+ // If true, performs a forced merge, without analyzing if there is a significant difference
+ // between before and after the merge.
// See ProfileAssistant#ProcessProfile.
+ // Only for S and T uses. U+ should use `force_merge_and_analyze_`.
bool force_merge_;
+ // If true, performs a forced merge and analyzes if there is any difference between before and
+ // after the merge.
+ // See ProfileAssistant#ProcessProfile.
+ bool force_merge_and_analyze_;
// Signals that the merge is for boot image profiles. It will ignore differences
// in profile versions (instead of aborting).
bool boot_image_merge_;
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index f7c4255071..93766e5d52 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -684,16 +684,6 @@ TEST_F(ProfileAssistantTest, ShouldAdviseCompilationMethodPercentage) {
kNumberOfMethodsInCurProfile, kNumberOfMethodsInRefProfile, extra_args));
}
-TEST_F(ProfileAssistantTest, DoNotAdviseCompilationMethodPercentageWithNewMin) {
- const uint16_t kNumberOfMethodsInRefProfile = 6000;
- const uint16_t kNumberOfMethodsInCurProfile = 6200; // Threshold is 20%.
-
- // We should not advise compilation.
- ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
- CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile,
- kNumberOfMethodsInRefProfile));
-}
-
TEST_F(ProfileAssistantTest, DoNotAdviseCompilationClassPercentage) {
const uint16_t kNumberOfClassesInRefProfile = 6000;
const uint16_t kNumberOfClassesInCurProfile = 6110; // Threshold is 2%.
@@ -716,16 +706,6 @@ TEST_F(ProfileAssistantTest, ShouldAdviseCompilationClassPercentage) {
kNumberOfClassesInCurProfile, kNumberOfClassesInRefProfile, extra_args));
}
-TEST_F(ProfileAssistantTest, DoNotAdviseCompilationClassPercentageWithNewMin) {
- const uint16_t kNumberOfClassesInRefProfile = 6000;
- const uint16_t kNumberOfClassesInCurProfile = 6200; // Threshold is 20%.
-
- // We should not advise compilation.
- ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
- CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile,
- kNumberOfClassesInRefProfile));
-}
-
TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) {
ScratchFile profile1;
ScratchFile profile2;
@@ -2078,38 +2058,60 @@ TEST_F(ProfileAssistantTest, CopyAndUpdateProfileKeyNoUpdate) {
}
TEST_F(ProfileAssistantTest, BootImageMerge) {
- ScratchFile profile;
- ScratchFile reference_profile;
- std::vector<int> profile_fds({GetFd(profile)});
- int reference_profile_fd = GetFd(reference_profile);
- std::vector<uint32_t> hot_methods_cur;
- std::vector<uint32_t> hot_methods_ref;
- std::vector<uint32_t> empty_vector;
- size_t num_methods = 100;
- for (size_t i = 0; i < num_methods; ++i) {
- hot_methods_cur.push_back(i);
+ ScratchFile profile1;
+ ScratchFile profile2;
+ ScratchFile profile3;
+ ScratchFile output_profile;
+ std::vector<uint32_t> hot_methods_1;
+ std::vector<uint32_t> hot_methods_2;
+ std::vector<uint32_t> hot_methods_3;
+ for (size_t i = 0; i < 100; ++i) {
+ hot_methods_1.push_back(i);
+ }
+ for (size_t i = 50; i < 150; ++i) {
+ hot_methods_2.push_back(i);
}
- for (size_t i = 0; i < num_methods; ++i) {
- hot_methods_ref.push_back(i);
+ for (size_t i = 100; i < 200; ++i) {
+ hot_methods_3.push_back(i);
}
- ProfileCompilationInfo info1(/*for_boot_image=*/ true);
- SetupBasicProfile(dex1, hot_methods_cur, empty_vector, empty_vector,
- profile, &info1);
+ ProfileCompilationInfo info1(/*for_boot_image=*/false);
+ SetupBasicProfile(
+ dex1, hot_methods_1, /*startup_methods=*/{}, /*post_startup_methods=*/{}, profile1, &info1);
ProfileCompilationInfo info2(/*for_boot_image=*/true);
- SetupBasicProfile(dex1, hot_methods_ref, empty_vector, empty_vector,
- reference_profile, &info2);
+ SetupBasicProfile(
+ dex1, hot_methods_2, /*startup_methods=*/{}, /*post_startup_methods=*/{}, profile2, &info2);
+ ProfileCompilationInfo info3(/*for_boot_image=*/true);
+ SetupBasicProfile(
+ dex1, hot_methods_3, /*startup_methods=*/{}, /*post_startup_methods=*/{}, profile3, &info3);
- std::vector<const std::string> extra_args({"--force-merge", "--boot-image-merge"});
-
- int return_code = ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
-
- ASSERT_EQ(return_code, ProfmanResult::kSuccess);
+ {
+ int return_code = ProcessProfiles({profile1.GetFd(), profile2.GetFd(), profile3.GetFd()},
+ output_profile.GetFd(),
+ {"--force-merge-and-analyze", "--boot-image-merge"});
+ ASSERT_EQ(return_code, ProfmanResult::kCompile);
+
+ // Verify the result: it should be equal to info2 union info3 since info1 is a regular profile
+ // and should be ignored.
+ ProfileCompilationInfo result(/*for_boot_image=*/true);
+ ASSERT_TRUE(result.Load(output_profile.GetFd()));
+ ASSERT_TRUE(info2.MergeWith(info3));
+ ASSERT_TRUE(result.Equals(info2));
+ }
- // Verify the result: it should be equal to info2 since info1 is a regular profile
- // and should be ignored.
- ProfileCompilationInfo result(/*for_boot_image=*/ true);
- ASSERT_TRUE(result.Load(reference_profile.GetFd()));
- ASSERT_TRUE(result.Equals(info2));
+ // Same for the legacy force merge mode.
+ {
+ int return_code = ProcessProfiles({profile1.GetFd(), profile2.GetFd(), profile3.GetFd()},
+ output_profile.GetFd(),
+ {"--force-merge", "--boot-image-merge"});
+ ASSERT_EQ(return_code, ProfmanResult::kSuccess);
+
+ // Verify the result: it should be equal to info2 union info3 since info1 is a regular profile
+ // and should be ignored.
+ ProfileCompilationInfo result(/*for_boot_image=*/true);
+ ASSERT_TRUE(result.Load(output_profile.GetFd()));
+ ASSERT_TRUE(info2.MergeWith(info3));
+ ASSERT_TRUE(result.Equals(info2));
+ }
}
// Under default behaviour we should not advice compilation
@@ -2154,6 +2156,82 @@ TEST_F(ProfileAssistantTest, ForceMerge) {
ASSERT_TRUE(result.Equals(info1));
}
+TEST_F(ProfileAssistantTest, ForceMergeAndAnalyze) {
+ const uint16_t kNumberOfMethodsInRefProfile = 600;
+ const uint16_t kNumberOfMethodsInCurProfile = 601;
+
+ ScratchFile ref_profile;
+ ScratchFile cur_profile;
+
+ ProfileCompilationInfo ref_info;
+ SetupProfile(
+ dex1, dex2, kNumberOfMethodsInRefProfile, /*number_of_classes=*/0, ref_profile, &ref_info);
+ ProfileCompilationInfo cur_info;
+ SetupProfile(
+ dex1, dex2, kNumberOfMethodsInCurProfile, /*number_of_classes=*/0, cur_profile, &cur_info);
+
+ std::vector<const std::string> extra_args({"--force-merge-and-analyze"});
+ int return_code = ProcessProfiles({cur_profile.GetFd()}, ref_profile.GetFd(), extra_args);
+
+ ASSERT_EQ(return_code, ProfmanResult::kCompile);
+
+ // Check that the result is the aggregation.
+ ProfileCompilationInfo result;
+ ASSERT_TRUE(result.Load(ref_profile.GetFd()));
+ ASSERT_TRUE(ref_info.MergeWith(cur_info));
+ ASSERT_TRUE(result.Equals(ref_info));
+}
+
+TEST_F(ProfileAssistantTest, ForceMergeAndAnalyzeNoDelta) {
+ const uint16_t kNumberOfMethodsInRefProfile = 600;
+ const uint16_t kNumberOfMethodsInCurProfile = 600;
+
+ ScratchFile ref_profile;
+ ScratchFile cur_profile;
+
+ ProfileCompilationInfo ref_info;
+ SetupProfile(
+ dex1, dex2, kNumberOfMethodsInRefProfile, /*number_of_classes=*/0, ref_profile, &ref_info);
+ ProfileCompilationInfo cur_info;
+ SetupProfile(
+ dex1, dex2, kNumberOfMethodsInCurProfile, /*number_of_classes=*/0, cur_profile, &cur_info);
+
+ std::vector<const std::string> extra_args({"--force-merge-and-analyze"});
+ int return_code = ProcessProfiles({cur_profile.GetFd()}, ref_profile.GetFd(), extra_args);
+
+ ASSERT_EQ(return_code, ProfmanResult::kSkipCompilationSmallDelta);
+
+ // Check that the reference profile is unchanged.
+ ProfileCompilationInfo result;
+ ASSERT_TRUE(result.Load(ref_profile.GetFd()));
+ ASSERT_TRUE(result.Equals(ref_info));
+}
+
+TEST_F(ProfileAssistantTest, ForceMergeAndAnalyzeEmptyProfiles) {
+ const uint16_t kNumberOfMethodsInRefProfile = 0;
+ const uint16_t kNumberOfMethodsInCurProfile = 0;
+
+ ScratchFile ref_profile;
+ ScratchFile cur_profile;
+
+ ProfileCompilationInfo ref_info;
+ SetupProfile(
+ dex1, dex2, kNumberOfMethodsInRefProfile, /*number_of_classes=*/0, ref_profile, &ref_info);
+ ProfileCompilationInfo cur_info;
+ SetupProfile(
+ dex1, dex2, kNumberOfMethodsInCurProfile, /*number_of_classes=*/0, cur_profile, &cur_info);
+
+ std::vector<const std::string> extra_args({"--force-merge-and-analyze"});
+ int return_code = ProcessProfiles({cur_profile.GetFd()}, ref_profile.GetFd(), extra_args);
+
+ ASSERT_EQ(return_code, ProfmanResult::kSkipCompilationEmptyProfiles);
+
+ // Check that the reference profile is unchanged.
+ ProfileCompilationInfo result;
+ ASSERT_TRUE(result.Load(ref_profile.GetFd()));
+ ASSERT_TRUE(result.Equals(ref_info));
+}
+
// Test that we consider the annations when we merge boot image profiles.
TEST_F(ProfileAssistantTest, BootImageMergeWithAnnotations) {
ScratchFile profile;
@@ -2236,25 +2314,40 @@ TEST_F(ProfileAssistantTest, ForceMergeIgnoreProfilesItCannotLoad) {
std::string content = "giberish";
ASSERT_TRUE(profile1.GetFile()->WriteFully(content.c_str(), content.length()));
- ProfileCompilationInfo info2(/*for_boot_image=*/ true);
+ ProfileCompilationInfo info2(/*for_boot_image=*/true);
info2.Save(profile2.GetFd());
- std::vector<int> profile_fds({ GetFd(profile1)});
+ std::vector<int> profile_fds({GetFd(profile1)});
int reference_profile_fd = GetFd(profile2);
// With force-merge we should merge successfully.
- std::vector<const std::string> extra_args({"--force-merge", "--boot-image-merge"});
- ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, extra_args),
- ProfmanResult::kSuccess);
+ {
+ ASSERT_EQ(
+ ProcessProfiles(
+ profile_fds, reference_profile_fd, {"--force-merge-and-analyze", "--boot-image-merge"}),
+ ProfmanResult::kSkipCompilationEmptyProfiles);
+
+ ProfileCompilationInfo result(/*for_boot_image=*/true);
+ ASSERT_TRUE(result.Load(reference_profile_fd));
+ ASSERT_TRUE(info2.Equals(result));
+ }
- ProfileCompilationInfo result(/*for_boot_image=*/ true);
- ASSERT_TRUE(result.Load(reference_profile_fd));
- ASSERT_TRUE(info2.Equals(result));
+ // Same for the legacy force merge mode.
+ {
+ ASSERT_EQ(
+ ProcessProfiles(profile_fds, reference_profile_fd, {"--force-merge", "--boot-image-merge"}),
+ ProfmanResult::kSuccess);
+
+ ProfileCompilationInfo result(/*for_boot_image=*/true);
+ ASSERT_TRUE(result.Load(reference_profile_fd));
+ ASSERT_TRUE(info2.Equals(result));
+ }
// Without force-merge we should fail.
- std::vector<const std::string> extra_args2({"--boot-image-merge"});
- ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, extra_args2),
- ProfmanResult::kErrorBadProfiles);
+ {
+ ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, {"--boot-image-merge"}),
+ ProfmanResult::kErrorBadProfiles);
+ }
}
} // namespace art
diff --git a/profman/profman.cc b/profman/profman.cc
index 375a489821..0958f4b01c 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -190,10 +190,13 @@ NO_RETURN static void Usage(const char *fmt, ...) {
UsageError(" --boot-image-merge: indicates that this merge is for a boot image profile.");
UsageError(" In this case, the reference profile must have a boot profile version.");
UsageError(" --force-merge: performs a forced merge, without analyzing if there is a");
- UsageError(" significant difference between the current profile and the reference profile.");
- UsageError(" --min-new-methods-percent-change=percentage between 0 and 100 (default 20)");
+ UsageError(" significant difference between before and after the merge.");
+ UsageError(" Deprecated. Use --force-merge-and-analyze instead.");
+ UsageError(" --force-merge-and-analyze: performs a forced merge and analyzes if there is any");
+ UsageError(" difference between before and after the merge.");
+ UsageError(" --min-new-methods-percent-change=percentage between 0 and 100 (default 2)");
UsageError(" the min percent of new methods to trigger a compilation.");
- UsageError(" --min-new-classes-percent-change=percentage between 0 and 100 (default 20)");
+ UsageError(" --min-new-classes-percent-change=percentage between 0 and 100 (default 2)");
UsageError(" the min percent of new classes to trigger a compilation.");
UsageError("");
@@ -469,7 +472,11 @@ class ProfMan final {
} else if (option == "--boot-image-merge") {
profile_assistant_options_.SetBootImageMerge(true);
} else if (option == "--force-merge") {
+ // For backward compatibility only.
+ // TODO(jiakaiz): Remove this when S and T are no longer supported.
profile_assistant_options_.SetForceMerge(true);
+ } else if (option == "--force-merge-and-analyze") {
+ profile_assistant_options_.SetForceMergeAndAnalyze(true);
} else {
Usage("Unknown argument '%s'", raw_option);
}
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index 44c476b182..8cd92a71f2 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -1059,6 +1059,8 @@ void MarkCompact::PrepareForCompaction() {
post_compact_end_ = AlignUp(space_begin + total, kPageSize);
CHECK_EQ(post_compact_end_, space_begin + moving_first_objs_count_ * kPageSize);
black_objs_slide_diff_ = black_allocations_begin_ - post_compact_end_;
+ // We shouldn't be consuming more space after compaction than pre-compaction.
+ CHECK_GE(black_objs_slide_diff_, 0);
// How do we handle compaction of heap portion used for allocations after the
// marking-pause?
// All allocations after the marking-pause are considered black (reachable)
@@ -1345,9 +1347,8 @@ void MarkCompact::MarkingPause() {
// Align-up to page boundary so that black allocations happen from next page
// onwards. Also, it ensures that 'end' is aligned for card-table's
// ClearCardRange().
- black_allocations_begin_ = bump_pointer_space_->AlignEnd(thread_running_gc_, kPageSize);
- DCHECK(IsAligned<kAlignment>(black_allocations_begin_));
- black_allocations_begin_ = AlignUp(black_allocations_begin_, kPageSize);
+ black_allocations_begin_ = bump_pointer_space_->AlignEnd(thread_running_gc_, kPageSize, heap_);
+ DCHECK_ALIGNED_PARAM(black_allocations_begin_, kPageSize);
// Re-mark root set. Doesn't include thread-roots as they are already marked
// above.
@@ -1408,11 +1409,11 @@ void MarkCompact::Sweep(bool swap_bitmaps) {
DCHECK(mark_stack_->IsEmpty());
}
for (const auto& space : GetHeap()->GetContinuousSpaces()) {
- if (space->IsContinuousMemMapAllocSpace() && space != bump_pointer_space_) {
+ if (space->IsContinuousMemMapAllocSpace() && space != bump_pointer_space_ &&
+ !immune_spaces_.ContainsSpace(space)) {
space::ContinuousMemMapAllocSpace* alloc_space = space->AsContinuousMemMapAllocSpace();
- TimingLogger::ScopedTiming split(
- alloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepMallocSpace",
- GetTimings());
+ DCHECK(!alloc_space->IsZygoteSpace());
+ TimingLogger::ScopedTiming split("SweepMallocSpace", GetTimings());
RecordFree(alloc_space->Sweep(swap_bitmaps));
}
}
@@ -2431,6 +2432,9 @@ void MarkCompact::UpdateMovingSpaceBlackAllocations() {
DCHECK_LE(begin, black_allocs);
size_t consumed_blocks_count = 0;
size_t first_block_size;
+ // Needed only for debug at the end of the function. Hopefully compiler will
+ // eliminate it otherwise.
+ size_t num_blocks = 0;
// Get the list of all blocks allocated in the bump-pointer space.
std::vector<size_t>* block_sizes = bump_pointer_space_->GetBlockSizes(thread_running_gc_,
&first_block_size);
@@ -2441,6 +2445,7 @@ void MarkCompact::UpdateMovingSpaceBlackAllocations() {
uint32_t remaining_chunk_size = 0;
uint32_t first_chunk_size = 0;
mirror::Object* first_obj = nullptr;
+ num_blocks = block_sizes->size();
for (size_t block_size : *block_sizes) {
block_end += block_size;
// Skip the blocks that are prior to the black allocations. These will be
@@ -2545,6 +2550,24 @@ void MarkCompact::UpdateMovingSpaceBlackAllocations() {
bump_pointer_space_->SetBlockSizes(thread_running_gc_,
post_compact_end_ - begin,
consumed_blocks_count);
+ if (kIsDebugBuild) {
+ size_t moving_space_size = bump_pointer_space_->Size();
+ size_t los_size = 0;
+ if (heap_->GetLargeObjectsSpace()) {
+ los_size = heap_->GetLargeObjectsSpace()->GetBytesAllocated();
+ }
+ // The moving-space size is already updated to post-compact size in SetBlockSizes above.
+ // Also, bytes-allocated has already been adjusted with large-object space' freed-bytes
+ // in Sweep(), but not with moving-space freed-bytes.
+ CHECK_GE(heap_->GetBytesAllocated() - black_objs_slide_diff_, moving_space_size + los_size)
+ << " moving-space size:" << moving_space_size
+ << " moving-space bytes-freed:" << black_objs_slide_diff_
+ << " large-object-space size:" << los_size
+ << " large-object-space bytes-freed:" << GetCurrentIteration()->GetFreedLargeObjectBytes()
+ << " num-tlabs-merged:" << consumed_blocks_count
+ << " main-block-size:" << (post_compact_end_ - begin)
+ << " total-tlabs-moving-space:" << num_blocks;
+ }
}
void MarkCompact::UpdateNonMovingSpaceBlackAllocations() {
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 922b58870d..c5bd79d24c 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -193,8 +193,7 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self,
}
if (bytes_tl_bulk_allocated > 0) {
starting_gc_num = GetCurrentGcNum();
- size_t num_bytes_allocated_before =
- num_bytes_allocated_.fetch_add(bytes_tl_bulk_allocated, std::memory_order_relaxed);
+ size_t num_bytes_allocated_before = AddBytesAllocated(bytes_tl_bulk_allocated);
new_num_bytes_allocated = num_bytes_allocated_before + bytes_tl_bulk_allocated;
// Only trace when we get an increase in the number of bytes allocated. This happens when
// obtaining a new TLAB and isn't often enough to hurt performance according to golem.
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 6a6a913bc4..2d1d393000 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -3754,7 +3754,10 @@ void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran,
grow_bytes = 0;
}
}
- CHECK_LE(target_size, std::numeric_limits<size_t>::max());
+ CHECK_LE(target_size, std::numeric_limits<size_t>::max())
+ << " bytes_allocated:" << bytes_allocated
+ << " bytes_freed:" << current_gc_iteration_.GetFreedBytes()
+ << " large_obj_bytes_freed:" << current_gc_iteration_.GetFreedLargeObjectBytes();
if (!ignore_target_footprint_) {
SetIdealFootprint(target_size);
// Store target size (computed with foreground heap growth multiplier) for updating
@@ -4533,10 +4536,9 @@ mirror::Object* Heap::AllocWithNewTLAB(Thread* self,
}
// Try allocating a new thread local buffer, if the allocation fails the space must be
// full so return null.
- if (!bump_pointer_space_->AllocNewTlab(self, new_tlab_size)) {
+ if (!bump_pointer_space_->AllocNewTlab(self, new_tlab_size, bytes_tl_bulk_allocated)) {
return nullptr;
}
- *bytes_tl_bulk_allocated = new_tlab_size;
if (CheckPerfettoJHPEnabled()) {
VLOG(heap) << "JHP:kAllocatorTypeTLAB, New Tlab bytes allocated= " << new_tlab_size;
}
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index fb56b5865f..d7f6948b85 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -550,6 +550,11 @@ class Heap {
return num_bytes_allocated_.load(std::memory_order_relaxed);
}
+ // Returns bytes_allocated before adding 'bytes' to it.
+ size_t AddBytesAllocated(size_t bytes) {
+ return num_bytes_allocated_.fetch_add(bytes, std::memory_order_relaxed);
+ }
+
bool GetUseGenerationalCC() const {
return use_generational_cc_;
}
diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc
index c357a877e2..c63559a555 100644
--- a/runtime/gc/space/bump_pointer_space.cc
+++ b/runtime/gc/space/bump_pointer_space.cc
@@ -164,7 +164,6 @@ void BumpPointerSpace::UpdateMainBlock() {
// Returns the start of the storage.
uint8_t* BumpPointerSpace::AllocBlock(size_t bytes) {
- bytes = RoundUp(bytes, kAlignment);
if (block_sizes_.empty()) {
UpdateMainBlock();
}
@@ -222,7 +221,8 @@ void BumpPointerSpace::RevokeThreadLocalBuffersLocked(Thread* thread) {
thread->ResetTlab();
}
-bool BumpPointerSpace::AllocNewTlab(Thread* self, size_t bytes) {
+bool BumpPointerSpace::AllocNewTlab(Thread* self, size_t bytes, size_t* bytes_tl_bulk_allocated) {
+ bytes = RoundUp(bytes, kAlignment);
MutexLock mu(Thread::Current(), lock_);
RevokeThreadLocalBuffersLocked(self);
uint8_t* start = AllocBlock(bytes);
@@ -230,6 +230,9 @@ bool BumpPointerSpace::AllocNewTlab(Thread* self, size_t bytes) {
return false;
}
self->SetTlab(start, start + bytes, start + bytes);
+ if (bytes_tl_bulk_allocated != nullptr) {
+ *bytes_tl_bulk_allocated = bytes;
+ }
return true;
}
@@ -253,7 +256,7 @@ size_t BumpPointerSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* u
return num_bytes;
}
-uint8_t* BumpPointerSpace::AlignEnd(Thread* self, size_t alignment) {
+uint8_t* BumpPointerSpace::AlignEnd(Thread* self, size_t alignment, Heap* heap) {
Locks::mutator_lock_->AssertExclusiveHeld(self);
DCHECK(IsAligned<kAlignment>(alignment));
uint8_t* end = end_.load(std::memory_order_relaxed);
@@ -261,6 +264,7 @@ uint8_t* BumpPointerSpace::AlignEnd(Thread* self, size_t alignment) {
ptrdiff_t diff = aligned_end - end;
if (diff > 0) {
end_.store(aligned_end, std::memory_order_relaxed);
+ heap->AddBytesAllocated(diff);
// If we have blocks after the main one. Then just add the diff to the last
// block.
MutexLock mu(self, lock_);
@@ -268,7 +272,7 @@ uint8_t* BumpPointerSpace::AlignEnd(Thread* self, size_t alignment) {
block_sizes_.back() += diff;
}
}
- return end;
+ return aligned_end;
}
std::vector<size_t>* BumpPointerSpace::GetBlockSizes(Thread* self, size_t* main_block_size) {
diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h
index c2cac13a1f..d5ab5069ec 100644
--- a/runtime/gc/space/bump_pointer_space.h
+++ b/runtime/gc/space/bump_pointer_space.h
@@ -142,8 +142,9 @@ class BumpPointerSpace final : public ContinuousMemMapAllocSpace {
// TODO: Change this? Mainly used for compacting to a particular region of memory.
BumpPointerSpace(const std::string& name, uint8_t* begin, uint8_t* limit);
- // Allocate a new TLAB, returns false if the allocation failed.
- bool AllocNewTlab(Thread* self, size_t bytes) REQUIRES(!lock_);
+ // Allocate a new TLAB and updates bytes_tl_bulk_allocated with the
+ // allocation-size, returns false if the allocation failed.
+ bool AllocNewTlab(Thread* self, size_t bytes, size_t* bytes_tl_bulk_allocated) REQUIRES(!lock_);
BumpPointerSpace* AsBumpPointerSpace() override {
return this;
@@ -212,8 +213,8 @@ class BumpPointerSpace final : public ContinuousMemMapAllocSpace {
// Align end to the given alignment. This is done in MarkCompact GC when
// mutators are suspended so that upcoming TLAB allocations start with a new
- // page. Returns the pre-alignment end.
- uint8_t* AlignEnd(Thread* self, size_t alignment) REQUIRES(Locks::mutator_lock_);
+ // page. Adjust's heap's bytes_allocated accordingly. Returns the aligned end.
+ uint8_t* AlignEnd(Thread* self, size_t alignment, Heap* heap) REQUIRES(Locks::mutator_lock_);
friend class collector::MarkSweep;
friend class collector::MarkCompact;
diff --git a/runtime/javaheapprof/javaheapsampler.cc b/runtime/javaheapprof/javaheapsampler.cc
index a73ed0b719..74671347c7 100644
--- a/runtime/javaheapprof/javaheapsampler.cc
+++ b/runtime/javaheapprof/javaheapsampler.cc
@@ -131,10 +131,6 @@ void HeapSampler::AdjustSampleOffset(size_t adjustment) {
<< " next_bytes_until_sample = " << next_bytes_until_sample;
}
-bool HeapSampler::IsEnabled() {
- return enabled_.load(std::memory_order_acquire);
-}
-
int HeapSampler::GetSamplingInterval() {
return p_sampling_interval_.load(std::memory_order_acquire);
}
diff --git a/runtime/javaheapprof/javaheapsampler.h b/runtime/javaheapprof/javaheapsampler.h
index 618893cad0..41514726cd 100644
--- a/runtime/javaheapprof/javaheapsampler.h
+++ b/runtime/javaheapprof/javaheapsampler.h
@@ -68,7 +68,7 @@ class HeapSampler {
// of new Tlab after Reset.
void AdjustSampleOffset(size_t adjustment);
// Is heap sampler enabled?
- bool IsEnabled();
+ bool IsEnabled() { return enabled_.load(std::memory_order_acquire); }
// Set the sampling interval.
void SetSamplingInterval(int sampling_interval) REQUIRES(!geo_dist_rng_lock_);
// Return the sampling interval.
@@ -80,7 +80,7 @@ class HeapSampler {
// possibly decreasing sample intervals by sample_adj_bytes.
size_t PickAndAdjustNextSample(size_t sample_adj_bytes = 0) REQUIRES(!geo_dist_rng_lock_);
- std::atomic<bool> enabled_;
+ std::atomic<bool> enabled_{false};
// Default sampling interval is 4kb.
// Writes guarded by geo_dist_rng_lock_.
std::atomic<int> p_sampling_interval_{4 * 1024};
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index b231cce0bc..3472c788ab 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -71,7 +71,7 @@ static constexpr uint32_t kJitStressDefaultOptimizeThreshold = kJitDefaultOptimi
static constexpr uint32_t kJitSlowStressDefaultOptimizeThreshold =
kJitStressDefaultOptimizeThreshold / 2;
-static constexpr uint32_t kJitDefaultWarmupThreshold = 0xffff;
+static constexpr uint32_t kJitDefaultWarmupThreshold = 0x3fff;
// Different warm-up threshold constants. These default to the equivalent warmup thresholds divided
// by 2, but can be overridden at the command-line.
static constexpr uint32_t kJitStressDefaultWarmupThreshold = kJitDefaultWarmupThreshold / 2;
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 3c73cc569e..af0ee53854 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -347,10 +347,12 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
runtime->GetHeap()->PostForkChildAction(thread);
- // Setup an app startup complete task in case the app doesn't notify it
- // through VMRuntime::notifyStartupCompleted.
- static constexpr uint64_t kMaxAppStartupTimeNs = MsToNs(5000); // 5 seconds
- runtime->GetHeap()->AddHeapTask(new StartupCompletedTask(NanoTime() + kMaxAppStartupTimeNs));
+ if (!is_zygote) {
+ // Setup an app startup complete task in case the app doesn't notify it
+ // through VMRuntime::notifyStartupCompleted.
+ static constexpr uint64_t kMaxAppStartupTimeNs = MsToNs(5000); // 5 seconds
+ runtime->GetHeap()->AddHeapTask(new StartupCompletedTask(NanoTime() + kMaxAppStartupTimeNs));
+ }
if (runtime->GetJit() != nullptr) {
if (!is_system_server) {
diff --git a/test/2247-checker-write-barrier-elimination/Android.bp b/test/2247-checker-write-barrier-elimination/Android.bp
index c9744e9b00..5848cb496e 100644
--- a/test/2247-checker-write-barrier-elimination/Android.bp
+++ b/test/2247-checker-write-barrier-elimination/Android.bp
@@ -15,7 +15,7 @@ package {
java_test {
name: "art-run-test-2247-checker-write-barrier-elimination",
defaults: ["art-run-test-defaults"],
- test_config_template: ":art-run-test-target-template",
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
srcs: ["src/**/*.java"],
data: [
":art-run-test-2247-checker-write-barrier-elimination-expected-stdout",
diff --git a/test/knownfailures.json b/test/knownfailures.json
index f6c0e25ebe..91cf968413 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1,5 +1,10 @@
[
{
+ "tests": "2247-checker-write-barrier-elimination",
+ "description": ["Disable 2247- until we fix the WBE issue."],
+ "bug": "http://b/310755375"
+ },
+ {
"tests": "153-reference-stress",
"description": ["Disable 153-reference-stress temporarily until a fix",
"arrives."],
diff --git a/test/utils/regen-test-files b/test/utils/regen-test-files
index d047c2354a..64846a7ef9 100755
--- a/test/utils/regen-test-files
+++ b/test/utils/regen-test-files
@@ -215,7 +215,9 @@ known_failing_tests = frozenset([
"993-breakpoints-non-debuggable",
"2243-single-step-default",
"2262-miranda-methods",
- "2262-default-conflict-methods"
+ "2262-default-conflict-methods",
+ # 2247-checker-write-barrier-elimination: Disabled while we investigate failures
+ "2247-checker-write-barrier-elimination"
])
known_failing_on_hwasan_tests = frozenset([
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index cac98cb131..41a06cd427 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -411,6 +411,6 @@ EOF
msginfo "Generating linkerconfig" "in $linkerconfig_out"
rm -rf $linkerconfig_out
mkdir -p $linkerconfig_out
- $ANDROID_HOST_OUT/bin/linkerconfig --target $linkerconfig_out --root $linkerconfig_root --vndk $platform_version
+ $ANDROID_HOST_OUT/bin/linkerconfig --target $linkerconfig_out --root $linkerconfig_root --vndk $platform_version --product_vndk $platform_version
msgnote "Don't be scared by \"Unable to access VNDK APEX\" message, it's not fatal"
fi