diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-05-23 15:41:01 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-05-23 15:41:01 +0000 |
commit | 0fe83a81a66492cb8c895bd19a4cb36def38212d (patch) | |
tree | a0459ea58fc12779b5ff4c362e138e7595394921 | |
parent | 86c80efbb0c0c347f4feb0a0d35cc2df0ddf16c5 (diff) | |
parent | d11c004a5df78a67e6e9021492a8e096de336db3 (diff) | |
download | art-build-tools-release.tar.gz |
Snap for 11880863 from d11c004a5df78a67e6e9021492a8e096de336db3 to build-tools-releasebuild-tools-release
Change-Id: I158c0b997bd9cc8e123afcccd47514c82810ca61
241 files changed, 5869 insertions, 3299 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index c5b3c9c18d..d43a1e90ba 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -3,10 +3,6 @@ check_generated_tests_up_to_date = tools/test_presubmit.py hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT} -# TODO(b/189484095): Port libnativebridge tests to atest and enable in presubmit -# so we don't need the custom runtests script and this check. -check_libnativebridge_test_field = libnativebridge/tests/preupload_check_test_tag.sh ${PREUPLOAD_COMMIT_MESSAGE} ${PREUPLOAD_FILES} - check_expectation_jsons = tools/check_presubmit_json_expectations.sh ${REPO_ROOT} ${PREUPLOAD_FILES} [Builtin Hooks] diff --git a/TEST_MAPPING b/TEST_MAPPING index 3f5c50f694..3d144250fd 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -4305,6 +4305,9 @@ }, { "name": "art-run-test-2273-checker-unreachable-intrinsics" + }, + { + "name": "libnativebridge-tests" } ] } diff --git a/artd/Android.bp b/artd/Android.bp index cd8a5749c5..a2b256a019 100644 --- a/artd/Android.bp +++ b/artd/Android.bp @@ -125,7 +125,6 @@ cc_fuzz { name: "artd_fuzzer", defaults: [ "service_fuzzer_defaults", - "art_module_source_build_defaults", "artd_defaults", // Fuzzer is on a special variant, different from the APEX variant. When // linking against "libdexfile" as a shared library, the build system diff --git a/artd/artd.cc b/artd/artd.cc index 095e78e495..d87588bbc9 100644 --- a/artd/artd.cc +++ b/artd/artd.cc @@ -116,7 +116,6 @@ using ::android::base::make_scope_guard; using ::android::base::ReadFileToString; using ::android::base::Result; using ::android::base::Split; -using ::android::base::StringReplace; using ::android::base::Tokenize; using ::android::base::WriteStringToFd; using ::android::base::WriteStringToFile; @@ -446,8 +445,11 @@ Result<File> ExtractEmbeddedProfileToFd(const std::string& dex_path) { // Reopen the memfd with readonly to make SELinux happy when the fd is passed to a child process // who doesn't have write permission. (b/303909581) std::string path = ART_FORMAT("/proc/self/fd/{}", memfd.Fd()); - File memfd_readonly( - open(path.c_str(), O_RDONLY), memfd_name, /*check_usage=*/false, /*read_only_mode=*/true); + // NOLINTNEXTLINE - O_CLOEXEC is omitted on purpose + File memfd_readonly(open(path.c_str(), O_RDONLY), + memfd_name, + /*check_usage=*/false, + /*read_only_mode=*/true); if (!memfd_readonly.IsOpened()) { return ErrnoErrorf("Failed to open file '{}' ('{}')", path, memfd_name); } @@ -485,19 +487,19 @@ std::ostream& operator<<(std::ostream& os, const FdLogger& fd_logger) { } // namespace #define RETURN_FATAL_IF_PRE_REBOOT(options) \ - if (options.is_pre_reboot) { \ + if ((options).is_pre_reboot) { \ return Fatal("This method is not supported in Pre-reboot Dexopt mode"); \ } #define RETURN_FATAL_IF_NOT_PRE_REBOOT(options) \ - if (!options.is_pre_reboot) { \ + if (!(options).is_pre_reboot) { \ return Fatal("This method is only supported in Pre-reboot Dexopt mode"); \ } #define RETURN_FATAL_IF_ARG_IS_PRE_REBOOT_IMPL(expected, arg, log_name) \ { \ auto&& __return_fatal_tmp = PreRebootFlag(arg); \ - if (expected != __return_fatal_tmp) { \ + if ((expected) != __return_fatal_tmp) { \ return Fatal(ART_FORMAT("Expected flag 'isPreReboot' in argument '{}' to be {}, got {}", \ log_name, \ expected, \ @@ -506,7 +508,7 @@ std::ostream& operator<<(std::ostream& os, const FdLogger& fd_logger) { } #define RETURN_FATAL_IF_PRE_REBOOT_MISMATCH(options, arg, log_name) \ - RETURN_FATAL_IF_ARG_IS_PRE_REBOOT_IMPL(options.is_pre_reboot, arg, log_name) + RETURN_FATAL_IF_ARG_IS_PRE_REBOOT_IMPL((options).is_pre_reboot, arg, log_name) #define RETURN_FATAL_IF_ARG_IS_PRE_REBOOT(arg, log_name) \ RETURN_FATAL_IF_ARG_IS_PRE_REBOOT_IMPL(false, arg, log_name) @@ -606,8 +608,7 @@ ndk::ScopedAStatus Artd::isProfileUsable(const ProfilePath& in_profile, FdLogger fd_logger; - CmdlineBuilder art_exec_args; - art_exec_args.Add(OR_RETURN_FATAL(GetArtExec())).Add("--drop-capabilities"); + CmdlineBuilder art_exec_args = OR_RETURN_FATAL(GetArtExecCmdlineBuilder()); CmdlineBuilder args; args.Add(OR_RETURN_FATAL(GetProfman())); @@ -659,8 +660,7 @@ ndk::ScopedAStatus Artd::CopyAndRewriteProfileImpl(File src, FdLogger fd_logger; - CmdlineBuilder art_exec_args; - art_exec_args.Add(OR_RETURN_FATAL(GetArtExec())).Add("--drop-capabilities"); + CmdlineBuilder art_exec_args = OR_RETURN_FATAL(GetArtExecCmdlineBuilder()); CmdlineBuilder args; args.Add(OR_RETURN_FATAL(GetProfman())).Add("--copy-and-update-profile-key"); @@ -833,8 +833,7 @@ ndk::ScopedAStatus Artd::mergeProfiles(const std::vector<ProfilePath>& in_profil FdLogger fd_logger; - CmdlineBuilder art_exec_args; - art_exec_args.Add(OR_RETURN_FATAL(GetArtExec())).Add("--drop-capabilities"); + CmdlineBuilder art_exec_args = OR_RETURN_FATAL(GetArtExecCmdlineBuilder()); CmdlineBuilder args; args.Add(OR_RETURN_FATAL(GetProfman())); @@ -1028,8 +1027,7 @@ ndk::ScopedAStatus Artd::dexopt( FdLogger fd_logger; - CmdlineBuilder art_exec_args; - art_exec_args.Add(OR_RETURN_FATAL(GetArtExec())).Add("--drop-capabilities"); + CmdlineBuilder art_exec_args = OR_RETURN_FATAL(GetArtExecCmdlineBuilder()); CmdlineBuilder args; args.Add(OR_RETURN_FATAL(GetDex2Oat())); @@ -1250,6 +1248,7 @@ ScopedAStatus Artd::cleanup(const std::vector<ProfilePath>& in_profilesToKeep, const std::vector<ArtifactsPath>& in_artifactsToKeep, const std::vector<VdexPath>& in_vdexFilesToKeep, const std::vector<RuntimeArtifactsPath>& in_runtimeArtifactsToKeep, + bool in_keepPreRebootStagedFiles, int64_t* _aidl_return) { RETURN_FATAL_IF_PRE_REBOOT(options_); std::unordered_set<std::string> files_to_keep; @@ -1278,7 +1277,8 @@ ScopedAStatus Artd::cleanup(const std::vector<ProfilePath>& in_profilesToKeep, } *_aidl_return = 0; for (const std::string& file : ListManagedFiles(android_data, android_expand)) { - if (files_to_keep.find(file) == files_to_keep.end()) { + if (files_to_keep.find(file) == files_to_keep.end() && + (!in_keepPreRebootStagedFiles || !IsPreRebootStagedFile(file))) { LOG(INFO) << ART_FORMAT("Cleaning up obsolete file '{}'", file); *_aidl_return += GetSizeAndDeleteFile(file); } @@ -1547,7 +1547,13 @@ bool Artd::DenyArtApexDataFilesLocked() { Result<std::string> Artd::GetProfman() { return BuildArtBinPath("profman"); } -Result<std::string> Artd::GetArtExec() { return BuildArtBinPath("art_exec"); } +Result<CmdlineBuilder> Artd::GetArtExecCmdlineBuilder() { + CmdlineBuilder args; + args.Add(OR_RETURN(BuildArtBinPath("art_exec"))) + .Add("--drop-capabilities") + .AddIf(options_.is_pre_reboot, "--process-name-suffix=Pre-reboot Dexopt chroot"); + return args; +} bool Artd::ShouldUseDex2Oat64() { return !props_->GetOrEmpty("ro.product.cpu.abilist64").empty() && @@ -1780,10 +1786,8 @@ Result<void> Artd::PreRebootInitDeriveClasspath(const std::string& path) { return ErrnoErrorf("Failed to create '{}'", path); } - CmdlineBuilder args; - args.Add(OR_RETURN(GetArtExec())) - .Add("--drop-capabilities") - .Add("--keep-fds=%d", output->Fd()) + CmdlineBuilder args = OR_RETURN(GetArtExecCmdlineBuilder()); + args.Add("--keep-fds=%d", output->Fd()) .Add("--") .Add("/apex/com.android.sdkext/bin/derive_classpath") .Add("/proc/self/fd/%d", output->Fd()); @@ -1809,10 +1813,8 @@ Result<void> Artd::PreRebootInitDeriveClasspath(const std::string& path) { } Result<void> Artd::PreRebootInitBootImages() { - CmdlineBuilder args; - args.Add(OR_RETURN(GetArtExec())) - .Add("--drop-capabilities") - .Add("--") + CmdlineBuilder args = OR_RETURN(GetArtExecCmdlineBuilder()); + args.Add("--") .Add(OR_RETURN(BuildArtBinPath("odrefresh"))) .Add("--only-boot-images") .Add("--compile"); diff --git a/artd/artd.h b/artd/artd.h index 37b6d0c42f..a926dbc652 100644 --- a/artd/artd.h +++ b/artd/artd.h @@ -199,6 +199,7 @@ class Artd : public aidl::com::android::server::art::BnArtd { const std::vector<aidl::com::android::server::art::VdexPath>& in_vdexFilesToKeep, const std::vector<aidl::com::android::server::art::RuntimeArtifactsPath>& in_runtimeArtifactsToKeep, + bool in_keepPreRebootStagedFiles, int64_t* _aidl_return) override; ndk::ScopedAStatus isInDalvikCache(const std::string& in_dexFile, bool* _aidl_return) override; @@ -262,7 +263,7 @@ class Artd : public aidl::com::android::server::art::BnArtd { android::base::Result<std::string> GetProfman(); - android::base::Result<std::string> GetArtExec(); + android::base::Result<tools::CmdlineBuilder> GetArtExecCmdlineBuilder(); bool ShouldUseDex2Oat64(); diff --git a/artd/artd_test.cc b/artd/artd_test.cc index 858fd9909b..1c71ab2fbd 100644 --- a/artd/artd_test.cc +++ b/artd/artd_test.cc @@ -2094,138 +2094,165 @@ TEST_F(ArtdTest, mergeProfilesWithOptionsDumpClassesAndMethods) { CheckContent(output_profile.profilePath.tmpPath, "dump"); } -TEST_F(ArtdTest, cleanup) { - std::vector<std::string> gc_removed_files; - std::vector<std::string> gc_kept_files; +class ArtdCleanupTest : public ArtdTest { + protected: + void SetUp() override { + ArtdTest::SetUp(); - auto CreateGcRemovedFile = [&](const std::string& path) { + // Unmanaged files. + CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/1.odex"); + CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/1.odex"); + CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/1.txt"); + CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.txt"); + CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.tmp"); + + // Files to keep. + CreateGcKeptFile(android_data_ + "/misc/profiles/cur/1/com.android.foo/primary.prof"); + CreateGcKeptFile(android_data_ + "/misc/profiles/cur/3/com.android.foo/primary.prof"); + CreateGcKeptFile(android_data_ + "/dalvik-cache/arm64/system@app@Foo@Foo.apk@classes.dex"); + CreateGcKeptFile(android_data_ + "/dalvik-cache/arm64/system@app@Foo@Foo.apk@classes.vdex"); + CreateGcKeptFile(android_data_ + "/dalvik-cache/arm64/system@app@Foo@Foo.apk@classes.art"); + CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/1.vdex"); + CreateGcKeptFile( + android_expand_ + + "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.odex"); + CreateGcKeptFile( + android_expand_ + + "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.vdex"); + CreateGcKeptFile( + android_expand_ + + "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.art"); + CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.odex"); + CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.vdex"); + CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.art"); + CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/cache/oat_primary/arm64/base.art"); + CreateGcKeptFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/arm64/base.art"); + CreateGcKeptFile(android_data_ + "/user/1/com.android.foo/cache/oat_primary/arm64/base.art"); + CreateGcKeptFile(android_expand_ + + "/123456-7890/user/1/com.android.foo/cache/oat_primary/arm64/base.art"); + CreateGcKeptFile(android_data_ + + "/user/0/com.android.foo/cache/not_oat_dir/oat_primary/arm64/base.art"); + + // Files to remove. + CreateGcRemovedFile(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof"); + CreateGcRemovedFile(android_data_ + "/misc/profiles/cur/2/com.android.foo/primary.prof"); + CreateGcRemovedFile(android_data_ + "/misc/profiles/cur/3/com.android.bar/primary.prof"); + CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/extra.odex"); + CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/system@app@Bar@Bar.apk@classes.dex"); + CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/system@app@Bar@Bar.apk@classes.vdex"); + CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/system@app@Bar@Bar.apk@classes.art"); + CreateGcRemovedFile( + android_expand_ + + "/123456-7890/app/~~daewfweaf==/com.android.foo-fjuwidhia==/oat/arm64/base.odex"); + CreateGcRemovedFile( + android_expand_ + + "/123456-7890/app/~~daewfweaf==/com.android.foo-fjuwidhia==/oat/arm64/base.vdex"); + CreateGcRemovedFile( + android_expand_ + + "/123456-7890/app/~~daewfweaf==/com.android.foo-fjuwidhia==/oat/arm64/base.art"); + CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/1.prof"); + CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/1.prof.123456.tmp"); + CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.odex"); + CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.vdex"); + CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.art"); + CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.odex.123456.tmp"); + CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/2.odex.123456.tmp"); + CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/1.odex"); + CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/1.art"); + CreateGcRemovedFile(android_data_ + + "/user_de/0/com.android.foo/aaa/oat/arm64/1.vdex.123456.tmp"); + CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.odex"); + CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.vdex"); + CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.art"); + CreateGcRemovedFile(android_data_ + + "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.art.123456.tmp"); + CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.bar/aaa/oat/arm64/1.vdex"); + CreateGcRemovedFile(android_data_ + + "/user/0/com.android.different_package/cache/oat_primary/arm64/base.art"); + CreateGcRemovedFile(android_data_ + + "/user/0/com.android.foo/cache/oat_primary/arm64/different_dex.art"); + CreateGcRemovedFile(android_data_ + + "/user/0/com.android.foo/cache/oat_primary/different_isa/base.art"); + } + + void CreateGcRemovedFile(const std::string& path) { CreateFile(path); - gc_removed_files.push_back(path); - }; + gc_removed_files_.push_back(path); + } - auto CreateGcKeptFile = [&](const std::string& path) { + void CreateGcKeptFile(const std::string& path) { CreateFile(path); - gc_kept_files.push_back(path); - }; + gc_kept_files_.push_back(path); + } - // Unmanaged files. - CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/1.odex"); - CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/1.odex"); - CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/1.txt"); - CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.txt"); - CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.tmp"); - - // Files to keep. - CreateGcKeptFile(android_data_ + "/misc/profiles/cur/1/com.android.foo/primary.prof"); - CreateGcKeptFile(android_data_ + "/misc/profiles/cur/3/com.android.foo/primary.prof"); - CreateGcKeptFile(android_data_ + "/dalvik-cache/arm64/system@app@Foo@Foo.apk@classes.dex"); - CreateGcKeptFile(android_data_ + "/dalvik-cache/arm64/system@app@Foo@Foo.apk@classes.vdex"); - CreateGcKeptFile(android_data_ + "/dalvik-cache/arm64/system@app@Foo@Foo.apk@classes.art"); - CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/1.vdex"); - CreateGcKeptFile( - android_expand_ + - "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.odex"); - CreateGcKeptFile( - android_expand_ + - "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.vdex"); + void RunCleanup(bool keepPreRebootStagedFiles) { + int64_t aidl_return; + ASSERT_STATUS_OK(artd_->cleanup( + { + PrimaryCurProfilePath{ + .userId = 1, .packageName = "com.android.foo", .profileName = "primary"}, + PrimaryCurProfilePath{ + .userId = 3, .packageName = "com.android.foo", .profileName = "primary"}, + }, + { + ArtifactsPath{ + .dexPath = "/system/app/Foo/Foo.apk", .isa = "arm64", .isInDalvikCache = true}, + ArtifactsPath{ + .dexPath = android_expand_ + + "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/base.apk", + .isa = "arm64", + .isInDalvikCache = false}, + ArtifactsPath{.dexPath = android_data_ + "/user_de/0/com.android.foo/aaa/2.apk", + .isa = "arm64", + .isInDalvikCache = false}, + }, + { + VdexPath{ + ArtifactsPath{.dexPath = android_data_ + "/user_de/0/com.android.foo/aaa/1.apk", + .isa = "arm64", + .isInDalvikCache = false}}, + }, + { + RuntimeArtifactsPath{ + .packageName = "com.android.foo", .dexPath = "/a/b/base.apk", .isa = "arm64"}, + }, + keepPreRebootStagedFiles, + &aidl_return)); + } + + void Verify() { + for (const std::string& path : gc_removed_files_) { + EXPECT_FALSE(std::filesystem::exists(path)) << ART_FORMAT("'{}' should be removed", path); + } + + for (const std::string& path : gc_kept_files_) { + EXPECT_TRUE(std::filesystem::exists(path)) << ART_FORMAT("'{}' should be kept", path); + } + } + + private: + std::vector<std::string> gc_removed_files_; + std::vector<std::string> gc_kept_files_; +}; + +TEST_F(ArtdCleanupTest, cleanupKeepingPreRebootStagedFiles) { CreateGcKeptFile( android_expand_ + - "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.art"); - CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.odex"); - CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.vdex"); - CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.art"); - CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/cache/oat_primary/arm64/base.art"); - CreateGcKeptFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/arm64/base.art"); - CreateGcKeptFile(android_data_ + "/user/1/com.android.foo/cache/oat_primary/arm64/base.art"); - CreateGcKeptFile(android_expand_ + - "/123456-7890/user/1/com.android.foo/cache/oat_primary/arm64/base.art"); - CreateGcKeptFile(android_data_ + - "/user/0/com.android.foo/cache/not_oat_dir/oat_primary/arm64/base.art"); - - // Files to remove. - CreateGcRemovedFile(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof"); - CreateGcRemovedFile(android_data_ + "/misc/profiles/cur/2/com.android.foo/primary.prof"); - CreateGcRemovedFile(android_data_ + "/misc/profiles/cur/3/com.android.bar/primary.prof"); - CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/extra.odex"); - CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/system@app@Bar@Bar.apk@classes.dex"); - CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/system@app@Bar@Bar.apk@classes.vdex"); - CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/system@app@Bar@Bar.apk@classes.art"); - CreateGcRemovedFile( - android_expand_ + - "/123456-7890/app/~~daewfweaf==/com.android.foo-fjuwidhia==/oat/arm64/base.odex"); - CreateGcRemovedFile( - android_expand_ + - "/123456-7890/app/~~daewfweaf==/com.android.foo-fjuwidhia==/oat/arm64/base.vdex"); - CreateGcRemovedFile( - android_expand_ + - "/123456-7890/app/~~daewfweaf==/com.android.foo-fjuwidhia==/oat/arm64/base.art"); - CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/1.prof"); - CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/1.prof.123456.tmp"); - CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.odex"); - CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.vdex"); - CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.art"); - CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.odex.123456.tmp"); - CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/2.odex.123456.tmp"); - CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/1.odex"); - CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/1.art"); - CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/1.vdex.123456.tmp"); - CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.odex"); - CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.vdex"); - CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.art"); - CreateGcRemovedFile(android_data_ + - "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.art.123456.tmp"); - CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.bar/aaa/oat/arm64/1.vdex"); - CreateGcRemovedFile(android_data_ + - "/user/0/com.android.different_package/cache/oat_primary/arm64/base.art"); - CreateGcRemovedFile(android_data_ + - "/user/0/com.android.foo/cache/oat_primary/arm64/different_dex.art"); - CreateGcRemovedFile(android_data_ + - "/user/0/com.android.foo/cache/oat_primary/different_isa/base.art"); + "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.odex.staged"); + CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.odex.staged"); - int64_t aidl_return; - ASSERT_TRUE( - artd_ - ->cleanup( - { - PrimaryCurProfilePath{ - .userId = 1, .packageName = "com.android.foo", .profileName = "primary"}, - PrimaryCurProfilePath{ - .userId = 3, .packageName = "com.android.foo", .profileName = "primary"}, - }, - { - ArtifactsPath{.dexPath = "/system/app/Foo/Foo.apk", - .isa = "arm64", - .isInDalvikCache = true}, - ArtifactsPath{ - .dexPath = - android_expand_ + - "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/base.apk", - .isa = "arm64", - .isInDalvikCache = false}, - ArtifactsPath{.dexPath = android_data_ + "/user_de/0/com.android.foo/aaa/2.apk", - .isa = "arm64", - .isInDalvikCache = false}, - }, - { - VdexPath{ArtifactsPath{ - .dexPath = android_data_ + "/user_de/0/com.android.foo/aaa/1.apk", - .isa = "arm64", - .isInDalvikCache = false}}, - }, - { - RuntimeArtifactsPath{ - .packageName = "com.android.foo", .dexPath = "/a/b/base.apk", .isa = "arm64"}, - }, - &aidl_return) - .isOk()); + ASSERT_NO_FATAL_FAILURE(RunCleanup(/*keepPreRebootStagedFiles=*/true)); + Verify(); +} - for (const std::string& path : gc_removed_files) { - EXPECT_FALSE(std::filesystem::exists(path)) << ART_FORMAT("'{}' should be removed", path); - } +TEST_F(ArtdCleanupTest, cleanupRemovingPreRebootStagedFiles) { + CreateGcRemovedFile( + android_expand_ + + "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.odex.staged"); + CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.odex.staged"); - for (const std::string& path : gc_kept_files) { - EXPECT_TRUE(std::filesystem::exists(path)) << ART_FORMAT("'{}' should be kept", path); - } + ASSERT_NO_FATAL_FAILURE(RunCleanup(/*keepPreRebootStagedFiles=*/false)); + Verify(); } TEST_F(ArtdTest, isInDalvikCache) { diff --git a/artd/binder/com/android/server/art/IArtd.aidl b/artd/binder/com/android/server/art/IArtd.aidl index 4532a8fb11..54f55f8460 100644 --- a/artd/binder/com/android/server/art/IArtd.aidl +++ b/artd/binder/com/android/server/art/IArtd.aidl @@ -188,7 +188,8 @@ interface IArtd { long cleanup(in List<com.android.server.art.ProfilePath> profilesToKeep, in List<com.android.server.art.ArtifactsPath> artifactsToKeep, in List<com.android.server.art.VdexPath> vdexFilesToKeep, - in List<com.android.server.art.RuntimeArtifactsPath> runtimeArtifactsToKeep); + in List<com.android.server.art.RuntimeArtifactsPath> runtimeArtifactsToKeep, + boolean keepPreRebootStagedFiles); /** * Returns whether the artifacts of the primary dex files should be in the global dalvik-cache diff --git a/artd/file_utils.cc b/artd/file_utils.cc index 195daf6162..c1977fb1bb 100644 --- a/artd/file_utils.cc +++ b/artd/file_utils.cc @@ -234,10 +234,13 @@ android::base::Result<void> MoveAllOrAbandon( const std::vector<std::pair<std::string, std::string>>& files_to_move, const std::vector<std::string>& files_to_remove) { std::vector<std::pair<std::string_view, std::string_view>> files_to_move_view; - std::vector<std::string_view> files_to_remove_view; + files_to_move_view.reserve(files_to_move.size()); for (const auto& [src, dst] : files_to_move) { files_to_move_view.emplace_back(src, dst); } + + std::vector<std::string_view> files_to_remove_view; + files_to_remove_view.reserve(files_to_remove.size()); for (const std::string& file : files_to_remove) { files_to_remove_view.emplace_back(file); } diff --git a/artd/path_utils.cc b/artd/path_utils.cc index 7cd9413dea..5c6ad9dc02 100644 --- a/artd/path_utils.cc +++ b/artd/path_utils.cc @@ -23,6 +23,7 @@ #include "aidl/com/android/server/art/BnArtd.h" #include "android-base/errors.h" #include "android-base/result.h" +#include "android-base/strings.h" #include "arch/instruction_set.h" #include "base/file_utils.h" #include "base/macros.h" @@ -44,6 +45,7 @@ using ::aidl::com::android::server::art::OutputProfile; using ::aidl::com::android::server::art::ProfilePath; using ::aidl::com::android::server::art::RuntimeArtifactsPath; using ::aidl::com::android::server::art::VdexPath; +using ::android::base::EndsWith; using ::android::base::Error; using ::android::base::Result; using ::art::service::ValidateDexPath; @@ -111,11 +113,11 @@ std::vector<std::string> ListManagedFiles(const std::string& android_data, // Profiles and artifacts for secondary dex files. Those files are in app data directories, so // we use more granular patterns to avoid accidentally deleting apps' files. std::string secondary_oat_dir = data_dir + "/**/oat"; - for (const char* maybe_tmp_suffix : {"", ".*.tmp"}) { - patterns.push_back(secondary_oat_dir + "/*.prof" + maybe_tmp_suffix); - patterns.push_back(secondary_oat_dir + "/*/*.odex" + maybe_tmp_suffix); - patterns.push_back(secondary_oat_dir + "/*/*.vdex" + maybe_tmp_suffix); - patterns.push_back(secondary_oat_dir + "/*/*.art" + maybe_tmp_suffix); + for (const char* suffix : {"", ".*.tmp", kPreRebootSuffix}) { + patterns.push_back(secondary_oat_dir + "/*.prof" + suffix); + patterns.push_back(secondary_oat_dir + "/*/*.odex" + suffix); + patterns.push_back(secondary_oat_dir + "/*/*.vdex" + suffix); + patterns.push_back(secondary_oat_dir + "/*/*.art" + suffix); } // Runtime image files. patterns.push_back(RuntimeImage::GetRuntimeImageDir(data_dir) + "**"); @@ -332,6 +334,10 @@ bool PreRebootFlag(const VdexPath& vdex_path) { return PreRebootFlag(vdex_path.get<VdexPath::artifactsPath>()); } +bool IsPreRebootStagedFile(std::string_view filename) { + return EndsWith(filename, kPreRebootSuffix); +} + void TestOnlySetListRootDir(std::string_view root_dir) { gListRootDir = root_dir; } } // namespace artd diff --git a/artd/path_utils.h b/artd/path_utils.h index 43a1043b42..1528d0610b 100644 --- a/artd/path_utils.h +++ b/artd/path_utils.h @@ -128,6 +128,8 @@ bool PreRebootFlag(const aidl::com::android::server::art::ArtifactsPath& artifac bool PreRebootFlag(const aidl::com::android::server::art::OutputArtifacts& artifacts); bool PreRebootFlag(const aidl::com::android::server::art::VdexPath& vdex_path); +bool IsPreRebootStagedFile(std::string_view filename); + // Sets the root dir for `ListManagedFiles` and `ListRuntimeImageFiles`. // The passed string must be alive until the test ends. // For testing use only. diff --git a/build/Android.bp b/build/Android.bp index 13dd43b257..9cc44489c6 100644 --- a/build/Android.bp +++ b/build/Android.bp @@ -80,15 +80,10 @@ soong_config_module_type_import { from: "art/build/SoongConfig.bp", module_types: [ "art_debug_defaults", - "art_module_art_global_defaults", - "art_module_cc_defaults", - "art_module_java_defaults", - "art_module_genrule_defaults", - "art_module_prebuilt_defaults", ], } -art_module_art_global_defaults { +art_global_defaults { // Additional flags are computed by art.go name: "art_defaults", @@ -368,69 +363,3 @@ java_library { }, }, } - -// Defaults for different module types to enable them only when building ART -// from sources. TODO(b/172480617): Clean up when sources are gone from the -// platform tree and we no longer need to support sources present when prebuilts -// are used. -// TODO(b/335854415): Remove the defaults module -art_module_cc_defaults { - name: "art_module_source_build_defaults", - defaults_visibility: [ - "//art:__subpackages__", - "//libcore:__subpackages__", - "//libnativehelper:__subpackages__", - ], - target: { - windows: { - // Windows is disabled by default, but if we set enabled:true - // globally above we need to disable it explicitly. - enabled: false, - }, - }, -} - -// TODO(b/335854415): Remove the defaults module -art_module_java_defaults { - name: "art_module_source_build_java_defaults", - defaults_visibility: [ - "//art:__subpackages__", - "//libcore:__subpackages__", - "//libnativehelper:__subpackages__", - ], - target: { - windows: { - enabled: false, - }, - }, -} - -// TODO(b/335854415): Remove the defaults module -art_module_genrule_defaults { - name: "art_module_source_build_genrule_defaults", - defaults_visibility: [ - "//art:__subpackages__", - "//libcore:__subpackages__", - "//libnativehelper:__subpackages__", - ], - target: { - windows: { - enabled: false, - }, - }, -} - -// TODO(b/335854415): Remove the defaults module -art_module_prebuilt_defaults { - name: "art_module_source_build_prebuilt_defaults", - defaults_visibility: [ - "//art:__subpackages__", - "//libcore:__subpackages__", - "//libnativehelper:__subpackages__", - ], - target: { - windows: { - enabled: false, - }, - }, -} diff --git a/build/SoongConfig.bp b/build/SoongConfig.bp index 24347a8219..d97349cc0e 100644 --- a/build/SoongConfig.bp +++ b/build/SoongConfig.bp @@ -1,95 +1,6 @@ // Set up Soong config variables. // https://android.googlesource.com/platform/build/soong/+/master/README.md#soong-config-variables -// art/ modules are no longer disabled even when prebuilt art apex and prebuilt module sdk is used. -// TODO(b/335854415): Remove the defaults module. -soong_config_bool_variable { - name: "source_build", -} - -soong_config_module_type { - name: "art_module_art_global_defaults", - module_type: "art_global_defaults", - config_namespace: "art_module", - bool_variables: ["source_build"], - properties: ["enabled"], -} - -soong_config_module_type { - name: "art_module_apex_defaults", - module_type: "apex_defaults", - config_namespace: "art_module", - bool_variables: ["source_build"], - properties: ["enabled"], -} - -soong_config_module_type { - name: "art_module_cc_defaults", - module_type: "cc_defaults", - config_namespace: "art_module", - bool_variables: ["source_build"], - properties: [ - "enabled", - "target.android.test_for", - ], -} - -soong_config_module_type { - name: "art_module_cc_genrule", - module_type: "cc_genrule", - config_namespace: "art_module", - bool_variables: ["source_build"], - properties: ["enabled"], -} - -soong_config_module_type { - name: "art_module_exports", - module_type: "module_exports", - config_namespace: "art_module", - bool_variables: ["source_build"], - properties: ["enabled"], -} - -soong_config_module_type { - name: "art_module_java_defaults", - module_type: "java_defaults", - config_namespace: "art_module", - bool_variables: ["source_build"], - properties: ["enabled"], -} - -soong_config_module_type { - name: "art_module_genrule_defaults", - module_type: "genrule_defaults", - config_namespace: "art_module", - bool_variables: ["source_build"], - properties: ["enabled"], -} - -soong_config_module_type { - name: "art_module_prebuilt_defaults", - module_type: "prebuilt_defaults", - config_namespace: "art_module", - bool_variables: ["source_build"], - properties: ["enabled"], -} - -soong_config_module_type { - name: "art_module_sdk", - module_type: "sdk", - config_namespace: "art_module", - bool_variables: ["source_build"], - properties: ["enabled"], -} - -soong_config_module_type { - name: "art_module_sh_binary", - module_type: "sh_binary", - config_namespace: "art_module", - bool_variables: ["source_build"], - properties: ["enabled"], -} - soong_config_module_type { name: "art_debug_defaults", module_type: "cc_defaults", diff --git a/build/apex/Android.bp b/build/apex/Android.bp index 06dc586723..7c2721f44b 100644 --- a/build/apex/Android.bp +++ b/build/apex/Android.bp @@ -12,14 +12,6 @@ package { default_applicable_licenses: ["art_license"], } -soong_config_module_type_import { - from: "art/build/SoongConfig.bp", - module_types: [ - "art_module_apex_defaults", - "art_module_cc_defaults", - ], -} - // Binaries for which both 32- and 64-bit versions are built, if possible. art_runtime_binaries_both = [ "dalvikvm", @@ -168,7 +160,7 @@ apex_key { } // Default shared by all ART APEXes. -art_module_apex_defaults { +apex_defaults { name: "com.android.art-base-defaults", target: { @@ -411,7 +403,6 @@ art_check_apex_gen_stem = "$(location art-apex-tester)" + // (even when APEX flattening is enabled). genrule_defaults { name: "art-check-apex-gen-defaults", - defaults: ["art_module_source_build_genrule_defaults"], tools: [ "art-apex-tester", "deapexer", @@ -420,7 +411,7 @@ genrule_defaults { ], } -art_module_cc_defaults { +cc_defaults { name: "art-check-apex-gen-fakebin-defaults", host_supported: true, device_supported: false, @@ -499,7 +490,6 @@ linker_config { // 1 variant, we can then depend on it from a different type of genrule like regular genrule. sdk_genrule { name: "art-module-host-exports-for-genrule", - defaults: ["art_module_source_build_genrule_defaults"], srcs: [":art-module-host-exports"], out: ["art-module-host-exports-current.zip"], cmd: "cp $(in) $(out)", @@ -509,7 +499,6 @@ sdk_genrule { // At the time of writing, this is only for Compiler Explorer (https://godbolt.org). genrule { name: "art_release_zip", - defaults: ["art_module_source_build_genrule_defaults"], srcs: [ ":art-module-host-exports-for-genrule", ":com.android.art", diff --git a/build/apex/art.rc b/build/apex/art.rc index bab7fe30a3..8a98a5f670 100644 --- a/build/apex/art.rc +++ b/build/apex/art.rc @@ -26,7 +26,12 @@ service artd /apex/com.android.art/bin/artd # Same as above, but for Pre-reboot Dexopt. It runs in a chroot environment that # is set up by dexopt_chroot_setup. It's a lazy service that is started and # stopped dynamically as needed. -service artd_pre_reboot /apex/com.android.art/bin/art_exec --chroot=/mnt/pre_reboot_dexopt/chroot -- /apex/com.android.art/bin/artd --pre-reboot +service artd_pre_reboot /apex/com.android.art/bin/art_exec \ + --chroot=/mnt/pre_reboot_dexopt/chroot \ + --process-name-suffix="Pre-reboot Dexopt chroot" \ + -- \ + /apex/com.android.art/bin/artd \ + --pre-reboot interface aidl artd_pre_reboot disabled # Prevents the service from automatically starting at boot. oneshot # Prevents the service from automatically restarting each time it is stopped. diff --git a/build/sdk/Android.bp b/build/sdk/Android.bp index 1db42aed04..6944f0d263 100644 --- a/build/sdk/Android.bp +++ b/build/sdk/Android.bp @@ -21,14 +21,6 @@ package { default_applicable_licenses: ["art_license"], } -soong_config_module_type_import { - from: "art/build/SoongConfig.bp", - module_types: [ - "art_module_sdk", - "art_module_exports", - ], -} - // Additional visibility to add to the prebuilt modules that are part of // the snapshots of the ART sdk/module_exports to ensure that they are // visible to each other. @@ -39,7 +31,7 @@ prebuilt_visibility = [ ] // The SDK for the art module apex. -art_module_sdk { +sdk { name: "art-module-sdk", host_supported: true, @@ -140,7 +132,7 @@ art_module_sdk { } // Exported host tools and libraries. -art_module_exports { +module_exports { name: "art-module-host-exports", host_supported: true, @@ -188,7 +180,7 @@ art_module_exports { } // Exported tests and supporting libraries -art_module_exports { +module_exports { name: "art-module-test-exports", prebuilt_visibility: prebuilt_visibility, diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc index 87dbfee412..8b415c641d 100644 --- a/compiler/driver/compiler_options.cc +++ b/compiler/driver/compiler_options.cc @@ -135,8 +135,8 @@ bool CompilerOptions::IsImageClass(const char* descriptor) const { return image_classes_.find(std::string_view(descriptor)) != image_classes_.end(); } -bool CompilerOptions::IsPreloadedClass(const char* pretty_descriptor) const { - return preloaded_classes_.find(std::string_view(pretty_descriptor)) != preloaded_classes_.end(); +bool CompilerOptions::IsPreloadedClass(std::string_view pretty_descriptor) const { + return preloaded_classes_.find(pretty_descriptor) != preloaded_classes_.end(); } bool CompilerOptions::ShouldCompileWithClinitCheck(ArtMethod* method) const { diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index abdb01b372..36ecf88199 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -292,7 +292,7 @@ class CompilerOptions final { // Returns whether the given `pretty_descriptor` is in the list of preloaded // classes. `pretty_descriptor` should be the result of calling `PrettyDescriptor`. - EXPORT bool IsPreloadedClass(const char* pretty_descriptor) const; + EXPORT bool IsPreloadedClass(std::string_view pretty_descriptor) const; bool ParseCompilerOptions(const std::vector<std::string>& options, bool ignore_unrecognized, diff --git a/compiler/driver/compiler_options_map.h b/compiler/driver/compiler_options_map.h index b2dd57d00e..136af36096 100644 --- a/compiler/driver/compiler_options_map.h +++ b/compiler/driver/compiler_options_map.h @@ -42,7 +42,7 @@ struct CompilerOptionsMap : VariantMap<Base, KeyType> { #include "compiler_options_map.def" }; -#undef DECLARE_KEY +#undef COMPILER_OPTIONS_KEY } // namespace art diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h index 19ee0e640c..b061e042f0 100644 --- a/compiler/linker/linker_patch.h +++ b/compiler/linker/linker_patch.h @@ -54,6 +54,7 @@ class LinkerPatch { kJniEntrypointRelative, kCallRelative, kTypeRelative, + kTypeAppImageRelRo, kTypeBssEntry, kPublicTypeBssEntry, kPackageTypeBssEntry, @@ -130,6 +131,16 @@ class LinkerPatch { return patch; } + static LinkerPatch TypeAppImageRelRoPatch(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t target_type_idx) { + LinkerPatch patch(literal_offset, Type::kTypeAppImageRelRo, target_dex_file); + patch.type_idx_ = target_type_idx; + patch.pc_insn_offset_ = pc_insn_offset; + return patch; + } + static LinkerPatch TypeBssEntryPatch(size_t literal_offset, const DexFile* target_dex_file, uint32_t pc_insn_offset, @@ -241,6 +252,7 @@ class LinkerPatch { TypeReference TargetType() const { DCHECK(patch_type_ == Type::kTypeRelative || + patch_type_ == Type::kTypeAppImageRelRo || patch_type_ == Type::kTypeBssEntry || patch_type_ == Type::kPublicTypeBssEntry || patch_type_ == Type::kPackageTypeBssEntry); @@ -265,6 +277,7 @@ class LinkerPatch { patch_type_ == Type::kMethodBssEntry || patch_type_ == Type::kJniEntrypointRelative || patch_type_ == Type::kTypeRelative || + patch_type_ == Type::kTypeAppImageRelRo || patch_type_ == Type::kTypeBssEntry || patch_type_ == Type::kPublicTypeBssEntry || patch_type_ == Type::kPackageTypeBssEntry || diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 3ac4bd7dcc..aec7b45a1a 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -59,15 +59,15 @@ static int32_t constexpr kPrimIntMax = 0x7fffffff; // Maximum value for a primitive long. static int64_t constexpr kPrimLongMax = INT64_C(0x7fffffffffffffff); -constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf(); -constexpr size_t status_byte_offset = - mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte); -constexpr uint32_t shifted_visibly_initialized_value = - enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << (status_lsb_position % kBitsPerByte); -constexpr uint32_t shifted_initializing_value = - enum_cast<uint32_t>(ClassStatus::kInitializing) << (status_lsb_position % kBitsPerByte); -constexpr uint32_t shifted_initialized_value = - enum_cast<uint32_t>(ClassStatus::kInitialized) << (status_lsb_position % kBitsPerByte); +constexpr size_t kClassStatusLsbPosition = SubtypeCheckBits::BitStructSizeOf(); +constexpr size_t kClassStatusByteOffset = + mirror::Class::StatusOffset().SizeValue() + (kClassStatusLsbPosition / kBitsPerByte); +constexpr uint32_t kShiftedVisiblyInitializedValue = enum_cast<uint32_t>( + ClassStatus::kVisiblyInitialized) << (kClassStatusLsbPosition % kBitsPerByte); +constexpr uint32_t kShiftedInitializingValue = + enum_cast<uint32_t>(ClassStatus::kInitializing) << (kClassStatusLsbPosition % kBitsPerByte); +constexpr uint32_t kShiftedInitializedValue = + enum_cast<uint32_t>(ClassStatus::kInitialized) << (kClassStatusLsbPosition % kBitsPerByte); class Assembler; class CodeGenerationData; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index cfa28eda25..226e5343e2 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1011,6 +1011,7 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + app_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), public_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), package_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), @@ -1349,18 +1350,18 @@ void CodeGeneratorARM64::GenerateFrameEntry() { // We don't emit a read barrier here to save on code size. We rely on the // resolution trampoline to do a suspend check before re-entering this code. __ Ldr(temp1, MemOperand(kArtMethodRegister, ArtMethod::DeclaringClassOffset().Int32Value())); - __ Ldrb(temp2, HeapOperand(temp1, status_byte_offset)); - __ Cmp(temp2, shifted_visibly_initialized_value); + __ Ldrb(temp2, HeapOperand(temp1, kClassStatusByteOffset)); + __ Cmp(temp2, kShiftedVisiblyInitializedValue); __ B(hs, &frame_entry_label_); // Check if we're initialized and jump to code that does a memory barrier if // so. - __ Cmp(temp2, shifted_initialized_value); + __ Cmp(temp2, kShiftedInitializedValue); __ B(hs, &memory_barrier); // Check if we're initializing and the thread initializing is the one // executing the code. - __ Cmp(temp2, shifted_initializing_value); + __ Cmp(temp2, kShiftedInitializingValue); __ B(lo, &resolution); __ Ldr(temp1, HeapOperand(temp1, mirror::Class::ClinitThreadIdOffset().Int32Value())); @@ -2079,8 +2080,8 @@ void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCod // size, load only the high byte of the field and compare with 0xf0. // Note: The same code size could be achieved with LDR+MNV(asr #24)+CBNZ but benchmarks // show that this pattern is slower (tested on little cores). - __ Ldrb(temp, HeapOperand(class_reg, status_byte_offset)); - __ Cmp(temp, shifted_visibly_initialized_value); + __ Ldrb(temp, HeapOperand(class_reg, kClassStatusByteOffset)); + __ Cmp(temp, kShiftedVisiblyInitializedValue); __ B(lo, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); } @@ -5155,6 +5156,13 @@ vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageTypePatch( return NewPcRelativePatch(&dex_file, type_index.index_, adrp_label, &boot_image_type_patches_); } +vixl::aarch64::Label* CodeGeneratorARM64::NewAppImageTypePatch( + const DexFile& dex_file, + dex::TypeIndex type_index, + vixl::aarch64::Label* adrp_label) { + return NewPcRelativePatch(&dex_file, type_index.index_, adrp_label, &app_image_type_patches_); +} + vixl::aarch64::Label* CodeGeneratorARM64::NewBssEntryTypePatch( HLoadClass* load_class, vixl::aarch64::Label* adrp_label) { @@ -5365,6 +5373,7 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* lin boot_image_method_patches_.size() + method_bss_entry_patches_.size() + boot_image_type_patches_.size() + + app_image_type_patches_.size() + type_bss_entry_patches_.size() + public_type_bss_entry_patches_.size() + package_type_bss_entry_patches_.size() + @@ -5387,12 +5396,15 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* lin DCHECK(boot_image_type_patches_.empty()); DCHECK(boot_image_string_patches_.empty()); } + DCHECK_IMPLIES(!GetCompilerOptions().IsAppImage(), app_image_type_patches_.empty()); if (GetCompilerOptions().IsBootImage()) { EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::IntrinsicReferencePatch>>( boot_image_other_patches_, linker_patches); } else { EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::BootImageRelRoPatch>>( boot_image_other_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeAppImageRelRoPatch>( + app_image_type_patches_, linker_patches); } EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>( method_bss_entry_patches_, linker_patches); @@ -5504,6 +5516,7 @@ HLoadClass::LoadKind CodeGeneratorARM64::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: case HLoadClass::LoadKind::kBootImageRelRo: + case HLoadClass::LoadKind::kAppImageRelRo: case HLoadClass::LoadKind::kBssEntry: case HLoadClass::LoadKind::kBssEntryPublic: case HLoadClass::LoadKind::kBssEntryPackage: @@ -5613,6 +5626,20 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA codegen_->LoadBootImageRelRoEntry(out.W(), boot_image_offset); break; } + case HLoadClass::LoadKind::kAppImageRelRo: { + DCHECK(codegen_->GetCompilerOptions().IsAppImage()); + DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); + // Add ADRP with its PC-relative type patch. + const DexFile& dex_file = cls->GetDexFile(); + dex::TypeIndex type_index = cls->GetTypeIndex(); + vixl::aarch64::Label* adrp_label = codegen_->NewAppImageTypePatch(dex_file, type_index); + codegen_->EmitAdrpPlaceholder(adrp_label, out.X()); + // Add LDR with its PC-relative type patch. + vixl::aarch64::Label* ldr_label = + codegen_->NewAppImageTypePatch(dex_file, type_index, adrp_label); + codegen_->EmitLdrOffsetPlaceholder(ldr_label, out.W(), out.X()); + break; + } case HLoadClass::LoadKind::kBssEntry: case HLoadClass::LoadKind::kBssEntryPublic: case HLoadClass::LoadKind::kBssEntryPackage: { diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 32a69a9df7..78049c5675 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -817,6 +817,14 @@ class CodeGeneratorARM64 : public CodeGenerator { dex::TypeIndex type_index, vixl::aarch64::Label* adrp_label = nullptr); + // Add a new app image type patch for an instruction and return the label + // to be bound before the instruction. The instruction will be either the + // ADRP (pass `adrp_label = null`) or the LDR (pass `adrp_label` pointing + // to the associated ADRP patch label). + vixl::aarch64::Label* NewAppImageTypePatch(const DexFile& dex_file, + dex::TypeIndex type_index, + vixl::aarch64::Label* adrp_label = nullptr); + // Add a new .bss entry type patch for an instruction and return the label // to be bound before the instruction. The instruction will be either the // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing @@ -1150,6 +1158,8 @@ class CodeGeneratorARM64 : public CodeGenerator { ArenaDeque<PcRelativePatchInfo> method_bss_entry_patches_; // PC-relative type patch info for kBootImageLinkTimePcRelative. ArenaDeque<PcRelativePatchInfo> boot_image_type_patches_; + // PC-relative type patch info for kAppImageRelRo. + ArenaDeque<PcRelativePatchInfo> app_image_type_patches_; // PC-relative type patch info for kBssEntry. ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_; // PC-relative public type patch info for kBssEntryPublic. diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index a7cc5a6d12..979db6ed75 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -1946,6 +1946,7 @@ CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph, boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + app_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), public_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), package_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), @@ -2353,18 +2354,18 @@ void CodeGeneratorARMVIXL::GenerateFrameEntry() { // We don't emit a read barrier here to save on code size. We rely on the // resolution trampoline to do a suspend check before re-entering this code. __ Ldr(temp1, MemOperand(kMethodRegister, ArtMethod::DeclaringClassOffset().Int32Value())); - __ Ldrb(temp2, MemOperand(temp1, status_byte_offset)); - __ Cmp(temp2, shifted_visibly_initialized_value); + __ Ldrb(temp2, MemOperand(temp1, kClassStatusByteOffset)); + __ Cmp(temp2, kShiftedVisiblyInitializedValue); __ B(cs, &frame_entry_label_); // Check if we're initialized and jump to code that does a memory barrier if // so. - __ Cmp(temp2, shifted_initialized_value); + __ Cmp(temp2, kShiftedInitializedValue); __ B(cs, &memory_barrier); // Check if we're initializing and the thread initializing is the one // executing the code. - __ Cmp(temp2, shifted_initializing_value); + __ Cmp(temp2, kShiftedInitializingValue); __ B(lo, &resolution); __ Ldr(temp1, MemOperand(temp1, mirror::Class::ClinitThreadIdOffset().Int32Value())); @@ -7657,6 +7658,7 @@ HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: case HLoadClass::LoadKind::kBootImageRelRo: + case HLoadClass::LoadKind::kAppImageRelRo: case HLoadClass::LoadKind::kBssEntry: case HLoadClass::LoadKind::kBssEntryPublic: case HLoadClass::LoadKind::kBssEntryPackage: @@ -7760,6 +7762,15 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ codegen_->LoadBootImageRelRoEntry(out, boot_image_offset); break; } + case HLoadClass::LoadKind::kAppImageRelRo: { + DCHECK(codegen_->GetCompilerOptions().IsAppImage()); + DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); + CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = + codegen_->NewAppImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); + codegen_->EmitMovwMovtPlaceholder(labels, out); + __ Ldr(out, MemOperand(out, /*offset=*/ 0)); + break; + } case HLoadClass::LoadKind::kBssEntry: case HLoadClass::LoadKind::kBssEntryPublic: case HLoadClass::LoadKind::kBssEntryPackage: { @@ -7851,8 +7862,8 @@ void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck( LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) { UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); - __ Ldrb(temp, MemOperand(class_reg, status_byte_offset)); - __ Cmp(temp, shifted_visibly_initialized_value); + __ Ldrb(temp, MemOperand(class_reg, kClassStatusByteOffset)); + __ Cmp(temp, kShiftedVisiblyInitializedValue); __ B(lo, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); } @@ -9691,6 +9702,11 @@ CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageTyp return NewPcRelativePatch(&dex_file, type_index.index_, &boot_image_type_patches_); } +CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewAppImageTypePatch( + const DexFile& dex_file, dex::TypeIndex type_index) { + return NewPcRelativePatch(&dex_file, type_index.index_, &app_image_type_patches_); +} + CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewTypeBssEntryPatch( HLoadClass* load_class) { const DexFile& dex_file = load_class->GetDexFile(); @@ -9877,6 +9893,7 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* l /* MOVW+MOVT for each entry */ 2u * boot_image_method_patches_.size() + /* MOVW+MOVT for each entry */ 2u * method_bss_entry_patches_.size() + /* MOVW+MOVT for each entry */ 2u * boot_image_type_patches_.size() + + /* MOVW+MOVT for each entry */ 2u * app_image_type_patches_.size() + /* MOVW+MOVT for each entry */ 2u * type_bss_entry_patches_.size() + /* MOVW+MOVT for each entry */ 2u * public_type_bss_entry_patches_.size() + /* MOVW+MOVT for each entry */ 2u * package_type_bss_entry_patches_.size() + @@ -9898,12 +9915,15 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* l DCHECK(boot_image_type_patches_.empty()); DCHECK(boot_image_string_patches_.empty()); } + DCHECK_IMPLIES(!GetCompilerOptions().IsAppImage(), app_image_type_patches_.empty()); if (GetCompilerOptions().IsBootImage()) { EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::IntrinsicReferencePatch>>( boot_image_other_patches_, linker_patches); } else { EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::BootImageRelRoPatch>>( boot_image_other_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeAppImageRelRoPatch>( + app_image_type_patches_, linker_patches); } EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>( method_bss_entry_patches_, linker_patches); diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index a62097d08f..51bee1cd77 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -709,6 +709,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { PcRelativePatchInfo* NewBootImageMethodPatch(MethodReference target_method); PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method); PcRelativePatchInfo* NewBootImageTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); + PcRelativePatchInfo* NewAppImageTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); PcRelativePatchInfo* NewTypeBssEntryPatch(HLoadClass* load_class); PcRelativePatchInfo* NewBootImageStringPatch(const DexFile& dex_file, dex::StringIndex string_index); @@ -1029,6 +1030,8 @@ class CodeGeneratorARMVIXL : public CodeGenerator { ArenaDeque<PcRelativePatchInfo> method_bss_entry_patches_; // PC-relative type patch info for kBootImageLinkTimePcRelative. ArenaDeque<PcRelativePatchInfo> boot_image_type_patches_; + // PC-relative type patch info for kAppImageRelRo. + ArenaDeque<PcRelativePatchInfo> app_image_type_patches_; // PC-relative type patch info for kBssEntry. ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_; // PC-relative public type patch info for kBssEntryPublic. diff --git a/compiler/optimizing/code_generator_riscv64.cc b/compiler/optimizing/code_generator_riscv64.cc index c870db662c..75d5db83a0 100644 --- a/compiler/optimizing/code_generator_riscv64.cc +++ b/compiler/optimizing/code_generator_riscv64.cc @@ -137,8 +137,8 @@ static RegisterSet OneRegInReferenceOutSaveEverythingCallerSaves() { template <ClassStatus kStatus> static constexpr int64_t ShiftedSignExtendedClassStatusValue() { // This is used only for status values that have the highest bit set. - static_assert(CLZ(enum_cast<uint32_t>(kStatus)) == status_lsb_position); - constexpr uint32_t kShiftedStatusValue = enum_cast<uint32_t>(kStatus) << status_lsb_position; + static_assert(CLZ(enum_cast<uint32_t>(kStatus)) == kClassStatusLsbPosition); + constexpr uint32_t kShiftedStatusValue = enum_cast<uint32_t>(kStatus) << kClassStatusLsbPosition; static_assert(kShiftedStatusValue >= 0x80000000u); return static_cast<int64_t>(kShiftedStatusValue) - (INT64_C(1) << 32); } @@ -3382,7 +3382,7 @@ TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); kWithoutReadBarrier); XRegister temp2 = maybe_temp2_loc.AsRegister<XRegister>(); XRegister temp3 = maybe_temp3_loc.AsRegister<XRegister>(); - // Iftable is never null. + // Load the size of the `IfTable`. The `Class::iftable_` is never null. __ Loadw(temp2, temp, array_length_offset); // Loop through the iftable and check if any class matches. Riscv64Label loop; @@ -3841,15 +3841,16 @@ void LocationsBuilderRISCV64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kExactCheck: case TypeCheckKind::kAbstractClassCheck: case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: { + case TypeCheckKind::kArrayObjectCheck: + case TypeCheckKind::kInterfaceCheck: { bool needs_read_barrier = codegen_->InstanceOfNeedsReadBarrier(instruction); call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; - baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier; + baker_read_barrier_slow_path = (kUseBakerReadBarrier && needs_read_barrier) && + (type_check_kind != TypeCheckKind::kInterfaceCheck); break; } case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: - case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; case TypeCheckKind::kBitstringCheck: @@ -3889,10 +3890,14 @@ void InstructionCodeGeneratorRISCV64::VisitInstanceOf(HInstanceOf* instruction) const size_t num_temps = NumberOfInstanceOfTemps(codegen_->EmitReadBarrier(), type_check_kind); DCHECK_LE(num_temps, 1u); Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation(); - uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); - uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); - uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); - uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); + const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); + const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); + const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); + const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); + const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value(); + const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value(); + const uint32_t object_array_data_offset = + mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); Riscv64Label done; SlowPathCodeRISCV64* slow_path = nullptr; @@ -3999,11 +4004,51 @@ void InstructionCodeGeneratorRISCV64::VisitInstanceOf(HInstanceOf* instruction) break; } - case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: { + if (codegen_->InstanceOfNeedsReadBarrier(instruction)) { + DCHECK(locations->OnlyCallsOnSlowPath()); + slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathRISCV64( + instruction, /* is_fatal= */ false); + codegen_->AddSlowPath(slow_path); + if (codegen_->EmitNonBakerReadBarrier()) { + __ J(slow_path->GetEntryLabel()); + break; + } + // For Baker read barrier, take the slow path while marking. + __ Loadw(out, TR, Thread::IsGcMarkingOffset<kRiscv64PointerSize>().Int32Value()); + __ Bnez(out, slow_path->GetEntryLabel()); + } + + // Fast-path without read barriers. + ScratchRegisterScope srs(GetAssembler()); + XRegister temp = srs.AllocateXRegister(); + // /* HeapReference<Class> */ temp = obj->klass_ + __ Loadwu(temp, obj, class_offset); + codegen_->MaybeUnpoisonHeapReference(temp); + // /* HeapReference<Class> */ temp = temp->iftable_ + __ Loadwu(temp, temp, iftable_offset); + codegen_->MaybeUnpoisonHeapReference(temp); + // Load the size of the `IfTable`. The `Class::iftable_` is never null. + __ Loadw(out, temp, array_length_offset); + // Loop through the `IfTable` and check if any class matches. + Riscv64Label loop; + XRegister temp2 = srs.AllocateXRegister(); + __ Bind(&loop); + __ Beqz(out, &done); // If taken, the result in `out` is already 0 (false). + __ Loadwu(temp2, temp, object_array_data_offset); + codegen_->MaybeUnpoisonHeapReference(temp2); + // Go to next interface. + __ Addi(temp, temp, 2 * kHeapReferenceSize); + __ Addi(out, out, -2); + // Compare the classes and continue the loop if they do not match. + __ Bne(cls.AsRegister<XRegister>(), temp2, &loop); + __ LoadConst32(out, 1); + break; + } + + case TypeCheckKind::kUnresolvedCheck: { // Note that we indeed only call on slow path, but we always go - // into the slow path for the unresolved and interface check - // cases. + // into the slow path for the unresolved check case. // // We cannot directly call the InstanceofNonTrivial runtime // entry point without resorting to a type checking slow path @@ -4328,6 +4373,18 @@ void InstructionCodeGeneratorRISCV64::VisitLoadClass(HLoadClass* instruction) codegen_->LoadBootImageRelRoEntry(out, boot_image_offset); break; } + case HLoadClass::LoadKind::kAppImageRelRo: { + DCHECK(codegen_->GetCompilerOptions().IsAppImage()); + DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); + CodeGeneratorRISCV64::PcRelativePatchInfo* info_high = + codegen_->NewAppImageTypePatch(instruction->GetDexFile(), instruction->GetTypeIndex()); + codegen_->EmitPcRelativeAuipcPlaceholder(info_high, out); + CodeGeneratorRISCV64::PcRelativePatchInfo* info_low = + codegen_->NewAppImageTypePatch( + instruction->GetDexFile(), instruction->GetTypeIndex(), info_high); + codegen_->EmitPcRelativeLwuPlaceholder(info_low, out, out); + break; + } case HLoadClass::LoadKind::kBssEntry: case HLoadClass::LoadKind::kBssEntryPublic: case HLoadClass::LoadKind::kBssEntryPackage: { @@ -5775,6 +5832,7 @@ CodeGeneratorRISCV64::CodeGeneratorRISCV64(HGraph* graph, boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + app_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), public_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), package_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), @@ -6383,6 +6441,7 @@ HLoadClass::LoadKind CodeGeneratorRISCV64::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: case HLoadClass::LoadKind::kBootImageRelRo: + case HLoadClass::LoadKind::kAppImageRelRo: case HLoadClass::LoadKind::kBssEntry: case HLoadClass::LoadKind::kBssEntryPublic: case HLoadClass::LoadKind::kBssEntryPackage: @@ -6434,6 +6493,11 @@ CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewBootImageTyp return NewPcRelativePatch(&dex_file, type_index.index_, info_high, &boot_image_type_patches_); } +CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewAppImageTypePatch( + const DexFile& dex_file, dex::TypeIndex type_index, const PcRelativePatchInfo* info_high) { + return NewPcRelativePatch(&dex_file, type_index.index_, info_high, &app_image_type_patches_); +} + CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewBootImageJniEntrypointPatch( MethodReference target_method, const PcRelativePatchInfo* info_high) { return NewPcRelativePatch( @@ -6597,6 +6661,7 @@ void CodeGeneratorRISCV64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* l boot_image_method_patches_.size() + method_bss_entry_patches_.size() + boot_image_type_patches_.size() + + app_image_type_patches_.size() + type_bss_entry_patches_.size() + public_type_bss_entry_patches_.size() + package_type_bss_entry_patches_.size() + @@ -6617,12 +6682,15 @@ void CodeGeneratorRISCV64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* l DCHECK(boot_image_type_patches_.empty()); DCHECK(boot_image_string_patches_.empty()); } + DCHECK_IMPLIES(!GetCompilerOptions().IsAppImage(), app_image_type_patches_.empty()); if (GetCompilerOptions().IsBootImage()) { EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::IntrinsicReferencePatch>>( boot_image_other_patches_, linker_patches); } else { EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::BootImageRelRoPatch>>( boot_image_other_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeAppImageRelRoPatch>( + app_image_type_patches_, linker_patches); } EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>( method_bss_entry_patches_, linker_patches); diff --git a/compiler/optimizing/code_generator_riscv64.h b/compiler/optimizing/code_generator_riscv64.h index c49e2b4771..227882e7b8 100644 --- a/compiler/optimizing/code_generator_riscv64.h +++ b/compiler/optimizing/code_generator_riscv64.h @@ -71,7 +71,6 @@ static constexpr int32_t kFClassNaNMinValue = 0x100; V(FP16LessEquals) \ V(FP16Min) \ V(FP16Max) \ - V(StringGetCharsNoCheck) \ V(StringStringIndexOf) \ V(StringStringIndexOfAfter) \ V(StringBufferAppend) \ @@ -590,6 +589,9 @@ class CodeGeneratorRISCV64 : public CodeGenerator { PcRelativePatchInfo* NewBootImageTypePatch(const DexFile& dex_file, dex::TypeIndex type_index, const PcRelativePatchInfo* info_high = nullptr); + PcRelativePatchInfo* NewAppImageTypePatch(const DexFile& dex_file, + dex::TypeIndex type_index, + const PcRelativePatchInfo* info_high = nullptr); PcRelativePatchInfo* NewTypeBssEntryPatch(HLoadClass* load_class, const PcRelativePatchInfo* info_high = nullptr); PcRelativePatchInfo* NewBootImageStringPatch(const DexFile& dex_file, @@ -820,6 +822,8 @@ class CodeGeneratorRISCV64 : public CodeGenerator { ArenaDeque<PcRelativePatchInfo> method_bss_entry_patches_; // PC-relative type patch info for kBootImageLinkTimePcRelative. ArenaDeque<PcRelativePatchInfo> boot_image_type_patches_; + // PC-relative type patch info for kAppImageRelRo. + ArenaDeque<PcRelativePatchInfo> app_image_type_patches_; // PC-relative type patch info for kBssEntry. ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_; // PC-relative public type patch info for kBssEntryPublic. diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 5ad818dd53..2f6dde3df7 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1173,6 +1173,7 @@ CodeGeneratorX86::CodeGeneratorX86(HGraph* graph, boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + app_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), public_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), package_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), @@ -1389,17 +1390,18 @@ void CodeGeneratorX86::GenerateFrameEntry() { NearLabel continue_execution, resolution; // We'll use EBP as temporary. __ pushl(EBP); + __ cfi().AdjustCFAOffset(4); // Check if we're visibly initialized. // We don't emit a read barrier here to save on code size. We rely on the // resolution trampoline to do a suspend check before re-entering this code. __ movl(EBP, Address(kMethodRegisterArgument, ArtMethod::DeclaringClassOffset().Int32Value())); - __ cmpb(Address(EBP, status_byte_offset), Immediate(shifted_visibly_initialized_value)); + __ cmpb(Address(EBP, kClassStatusByteOffset), Immediate(kShiftedVisiblyInitializedValue)); __ j(kAboveEqual, &continue_execution); // Check if we're initializing and the thread initializing is the one // executing the code. - __ cmpb(Address(EBP, status_byte_offset), Immediate(shifted_initializing_value)); + __ cmpb(Address(EBP, kClassStatusByteOffset), Immediate(kShiftedInitializingValue)); __ j(kBelow, &resolution); __ movl(EBP, Address(EBP, mirror::Class::ClinitThreadIdOffset().Int32Value())); @@ -1408,13 +1410,16 @@ void CodeGeneratorX86::GenerateFrameEntry() { __ Bind(&resolution); __ popl(EBP); + __ cfi().AdjustCFAOffset(-4); // Jump to the resolution stub. ThreadOffset32 entrypoint_offset = GetThreadOffset<kX86PointerSize>(kQuickQuickResolutionTrampoline); __ fs()->jmp(Address::Absolute(entrypoint_offset)); __ Bind(&continue_execution); + __ cfi().AdjustCFAOffset(4); // Undo the `-4` adjustment above. We get here with EBP pushed. __ popl(EBP); + __ cfi().AdjustCFAOffset(-4); } __ Bind(&frame_entry_label_); @@ -5715,6 +5720,14 @@ void CodeGeneratorX86::RecordBootImageTypePatch(HLoadClass* load_class) { __ Bind(&boot_image_type_patches_.back().label); } +void CodeGeneratorX86::RecordAppImageTypePatch(HLoadClass* load_class) { + HX86ComputeBaseMethodAddress* method_address = + load_class->InputAt(0)->AsX86ComputeBaseMethodAddress(); + app_image_type_patches_.emplace_back( + method_address, &load_class->GetDexFile(), load_class->GetTypeIndex().index_); + __ Bind(&app_image_type_patches_.back().label); +} + Label* CodeGeneratorX86::NewTypeBssEntryPatch(HLoadClass* load_class) { HX86ComputeBaseMethodAddress* method_address = load_class->InputAt(0)->AsX86ComputeBaseMethodAddress(); @@ -5844,6 +5857,7 @@ void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linke boot_image_method_patches_.size() + method_bss_entry_patches_.size() + boot_image_type_patches_.size() + + app_image_type_patches_.size() + type_bss_entry_patches_.size() + public_type_bss_entry_patches_.size() + package_type_bss_entry_patches_.size() + @@ -5864,12 +5878,15 @@ void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linke DCHECK(boot_image_type_patches_.empty()); DCHECK(boot_image_string_patches_.empty()); } + DCHECK_IMPLIES(!GetCompilerOptions().IsAppImage(), app_image_type_patches_.empty()); if (GetCompilerOptions().IsBootImage()) { EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::IntrinsicReferencePatch>>( boot_image_other_patches_, linker_patches); } else { EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::BootImageRelRoPatch>>( boot_image_other_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeAppImageRelRoPatch>( + app_image_type_patches_, linker_patches); } EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>( method_bss_entry_patches_, linker_patches); @@ -7283,6 +7300,7 @@ HLoadClass::LoadKind CodeGeneratorX86::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: case HLoadClass::LoadKind::kBootImageRelRo: + case HLoadClass::LoadKind::kAppImageRelRo: case HLoadClass::LoadKind::kBssEntry: case HLoadClass::LoadKind::kBssEntryPublic: case HLoadClass::LoadKind::kBssEntryPackage: @@ -7396,6 +7414,14 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE CodeGenerator::GetBootImageOffset(cls)); break; } + case HLoadClass::LoadKind::kAppImageRelRo: { + DCHECK(codegen_->GetCompilerOptions().IsAppImage()); + DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); + Register method_address = locations->InAt(0).AsRegister<Register>(); + __ movl(out, Address(method_address, CodeGeneratorX86::kPlaceholder32BitOffset)); + codegen_->RecordAppImageTypePatch(cls); + break; + } case HLoadClass::LoadKind::kBssEntry: case HLoadClass::LoadKind::kBssEntryPublic: case HLoadClass::LoadKind::kBssEntryPackage: { @@ -7488,7 +7514,7 @@ void InstructionCodeGeneratorX86::VisitClinitCheck(HClinitCheck* check) { void InstructionCodeGeneratorX86::GenerateClassInitializationCheck( SlowPathCode* slow_path, Register class_reg) { - __ cmpb(Address(class_reg, status_byte_offset), Immediate(shifted_visibly_initialized_value)); + __ cmpb(Address(class_reg, kClassStatusByteOffset), Immediate(kShiftedVisiblyInitializedValue)); __ j(kBelow, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); } diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index ee87947ca0..93f8e6ed9b 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -539,6 +539,7 @@ class CodeGeneratorX86 : public CodeGenerator { void RecordBootImageMethodPatch(HInvoke* invoke); void RecordMethodBssEntryPatch(HInvoke* invoke); void RecordBootImageTypePatch(HLoadClass* load_class); + void RecordAppImageTypePatch(HLoadClass* load_class); Label* NewTypeBssEntryPatch(HLoadClass* load_class); void RecordBootImageStringPatch(HLoadString* load_string); Label* NewStringBssEntryPatch(HLoadString* load_string); @@ -775,6 +776,8 @@ class CodeGeneratorX86 : public CodeGenerator { ArenaDeque<X86PcRelativePatchInfo> method_bss_entry_patches_; // PC-relative type patch info for kBootImageLinkTimePcRelative. ArenaDeque<X86PcRelativePatchInfo> boot_image_type_patches_; + // PC-relative type patch info for kAppImageRelRo. + ArenaDeque<X86PcRelativePatchInfo> app_image_type_patches_; // PC-relative type patch info for kBssEntry. ArenaDeque<X86PcRelativePatchInfo> type_bss_entry_patches_; // PC-relative public type patch info for kBssEntryPublic. diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 4855b086ae..5c9ee8fea4 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1328,6 +1328,12 @@ void CodeGeneratorX86_64::RecordBootImageTypePatch(const DexFile& dex_file, __ Bind(&boot_image_type_patches_.back().label); } +void CodeGeneratorX86_64::RecordAppImageTypePatch(const DexFile& dex_file, + dex::TypeIndex type_index) { + app_image_type_patches_.emplace_back(&dex_file, type_index.index_); + __ Bind(&app_image_type_patches_.back().label); +} + Label* CodeGeneratorX86_64::NewTypeBssEntryPatch(HLoadClass* load_class) { ArenaDeque<PatchInfo<Label>>* patches = nullptr; switch (load_class->GetLoadKind()) { @@ -1448,6 +1454,7 @@ void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* li boot_image_method_patches_.size() + method_bss_entry_patches_.size() + boot_image_type_patches_.size() + + app_image_type_patches_.size() + type_bss_entry_patches_.size() + public_type_bss_entry_patches_.size() + package_type_bss_entry_patches_.size() + @@ -1469,12 +1476,15 @@ void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* li DCHECK(boot_image_type_patches_.empty()); DCHECK(boot_image_string_patches_.empty()); } + DCHECK_IMPLIES(!GetCompilerOptions().IsAppImage(), app_image_type_patches_.empty()); if (GetCompilerOptions().IsBootImage()) { EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::IntrinsicReferencePatch>>( boot_image_other_patches_, linker_patches); } else { EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::BootImageRelRoPatch>>( boot_image_other_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeAppImageRelRoPatch>( + app_image_type_patches_, linker_patches); } EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>( method_bss_entry_patches_, linker_patches); @@ -1607,6 +1617,7 @@ CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph, boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + app_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), public_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), package_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), @@ -1828,13 +1839,14 @@ void CodeGeneratorX86_64::GenerateFrameEntry() { __ movl(CpuRegister(TMP), Address(CpuRegister(kMethodRegisterArgument), ArtMethod::DeclaringClassOffset().Int32Value())); - __ cmpb(Address(CpuRegister(TMP), status_byte_offset), - Immediate(shifted_visibly_initialized_value)); + __ cmpb(Address(CpuRegister(TMP), kClassStatusByteOffset), + Immediate(kShiftedVisiblyInitializedValue)); __ j(kAboveEqual, &frame_entry_label_); // Check if we're initializing and the thread initializing is the one // executing the code. - __ cmpb(Address(CpuRegister(TMP), status_byte_offset), Immediate(shifted_initializing_value)); + __ cmpb(Address(CpuRegister(TMP), kClassStatusByteOffset), + Immediate(kShiftedInitializingValue)); __ j(kBelow, &resolution); __ movl(CpuRegister(TMP), @@ -6585,7 +6597,7 @@ void ParallelMoveResolverX86_64::RestoreScratch(int reg) { void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck( SlowPathCode* slow_path, CpuRegister class_reg) { - __ cmpb(Address(class_reg, status_byte_offset), Immediate(shifted_visibly_initialized_value)); + __ cmpb(Address(class_reg, kClassStatusByteOffset), Immediate(kShiftedVisiblyInitializedValue)); __ j(kBelow, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); } @@ -6620,6 +6632,7 @@ HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: case HLoadClass::LoadKind::kBootImageRelRo: + case HLoadClass::LoadKind::kAppImageRelRo: case HLoadClass::LoadKind::kBssEntry: case HLoadClass::LoadKind::kBssEntryPublic: case HLoadClass::LoadKind::kBssEntryPackage: @@ -6732,6 +6745,14 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S codegen_->RecordBootImageRelRoPatch(CodeGenerator::GetBootImageOffset(cls)); break; } + case HLoadClass::LoadKind::kAppImageRelRo: { + DCHECK(codegen_->GetCompilerOptions().IsAppImage()); + DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); + __ movl(out, + Address::Absolute(CodeGeneratorX86_64::kPlaceholder32BitOffset, /* no_rip= */ false)); + codegen_->RecordAppImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); + break; + } case HLoadClass::LoadKind::kBssEntry: case HLoadClass::LoadKind::kBssEntryPublic: case HLoadClass::LoadKind::kBssEntryPackage: { diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index b8e2456381..7a5e4aa894 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -532,6 +532,7 @@ class CodeGeneratorX86_64 : public CodeGenerator { void RecordBootImageMethodPatch(HInvoke* invoke); void RecordMethodBssEntryPatch(HInvoke* invoke); void RecordBootImageTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); + void RecordAppImageTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); Label* NewTypeBssEntryPatch(HLoadClass* load_class); void RecordBootImageStringPatch(HLoadString* load_string); Label* NewStringBssEntryPatch(HLoadString* load_string); @@ -738,6 +739,8 @@ class CodeGeneratorX86_64 : public CodeGenerator { ArenaDeque<PatchInfo<Label>> method_bss_entry_patches_; // PC-relative type patch info for kBootImageLinkTimePcRelative. ArenaDeque<PatchInfo<Label>> boot_image_type_patches_; + // PC-relative type patch info for kAppImageRelRo. + ArenaDeque<PatchInfo<Label>> app_image_type_patches_; // PC-relative type patch info for kBssEntry. ArenaDeque<PatchInfo<Label>> type_bss_entry_patches_; // PC-relative public type patch info for kBssEntryPublic. diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 8ed43b1d3e..d8c7a6531e 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -3445,27 +3445,26 @@ void IntrinsicCodeGeneratorARM64::VisitDoubleIsInfinite(HInvoke* invoke) { GenIsInfinite(invoke->GetLocations(), /* is64bit= */ true, GetVIXLAssembler()); } -#define VISIT_INTRINSIC(name, low, high, type, start_index) \ - void IntrinsicLocationsBuilderARM64::Visit ##name ##ValueOf(HInvoke* invoke) { \ - InvokeRuntimeCallingConvention calling_convention; \ - IntrinsicVisitor::ComputeValueOfLocations( \ - invoke, \ - codegen_, \ - low, \ - high - low + 1, \ - calling_convention.GetReturnLocation(DataType::Type::kReference), \ - Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode())); \ - } \ - void IntrinsicCodeGeneratorARM64::Visit ##name ##ValueOf(HInvoke* invoke) { \ - IntrinsicVisitor::ValueOfInfo info = \ - IntrinsicVisitor::ComputeValueOfInfo( \ - invoke, \ - codegen_->GetCompilerOptions(), \ - WellKnownClasses::java_lang_ ##name ##_value, \ - low, \ - high - low + 1, \ - start_index); \ - HandleValueOf(invoke, info, type); \ +#define VISIT_INTRINSIC(name, low, high, type, start_index) \ + void IntrinsicLocationsBuilderARM64::Visit##name##ValueOf(HInvoke* invoke) { \ + InvokeRuntimeCallingConvention calling_convention; \ + IntrinsicVisitor::ComputeValueOfLocations( \ + invoke, \ + codegen_, \ + low, \ + (high) - (low) + 1, \ + calling_convention.GetReturnLocation(DataType::Type::kReference), \ + Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode())); \ + } \ + void IntrinsicCodeGeneratorARM64::Visit##name##ValueOf(HInvoke* invoke) { \ + IntrinsicVisitor::ValueOfInfo info = \ + IntrinsicVisitor::ComputeValueOfInfo(invoke, \ + codegen_->GetCompilerOptions(), \ + WellKnownClasses::java_lang_##name##_value, \ + low, \ + (high) - (low) + 1, \ + start_index); \ + HandleValueOf(invoke, info, type); \ } BOXED_TYPES(VISIT_INTRINSIC) #undef VISIT_INTRINSIC diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index 25e35540ab..ecc9732816 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -2305,27 +2305,25 @@ void IntrinsicCodeGeneratorARMVIXL::VisitMathFloor(HInvoke* invoke) { __ Vrintm(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0)); } -#define VISIT_INTRINSIC(name, low, high, type, start_index) \ - void IntrinsicLocationsBuilderARMVIXL::Visit ##name ##ValueOf(HInvoke* invoke) { \ - InvokeRuntimeCallingConventionARMVIXL calling_convention; \ - IntrinsicVisitor::ComputeValueOfLocations( \ - invoke, \ - codegen_, \ - low, \ - high - low + 1, \ - LocationFrom(r0), \ - LocationFrom(calling_convention.GetRegisterAt(0))); \ - } \ - void IntrinsicCodeGeneratorARMVIXL::Visit ##name ##ValueOf(HInvoke* invoke) { \ - IntrinsicVisitor::ValueOfInfo info = \ - IntrinsicVisitor::ComputeValueOfInfo( \ - invoke, \ - codegen_->GetCompilerOptions(), \ - WellKnownClasses::java_lang_ ##name ##_value, \ - low, \ - high - low + 1, \ - start_index); \ - HandleValueOf(invoke, info, type); \ +#define VISIT_INTRINSIC(name, low, high, type, start_index) \ + void IntrinsicLocationsBuilderARMVIXL::Visit##name##ValueOf(HInvoke* invoke) { \ + InvokeRuntimeCallingConventionARMVIXL calling_convention; \ + IntrinsicVisitor::ComputeValueOfLocations(invoke, \ + codegen_, \ + low, \ + (high) - (low) + 1, \ + LocationFrom(r0), \ + LocationFrom(calling_convention.GetRegisterAt(0))); \ + } \ + void IntrinsicCodeGeneratorARMVIXL::Visit##name##ValueOf(HInvoke* invoke) { \ + IntrinsicVisitor::ValueOfInfo info = \ + IntrinsicVisitor::ComputeValueOfInfo(invoke, \ + codegen_->GetCompilerOptions(), \ + WellKnownClasses::java_lang_##name##_value, \ + low, \ + (high) - (low) + 1, \ + start_index); \ + HandleValueOf(invoke, info, type); \ } BOXED_TYPES(VISIT_INTRINSIC) #undef VISIT_INTRINSIC diff --git a/compiler/optimizing/intrinsics_riscv64.cc b/compiler/optimizing/intrinsics_riscv64.cc index 4e248a2b7c..0a9ac872db 100644 --- a/compiler/optimizing/intrinsics_riscv64.cc +++ b/compiler/optimizing/intrinsics_riscv64.cc @@ -636,27 +636,26 @@ void IntrinsicCodeGeneratorRISCV64::VisitLongDivideUnsigned(HInvoke* invoke) { GenerateDivideUnsigned(invoke, codegen_); } -#define VISIT_INTRINSIC(name, low, high, type, start_index) \ - void IntrinsicLocationsBuilderRISCV64::Visit ##name ##ValueOf(HInvoke* invoke) { \ - InvokeRuntimeCallingConvention calling_convention; \ - IntrinsicVisitor::ComputeValueOfLocations( \ - invoke, \ - codegen_, \ - low, \ - high - low + 1, \ - calling_convention.GetReturnLocation(DataType::Type::kReference), \ - Location::RegisterLocation(calling_convention.GetRegisterAt(0))); \ - } \ - void IntrinsicCodeGeneratorRISCV64::Visit ##name ##ValueOf(HInvoke* invoke) { \ - IntrinsicVisitor::ValueOfInfo info = \ - IntrinsicVisitor::ComputeValueOfInfo( \ - invoke, \ - codegen_->GetCompilerOptions(), \ - WellKnownClasses::java_lang_ ##name ##_value, \ - low, \ - high - low + 1, \ - start_index); \ - HandleValueOf(invoke, info, type); \ +#define VISIT_INTRINSIC(name, low, high, type, start_index) \ + void IntrinsicLocationsBuilderRISCV64::Visit##name##ValueOf(HInvoke* invoke) { \ + InvokeRuntimeCallingConvention calling_convention; \ + IntrinsicVisitor::ComputeValueOfLocations( \ + invoke, \ + codegen_, \ + low, \ + (high) - (low) + 1, \ + calling_convention.GetReturnLocation(DataType::Type::kReference), \ + Location::RegisterLocation(calling_convention.GetRegisterAt(0))); \ + } \ + void IntrinsicCodeGeneratorRISCV64::Visit##name##ValueOf(HInvoke* invoke) { \ + IntrinsicVisitor::ValueOfInfo info = \ + IntrinsicVisitor::ComputeValueOfInfo(invoke, \ + codegen_->GetCompilerOptions(), \ + WellKnownClasses::java_lang_##name##_value, \ + low, \ + (high) - (low) + 1, \ + start_index); \ + HandleValueOf(invoke, info, type); \ } BOXED_TYPES(VISIT_INTRINSIC) #undef VISIT_INTRINSIC @@ -767,8 +766,8 @@ void IntrinsicCodeGeneratorRISCV64::VisitReferenceGetReferent(HInvoke* invoke) { out, obj.AsRegister<XRegister>(), referent_offset, - /*maybe_temp=*/ locations->GetTemp(0), - /*needs_null_check=*/ false); + /*temp=*/locations->GetTemp(0), + /*needs_null_check=*/false); } else { codegen_->GetInstructionVisitor()->Load( out, obj.AsRegister<XRegister>(), referent_offset, DataType::Type::kReference); @@ -5043,6 +5042,185 @@ void IntrinsicCodeGeneratorRISCV64::VisitMathMultiplyHigh(HInvoke* invoke) { __ Mulh(out, x, y); } +void IntrinsicLocationsBuilderRISCV64::VisitStringGetCharsNoCheck(HInvoke* invoke) { + LocationSummary* locations = + new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); + locations->SetInAt(4, Location::RequiresRegister()); + + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorRISCV64::VisitStringGetCharsNoCheck(HInvoke* invoke) { + Riscv64Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // In Java sizeof(Char) is 2. + constexpr size_t char_size = DataType::Size(DataType::Type::kUint16); + static_assert(char_size == 2u); + + // Location of data in the destination char array buffer. + const uint32_t array_data_offset = mirror::Array::DataOffset(char_size).Uint32Value(); + + // Location of char array data in the source string. + const uint32_t string_value_offset = mirror::String::ValueOffset().Uint32Value(); + + // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin); + + // The source string. + XRegister source_string_object = locations->InAt(0).AsRegister<XRegister>(); + // Index of the first character. + XRegister source_begin_index = locations->InAt(1).AsRegister<XRegister>(); + // Index that immediately follows the last character. + XRegister source_end_index = locations->InAt(2).AsRegister<XRegister>(); + // The destination array. + XRegister destination_array_object = locations->InAt(3).AsRegister<XRegister>(); + // The start offset in the destination array. + XRegister destination_begin_offset = locations->InAt(4).AsRegister<XRegister>(); + + XRegister source_ptr = locations->GetTemp(0).AsRegister<XRegister>(); + XRegister destination_ptr = locations->GetTemp(1).AsRegister<XRegister>(); + XRegister number_of_chars = locations->GetTemp(2).AsRegister<XRegister>(); + + ScratchRegisterScope temps(assembler); + XRegister tmp = temps.AllocateXRegister(); + + Riscv64Label done; + + // Calculate the length(number_of_chars) of the string. + __ Subw(number_of_chars, source_end_index, source_begin_index); + + // If the string has zero length then exit. + __ Beqz(number_of_chars, &done); + + // Prepare a register with the destination address + // to start copying to the address: + // 1. set the address from which the data in the + // destination array begins (destination_array_object + array_data_offset); + __ Addi(destination_ptr, destination_array_object, array_data_offset); + // 2. it is necessary to add the start offset relative to the beginning + // of the data in the destination array, + // yet, due to sizeof(Char) being 2, formerly scaling must be performed + // (destination_begin_offset * 2 that equals to destination_begin_offset << 1); + __ Sh1Add(destination_ptr, destination_begin_offset, destination_ptr); + + // Prepare a register with the source address + // to start copying from the address: + // 1. set the address from which the data in the + // source string begins (source_string_object + string_value_offset). + // Other manipulations will be performed later, + // since they depend on whether the string is compressed or not. + __ Addi(source_ptr, source_string_object, string_value_offset); + + // The string can be compressed. It is a way to store strings more compactly. + // In this instance, every character is located in one byte (instead of two). + Riscv64Label compressed_string_preloop; + + // Information about whether the string is compressed or not is located + // in the area intended for storing the length of the string. + // The least significant bit of the string's length is used + // as the compression flag if STRING_COMPRESSION_ENABLED. + if (mirror::kUseStringCompression) { + // Location of count in string. + const uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); + // String's length. + __ Loadwu(tmp, source_string_object, count_offset); + + // Checking the string for compression. + // If so, move to the "compressed_string_preloop". + __ Andi(tmp, tmp, 0x1); + __ Beqz(tmp, &compressed_string_preloop); + } + + // Continue preparing the source register: + // proceed similarly to what was done for the destination register. + __ Sh1Add(source_ptr, source_begin_index, source_ptr); + + // If the string is not compressed, then perform ordinary copying. + // Copying will occur 4 characters (8 bytes) at a time, immediately after there are + // less than 4 characters left, move to the "remainder_loop" and copy the remaining + // characters one character (2 bytes) at a time. + // Note: Unaligned addresses are acceptable here and it is not required to embed + // additional code to correct them. + Riscv64Label main_loop; + Riscv64Label remainder_loop; + + // If initially there are less than 4 characters, + // then we directly calculate the remainder. + __ Addi(tmp, number_of_chars, -4); + __ Bltz(tmp, &remainder_loop); + + // Otherwise, save the value to the counter and continue. + __ Mv(number_of_chars, tmp); + + // Main loop. Loads and stores 4 16-bit Java characters at a time. + __ Bind(&main_loop); + + __ Loadd(tmp, source_ptr, 0); + __ Addi(source_ptr, source_ptr, char_size * 4); + __ Stored(tmp, destination_ptr, 0); + __ Addi(destination_ptr, destination_ptr, char_size * 4); + + __ Addi(number_of_chars, number_of_chars, -4); + + __ Bgez(number_of_chars, &main_loop); + + // Restore the previous counter value. + __ Addi(number_of_chars, number_of_chars, 4); + __ Beqz(number_of_chars, &done); + + // Remainder loop for < 4 characters case and remainder handling. + // Loads and stores one 16-bit Java character at a time. + __ Bind(&remainder_loop); + + __ Loadhu(tmp, source_ptr, 0); + __ Addi(source_ptr, source_ptr, char_size); + + __ Storeh(tmp, destination_ptr, 0); + __ Addi(destination_ptr, destination_ptr, char_size); + + __ Addi(number_of_chars, number_of_chars, -1); + __ Bgtz(number_of_chars, &remainder_loop); + + Riscv64Label compressed_string_loop; + if (mirror::kUseStringCompression) { + __ J(&done); + + // Below is the copying under the string compression circumstance mentioned above. + // Every character in the source string occupies only one byte (instead of two). + constexpr size_t compressed_char_size = DataType::Size(DataType::Type::kInt8); + static_assert(compressed_char_size == 1u); + + __ Bind(&compressed_string_preloop); + + // Continue preparing the source register: + // proceed identically to what was done for the destination register, + // yet take into account that only one byte yields for every source character, + // hence we need to extend it to two ones when copying it to the destination address. + // Against this background scaling for source_begin_index is not needed. + __ Add(source_ptr, source_ptr, source_begin_index); + + // Copy loop for compressed strings. Copying one 8-bit character to 16-bit one at a time. + __ Bind(&compressed_string_loop); + + __ Loadbu(tmp, source_ptr, 0); + __ Addi(source_ptr, source_ptr, compressed_char_size); + __ Storeh(tmp, destination_ptr, 0); + __ Addi(destination_ptr, destination_ptr, char_size); + + __ Addi(number_of_chars, number_of_chars, -1); + __ Bgtz(number_of_chars, &compressed_string_loop); + } + + __ Bind(&done); +} + #define MARK_UNIMPLEMENTED(Name) UNIMPLEMENTED_INTRINSIC(RISCV64, Name) UNIMPLEMENTED_INTRINSIC_LIST_RISCV64(MARK_UNIMPLEMENTED); #undef MARK_UNIMPLEMENTED diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 62efeb1d8c..63dd963a9f 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -3380,28 +3380,27 @@ static void RequestBaseMethodAddressInRegister(HInvoke* invoke) { } } -#define VISIT_INTRINSIC(name, low, high, type, start_index) \ - void IntrinsicLocationsBuilderX86::Visit ##name ##ValueOf(HInvoke* invoke) { \ - InvokeRuntimeCallingConvention calling_convention; \ - IntrinsicVisitor::ComputeValueOfLocations( \ - invoke, \ - codegen_, \ - low, \ - high - low + 1, \ - Location::RegisterLocation(EAX), \ - Location::RegisterLocation(calling_convention.GetRegisterAt(0))); \ - RequestBaseMethodAddressInRegister(invoke); \ - } \ - void IntrinsicCodeGeneratorX86::Visit ##name ##ValueOf(HInvoke* invoke) { \ - IntrinsicVisitor::ValueOfInfo info = \ - IntrinsicVisitor::ComputeValueOfInfo( \ - invoke, \ - codegen_->GetCompilerOptions(), \ - WellKnownClasses::java_lang_ ##name ##_value, \ - low, \ - high - low + 1, \ - start_index); \ - HandleValueOf(invoke, info, type); \ +#define VISIT_INTRINSIC(name, low, high, type, start_index) \ + void IntrinsicLocationsBuilderX86::Visit##name##ValueOf(HInvoke* invoke) { \ + InvokeRuntimeCallingConvention calling_convention; \ + IntrinsicVisitor::ComputeValueOfLocations( \ + invoke, \ + codegen_, \ + low, \ + (high) - (low) + 1, \ + Location::RegisterLocation(EAX), \ + Location::RegisterLocation(calling_convention.GetRegisterAt(0))); \ + RequestBaseMethodAddressInRegister(invoke); \ + } \ + void IntrinsicCodeGeneratorX86::Visit##name##ValueOf(HInvoke* invoke) { \ + IntrinsicVisitor::ValueOfInfo info = \ + IntrinsicVisitor::ComputeValueOfInfo(invoke, \ + codegen_->GetCompilerOptions(), \ + WellKnownClasses::java_lang_##name##_value, \ + low, \ + (high) - (low) + 1, \ + start_index); \ + HandleValueOf(invoke, info, type); \ } BOXED_TYPES(VISIT_INTRINSIC) #undef VISIT_INTRINSIC diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index b5ddaaa0b7..131460d605 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -3180,27 +3180,26 @@ void IntrinsicCodeGeneratorX86_64::VisitLongNumberOfTrailingZeros(HInvoke* invok GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long= */ true); } -#define VISIT_INTRINSIC(name, low, high, type, start_index) \ - void IntrinsicLocationsBuilderX86_64::Visit ##name ##ValueOf(HInvoke* invoke) { \ - InvokeRuntimeCallingConvention calling_convention; \ - IntrinsicVisitor::ComputeValueOfLocations( \ - invoke, \ - codegen_, \ - low, \ - high - low + 1, \ - Location::RegisterLocation(RAX), \ - Location::RegisterLocation(calling_convention.GetRegisterAt(0))); \ - } \ - void IntrinsicCodeGeneratorX86_64::Visit ##name ##ValueOf(HInvoke* invoke) { \ - IntrinsicVisitor::ValueOfInfo info = \ - IntrinsicVisitor::ComputeValueOfInfo( \ - invoke, \ - codegen_->GetCompilerOptions(), \ - WellKnownClasses::java_lang_ ##name ##_value, \ - low, \ - high - low + 1, \ - start_index); \ - HandleValueOf(invoke, info, type); \ +#define VISIT_INTRINSIC(name, low, high, type, start_index) \ + void IntrinsicLocationsBuilderX86_64::Visit##name##ValueOf(HInvoke* invoke) { \ + InvokeRuntimeCallingConvention calling_convention; \ + IntrinsicVisitor::ComputeValueOfLocations( \ + invoke, \ + codegen_, \ + low, \ + (high) - (low) + 1, \ + Location::RegisterLocation(RAX), \ + Location::RegisterLocation(calling_convention.GetRegisterAt(0))); \ + } \ + void IntrinsicCodeGeneratorX86_64::Visit##name##ValueOf(HInvoke* invoke) { \ + IntrinsicVisitor::ValueOfInfo info = \ + IntrinsicVisitor::ComputeValueOfInfo(invoke, \ + codegen_->GetCompilerOptions(), \ + WellKnownClasses::java_lang_##name##_value, \ + low, \ + (high) - (low) + 1, \ + start_index); \ + HandleValueOf(invoke, info, type); \ } BOXED_TYPES(VISIT_INTRINSIC) #undef VISIT_INTRINSIC diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 90fc5db02e..33ffc07ba8 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -6743,6 +6743,10 @@ class HLoadClass final : public HInstruction { // Used for boot image classes referenced by apps in AOT-compiled code. kBootImageRelRo, + // Load from an app image entry in the .data.img.rel.ro using a PC-relative load. + // Used for app image classes referenced by apps in AOT-compiled code. + kAppImageRelRo, + // Load from an entry in the .bss section using a PC-relative load. // Used for classes outside boot image referenced by AOT-compiled app and boot image code. kBssEntry, @@ -6814,6 +6818,7 @@ class HLoadClass final : public HInstruction { bool HasPcRelativeLoadKind() const { return GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative || GetLoadKind() == LoadKind::kBootImageRelRo || + GetLoadKind() == LoadKind::kAppImageRelRo || GetLoadKind() == LoadKind::kBssEntry || GetLoadKind() == LoadKind::kBssEntryPublic || GetLoadKind() == LoadKind::kBssEntryPackage; @@ -6933,6 +6938,7 @@ class HLoadClass final : public HInstruction { static bool HasTypeReference(LoadKind load_kind) { return load_kind == LoadKind::kReferrersClass || load_kind == LoadKind::kBootImageLinkTimePcRelative || + load_kind == LoadKind::kAppImageRelRo || load_kind == LoadKind::kBssEntry || load_kind == LoadKind::kBssEntryPublic || load_kind == LoadKind::kBssEntryPackage || @@ -6978,6 +6984,7 @@ inline void HLoadClass::AddSpecialInput(HInstruction* special_input) { // including literal pool loads, which are PC-relative too. DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative || GetLoadKind() == LoadKind::kBootImageRelRo || + GetLoadKind() == LoadKind::kAppImageRelRo || GetLoadKind() == LoadKind::kBssEntry || GetLoadKind() == LoadKind::kBssEntryPublic || GetLoadKind() == LoadKind::kBssEntryPackage || diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index aa75c2464b..cb94491b8e 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -277,7 +277,7 @@ HLoadClass::LoadKind HSharpening::ComputeLoadClassKind( } else if (compiler_options.IsAppImage() && is_class_in_current_image()) { // AOT app compilation, app image class. is_in_image = true; - desired_load_kind = HLoadClass::LoadKind::kBssEntry; + desired_load_kind = HLoadClass::LoadKind::kAppImageRelRo; } else { // Not JIT and the klass is not in boot image or app image. desired_load_kind = HLoadClass::LoadKind::kBssEntry; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index c2c4fb27a1..9529e6d28b 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1966,6 +1966,9 @@ class Dex2Oat final { } } } + if (IsAppImage()) { + AotClassLinker::SetAppImageDexFiles(&compiler_options_->GetDexFilesForOatFile()); + } // Register dex caches and key them to the class loader so that they only unload when the // class loader unloads. @@ -2127,6 +2130,7 @@ class Dex2Oat final { elf_writer->PrepareDynamicSection(oat_writer->GetOatHeader().GetExecutableOffset(), oat_writer->GetCodeSize(), oat_writer->GetDataImgRelRoSize(), + oat_writer->GetDataImgRelRoAppImageOffset(), oat_writer->GetBssSize(), oat_writer->GetBssMethodsOffset(), oat_writer->GetBssRootsOffset(), diff --git a/dex2oat/driver/compiler_driver.cc b/dex2oat/driver/compiler_driver.cc index e830e86d90..c696191491 100644 --- a/dex2oat/driver/compiler_driver.cc +++ b/dex2oat/driver/compiler_driver.cc @@ -43,6 +43,7 @@ #include "base/timing_logger.h" #include "class_linker-inl.h" #include "class_root-inl.h" +#include "common_throws.h" #include "compiled_method-inl.h" #include "compiler.h" #include "compiler_callbacks.h" @@ -82,7 +83,6 @@ #include "thread_list.h" #include "thread_pool.h" #include "trampolines/trampoline_compiler.h" -#include "transaction.h" #include "utils/atomic_dex_ref_map-inl.h" #include "utils/swap_space.h" #include "vdex_file.h" @@ -889,6 +889,12 @@ void CompilerDriver::PreCompile(jobject class_loader, << "Please check the log."; _exit(1); } + + if (GetCompilerOptions().IsAppImage() && had_hard_verifier_failure_) { + // Prune erroneous classes and classes that depend on them. + UpdateImageClasses(timings, image_classes); + VLOG(compiler) << "verify/UpdateImageClasses: " << GetMemoryUsageString(false); + } } if (GetCompilerOptions().IsGeneratingImage()) { @@ -911,8 +917,10 @@ void CompilerDriver::PreCompile(jobject class_loader, visitor.FillAllIMTAndConflictTables(); } - UpdateImageClasses(timings, image_classes); - VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false); + if (GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension()) { + UpdateImageClasses(timings, image_classes); + VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false); + } if (kBitstringSubtypeCheckEnabled && GetCompilerOptions().IsForceDeterminism() && GetCompilerOptions().IsBootImage()) { @@ -1380,7 +1388,8 @@ class ClinitImageUpdate { bool operator()(ObjPtr<mirror::Class> klass) override REQUIRES_SHARED(Locks::mutator_lock_) { bool resolved = klass->IsResolved(); DCHECK(resolved || klass->IsErroneousUnresolved()); - bool can_include_in_image = LIKELY(resolved) && CanIncludeInCurrentImage(klass); + bool can_include_in_image = + LIKELY(resolved) && LIKELY(!klass->IsErroneous()) && CanIncludeInCurrentImage(klass); std::string temp; std::string_view descriptor(klass->GetDescriptor(&temp)); auto it = data_->image_class_descriptors_->find(descriptor); @@ -1451,17 +1460,16 @@ class ClinitImageUpdate { void CompilerDriver::UpdateImageClasses(TimingLogger* timings, /*inout*/ HashSet<std::string>* image_classes) { - if (GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension()) { - TimingLogger::ScopedTiming t("UpdateImageClasses", timings); + DCHECK(GetCompilerOptions().IsGeneratingImage()); + TimingLogger::ScopedTiming t("UpdateImageClasses", timings); - // Suspend all threads. - ScopedSuspendAll ssa(__FUNCTION__); + // Suspend all threads. + ScopedSuspendAll ssa(__FUNCTION__); - ClinitImageUpdate update(image_classes, Thread::Current()); + ClinitImageUpdate update(image_classes, Thread::Current()); - // Do the marking. - update.Walk(); - } + // Do the marking. + update.Walk(); } void CompilerDriver::ProcessedInstanceField(bool resolved) { @@ -1836,17 +1844,10 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader))); std::string error_msg; - if (!verifier_deps->ValidateDependencies( + verifier_deps->ValidateDependenciesAndUpdateStatus( soa.Self(), class_loader, - dex_files, - &error_msg)) { - // Clear the information we have as we are going to re-verify and we do not - // want to keep that a class is verified. - verifier_deps->ClearData(dex_files); - LOG(WARNING) << "Fast verification failed: " << error_msg; - return false; - } + dex_files); bool compiler_only_verifies = !GetCompilerOptions().IsAnyCompilationEnabled() && @@ -2039,7 +2040,8 @@ class VerifyClassVisitor : public CompilationVisitor { klass, log_level_); - if (klass->IsErroneous()) { + DCHECK_EQ(klass->IsErroneous(), failure_kind == verifier::FailureKind::kHardFailure); + if (failure_kind == verifier::FailureKind::kHardFailure) { // ClassLinker::VerifyClass throws, which isn't useful in the compiler. CHECK(soa.Self()->IsExceptionPending()); soa.Self()->ClearException(); @@ -2227,7 +2229,7 @@ class InitializeClassVisitor : public CompilationVisitor { const dex::TypeId& class_type_id = dex_file.GetTypeId(class_def->class_idx_); const char* descriptor = dex_file.GetStringData(class_type_id.descriptor_idx_); StackHandleScope<3> hs(self); - ClassLinker* const class_linker = manager_->GetClassLinker(); + AotClassLinker* const class_linker = down_cast<AotClassLinker*>(manager_->GetClassLinker()); Runtime* const runtime = Runtime::Current(); const CompilerOptions& compiler_options = manager_->GetCompiler()->GetCompilerOptions(); const bool is_boot_image = compiler_options.IsBootImage(); @@ -2329,16 +2331,14 @@ class InitializeClassVisitor : public CompilationVisitor { // the transaction aborts and cannot resolve the type. // TransactionAbortError is not initialized ant not in boot image, needed only by // compiler and will be pruned by ImageWriter. - Handle<mirror::Class> exception_class = - hs.NewHandle(class_linker->FindClass(self, - Transaction::kAbortExceptionDescriptor, - class_loader)); + Handle<mirror::Class> exception_class = hs.NewHandle( + class_linker->FindClass(self, kTransactionAbortErrorDescriptor, class_loader)); bool exception_initialized = class_linker->EnsureInitialized(self, exception_class, true, true); DCHECK(exception_initialized); // Run the class initializer in transaction mode. - runtime->EnterTransactionMode(is_app_image, klass.Get()); + class_linker->EnterTransactionMode(is_app_image, klass.Get()); bool success = class_linker->EnsureInitialized(self, klass, true, true); // TODO we detach transaction from runtime to indicate we quit the transactional @@ -2349,7 +2349,7 @@ class InitializeClassVisitor : public CompilationVisitor { ScopedAssertNoThreadSuspension ants("Transaction end"); if (success) { - runtime->ExitTransactionMode(); + class_linker->ExitTransactionMode(); DCHECK(!runtime->IsActiveTransaction()); if (is_boot_image || is_boot_image_extension) { @@ -2370,7 +2370,7 @@ class InitializeClassVisitor : public CompilationVisitor { *file_log << exception->Dump() << "\n"; } self->ClearException(); - runtime->RollbackAllTransactions(); + class_linker->RollbackAllTransactions(); CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored"; } } @@ -2419,9 +2419,9 @@ class InitializeClassVisitor : public CompilationVisitor { self->GetJniEnv()->AssertLocalsEmpty(); } - if (!klass->IsVisiblyInitialized() && + if (!klass->IsInitialized() && (is_boot_image || is_boot_image_extension) && - !compiler_options.IsPreloadedClass(PrettyDescriptor(descriptor).c_str())) { + !compiler_options.IsPreloadedClass(PrettyDescriptor(descriptor))) { klass->SetInBootImageAndNotInPreloadedClasses(); } diff --git a/dex2oat/linker/arm64/relative_patcher_arm64.cc b/dex2oat/linker/arm64/relative_patcher_arm64.cc index 8772974c4b..2956285545 100644 --- a/dex2oat/linker/arm64/relative_patcher_arm64.cc +++ b/dex2oat/linker/arm64/relative_patcher_arm64.cc @@ -66,6 +66,7 @@ inline bool IsAdrpPatch(const LinkerPatch& patch) { case LinkerPatch::Type::kMethodBssEntry: case LinkerPatch::Type::kJniEntrypointRelative: case LinkerPatch::Type::kTypeRelative: + case LinkerPatch::Type::kTypeAppImageRelRo: case LinkerPatch::Type::kTypeBssEntry: case LinkerPatch::Type::kPublicTypeBssEntry: case LinkerPatch::Type::kPackageTypeBssEntry: @@ -274,6 +275,7 @@ void Arm64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, DCHECK(patch.GetType() == LinkerPatch::Type::kBootImageRelRo || patch.GetType() == LinkerPatch::Type::kMethodBssEntry || patch.GetType() == LinkerPatch::Type::kJniEntrypointRelative || + patch.GetType() == LinkerPatch::Type::kTypeAppImageRelRo || patch.GetType() == LinkerPatch::Type::kTypeBssEntry || patch.GetType() == LinkerPatch::Type::kPublicTypeBssEntry || patch.GetType() == LinkerPatch::Type::kPackageTypeBssEntry || diff --git a/dex2oat/linker/elf_writer.h b/dex2oat/linker/elf_writer.h index a1d0ece9de..35e3565592 100644 --- a/dex2oat/linker/elf_writer.h +++ b/dex2oat/linker/elf_writer.h @@ -61,6 +61,7 @@ class ElfWriter { virtual void PrepareDynamicSection(size_t rodata_size, size_t text_size, size_t data_img_rel_ro_size, + size_t data_img_rel_ro_app_image_offset, size_t bss_size, size_t bss_methods_offset, size_t bss_roots_offset, diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc index 373c46ce2c..f87ca6d81e 100644 --- a/dex2oat/linker/elf_writer_quick.cc +++ b/dex2oat/linker/elf_writer_quick.cc @@ -92,6 +92,7 @@ class ElfWriterQuick final : public ElfWriter { void PrepareDynamicSection(size_t rodata_size, size_t text_size, size_t data_img_rel_ro_size, + size_t data_img_rel_ro_app_image_offset, size_t bss_size, size_t bss_methods_offset, size_t bss_roots_offset, @@ -173,6 +174,7 @@ template <typename ElfTypes> void ElfWriterQuick<ElfTypes>::PrepareDynamicSection(size_t rodata_size, size_t text_size, size_t data_img_rel_ro_size, + size_t data_img_rel_ro_app_image_offset, size_t bss_size, size_t bss_methods_offset, size_t bss_roots_offset, @@ -191,6 +193,7 @@ void ElfWriterQuick<ElfTypes>::PrepareDynamicSection(size_t rodata_size, rodata_size_, text_size_, data_img_rel_ro_size_, + data_img_rel_ro_app_image_offset, bss_size_, bss_methods_offset, bss_roots_offset, diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index 9f553c1c74..f4a87bb84d 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -309,6 +309,7 @@ inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode, elf_writer->PrepareDynamicSection(oat_writer->GetOatHeader().GetExecutableOffset(), oat_writer->GetCodeSize(), oat_writer->GetDataImgRelRoSize(), + oat_writer->GetDataImgRelRoAppImageOffset(), oat_writer->GetBssSize(), oat_writer->GetBssMethodsOffset(), oat_writer->GetBssRootsOffset(), diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 63ac71fc12..abc3354421 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -1101,7 +1101,17 @@ bool ImageWriter::KeepClass(ObjPtr<mirror::Class> klass) { // the boot image spaces since these may have already been loaded at // run time when this image is loaded. Keep classes in the boot image // spaces we're compiling against since we don't want to re-resolve these. - return !PruneImageClass(klass); + // FIXME: Update image classes in the `CompilerOptions` after initializing classes + // with `--initialize-app-image-classes=true`. This experimental flag can currently + // cause an inconsistency between `CompilerOptions::IsImageClass()` and what actually + // ends up in the app image as seen in the run-test `660-clinit` where the class + // `ObjectRef` is considered an app image class during compilation but in the end + // it's pruned here. This inconsistency should be fixed if we want to properly + // initialize app image classes. b/38313278 + bool keep = !PruneImageClass(klass); + CHECK_IMPLIES(!compiler_options_.InitializeAppImageClasses(), keep) + << klass->PrettyDescriptor(); + return keep; } return true; } diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index 4d45529871..8ffe226bec 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -128,6 +128,15 @@ class ImageWriter final { } } + uint32_t GetGlobalImageOffset(mirror::Object* object) const REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(object != nullptr); + DCHECK(!IsInBootImage(object)); + size_t oat_index = GetOatIndex(object); + const ImageInfo& image_info = GetImageInfo(oat_index); + return dchecked_integral_cast<uint32_t>( + image_info.image_begin_ + GetImageOffset(object, oat_index) - global_image_begin_); + } + ArtMethod* GetImageMethodAddress(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); const void* GetIntrinsicReferenceAddress(uint32_t intrinsic_data) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 0dbb4bb05b..1419338066 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -373,6 +373,7 @@ OatWriter::OatWriter(const CompilerOptions& compiler_options, oat_size_(0u), data_img_rel_ro_start_(0u), data_img_rel_ro_size_(0u), + data_img_rel_ro_app_image_offset_(0u), bss_start_(0u), bss_size_(0u), bss_methods_offset_(0u), @@ -380,6 +381,7 @@ OatWriter::OatWriter(const CompilerOptions& compiler_options, boot_image_rel_ro_entries_(), bss_method_entry_references_(), bss_method_entries_(), + app_image_rel_ro_type_entries_(), bss_type_entries_(), bss_public_type_entries_(), bss_package_type_entries_(), @@ -486,7 +488,7 @@ bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file, const char* loc } // Add dex file source from raw memory. -bool OatWriter::AddRawDexFileSource(std::shared_ptr<DexFileContainer> container, +bool OatWriter::AddRawDexFileSource(const std::shared_ptr<DexFileContainer>& container, const uint8_t* dex_file_begin, const char* location, uint32_t location_checksum) { @@ -754,6 +756,9 @@ class OatWriter::InitBssLayoutMethodVisitor : public DexMethodVisitor { target_method.dex_file->NumMethodIds(), &writer_->bss_method_entry_references_); writer_->bss_method_entries_.Overwrite(target_method, /* placeholder */ 0u); + } else if (patch.GetType() == LinkerPatch::Type::kTypeAppImageRelRo) { + writer_->app_image_rel_ro_type_entries_.Overwrite(patch.TargetType(), + /* placeholder */ 0u); } else if (patch.GetType() == LinkerPatch::Type::kTypeBssEntry) { TypeReference target_type = patch.TargetType(); AddBssReference(target_type, @@ -1710,6 +1715,16 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { target_offset); break; } + case LinkerPatch::Type::kTypeAppImageRelRo: { + uint32_t target_offset = + writer_->data_img_rel_ro_start_ + + writer_->app_image_rel_ro_type_entries_.Get(patch.TargetType()); + writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, + patch, + offset_ + literal_offset, + target_offset); + break; + } case LinkerPatch::Type::kTypeBssEntry: { uint32_t target_offset = writer_->bss_start_ + writer_->bss_type_entries_.Get(patch.TargetType()); @@ -2363,7 +2378,7 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) { size_t OatWriter::InitDataImgRelRoLayout(size_t offset) { DCHECK_EQ(data_img_rel_ro_size_, 0u); - if (boot_image_rel_ro_entries_.empty()) { + if (boot_image_rel_ro_entries_.empty() && app_image_rel_ro_type_entries_.empty()) { // Nothing to put to the .data.img.rel.ro section. return offset; } @@ -2376,6 +2391,14 @@ size_t OatWriter::InitDataImgRelRoLayout(size_t offset) { data_img_rel_ro_size_ += sizeof(uint32_t); } + data_img_rel_ro_app_image_offset_ = data_img_rel_ro_size_; + + for (auto& entry : app_image_rel_ro_type_entries_) { + size_t& entry_offset = entry.second; + entry_offset = data_img_rel_ro_size_; + data_img_rel_ro_size_ += sizeof(uint32_t); + } + offset = data_img_rel_ro_start_ + data_img_rel_ro_size_; return offset; } @@ -3176,18 +3199,39 @@ size_t OatWriter::WriteCodeDexFiles(OutputStream* out, size_t OatWriter::WriteDataImgRelRo(OutputStream* out, size_t file_offset, size_t relative_offset) { - if (boot_image_rel_ro_entries_.empty()) { + if (boot_image_rel_ro_entries_.empty() && app_image_rel_ro_type_entries_.empty()) { return relative_offset; } // Write the entire .data.img.rel.ro with a single WriteFully(). std::vector<uint32_t> data; - data.reserve(boot_image_rel_ro_entries_.size()); + data.reserve(boot_image_rel_ro_entries_.size() + app_image_rel_ro_type_entries_.size()); for (const auto& entry : boot_image_rel_ro_entries_) { uint32_t boot_image_offset = entry.first; data.push_back(boot_image_offset); } - DCHECK_EQ(data.size(), boot_image_rel_ro_entries_.size()); + if (!app_image_rel_ro_type_entries_.empty()) { + DCHECK(GetCompilerOptions().IsAppImage()); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + ScopedObjectAccess soa(Thread::Current()); + const DexFile* last_dex_file = nullptr; + ObjPtr<mirror::DexCache> dex_cache = nullptr; + ObjPtr<mirror::ClassLoader> class_loader = nullptr; + for (const auto& entry : app_image_rel_ro_type_entries_) { + TypeReference target_type = entry.first; + if (target_type.dex_file != last_dex_file) { + dex_cache = class_linker->FindDexCache(soa.Self(), *target_type.dex_file); + class_loader = dex_cache->GetClassLoader(); + last_dex_file = target_type.dex_file; + } + ObjPtr<mirror::Class> type = + class_linker->LookupResolvedType(target_type.TypeIndex(), dex_cache, class_loader); + CHECK(type != nullptr); + uint32_t app_image_offset = image_writer_->GetGlobalImageOffset(type.Ptr()); + data.push_back(app_image_offset); + } + } + DCHECK_EQ(data.size(), boot_image_rel_ro_entries_.size() + app_image_rel_ro_type_entries_.size()); DCHECK_OFFSET(); if (!out->WriteFully(data.data(), data.size() * sizeof(data[0]))) { PLOG(ERROR) << "Failed to write .data.img.rel.ro in " << out->GetLocation(); diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index 4e2332e3cc..dc441a11d8 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -147,11 +147,10 @@ class OatWriter { File&& dex_file_fd, const char* location); // Add dex file source from raw memory. - bool AddRawDexFileSource( - std::shared_ptr<DexFileContainer> container, - const uint8_t* dex_file_begin, - const char* location, - uint32_t location_checksum); + bool AddRawDexFileSource(const std::shared_ptr<DexFileContainer>& container, + const uint8_t* dex_file_begin, + const char* location, + uint32_t location_checksum); // Add dex file source(s) from a vdex file. bool AddVdexDexFilesSource( const VdexFile& vdex_file, @@ -215,6 +214,10 @@ class OatWriter { return data_img_rel_ro_size_; } + size_t GetDataImgRelRoAppImageOffset() const { + return data_img_rel_ro_app_image_offset_; + } + size_t GetBssSize() const { return bss_size_; } @@ -419,6 +422,9 @@ class OatWriter { // The size of the optional .data.img.rel.ro section holding the image relocations. size_t data_img_rel_ro_size_; + // The start of app image relocations in the .data.img.rel.ro section. + size_t data_img_rel_ro_app_image_offset_; + // The start of the optional .bss section. size_t bss_start_; @@ -462,6 +468,11 @@ class OatWriter { // The value is the target offset for patching, starting at `bss_start_ + bss_methods_offset_`. SafeMap<MethodReference, size_t, MethodReferenceValueComparator> bss_method_entries_; + // Map for allocating app image Class entries in .data.img.rel.ro. Indexed by TypeReference for + // the source type in the dex file with the "type value comparator" for deduplication. The value + // is the target offset for patching, starting at `data_img_rel_ro_start_`. + SafeMap<TypeReference, size_t, TypeReferenceValueComparator> app_image_rel_ro_type_entries_; + // Map for allocating Class entries in .bss. Indexed by TypeReference for the source // type in the dex file with the "type value comparator" for deduplication. The value // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`. diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 0972e5d14a..f2a6ee47f0 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -211,6 +211,7 @@ class OatTest : public CommonCompilerDriverTest { elf_writer->PrepareDynamicSection(oat_writer.GetOatHeader().GetExecutableOffset(), oat_writer.GetCodeSize(), oat_writer.GetDataImgRelRoSize(), + oat_writer.GetDataImgRelRoAppImageOffset(), oat_writer.GetBssSize(), oat_writer.GetBssMethodsOffset(), oat_writer.GetBssRootsOffset(), diff --git a/dex2oat/verifier_deps_test.cc b/dex2oat/verifier_deps_test.cc index a1e93b750e..1ede9e07bc 100644 --- a/dex2oat/verifier_deps_test.cc +++ b/dex2oat/verifier_deps_test.cc @@ -310,7 +310,7 @@ class VerifierDepsTest : public CommonCompilerDriverTest { // Load the dex file again with a new class loader, decode the VerifierDeps // in `buffer`, allow the caller to modify the deps and then run validation. template<typename Fn> - bool RunValidation(Fn fn, const std::vector<uint8_t>& buffer, std::string* error_msg) { + bool RunValidation(Fn fn, const std::vector<uint8_t>& buffer) { ScopedObjectAccess soa(Thread::Current()); jobject second_loader = LoadDex("VerifierDeps"); @@ -329,10 +329,9 @@ class VerifierDepsTest : public CommonCompilerDriverTest { Handle<mirror::ClassLoader> new_class_loader = hs.NewHandle<mirror::ClassLoader>(soa.Decode<mirror::ClassLoader>(second_loader)); - return decoded_deps.ValidateDependencies(soa.Self(), - new_class_loader, - second_dex_files, - error_msg); + return decoded_deps.ValidateDependenciesAndUpdateStatus(soa.Self(), + new_class_loader, + second_dex_files); } std::unique_ptr<verifier::VerifierDeps> verifier_deps_; @@ -564,8 +563,6 @@ TEST_F(VerifierDepsTest, UnverifiedOrder) { } TEST_F(VerifierDepsTest, VerifyDeps) { - std::string error_msg; - VerifyDexFile(); ASSERT_EQ(1u, NumberOfCompiledDexFiles()); ASSERT_TRUE(HasEachKindOfRecord()); @@ -579,8 +576,7 @@ TEST_F(VerifierDepsTest, VerifyDeps) { ASSERT_FALSE(buffer.empty()); // Check that dependencies are satisfied after decoding `buffer`. - ASSERT_TRUE(RunValidation([](VerifierDeps::DexFileDeps&) {}, buffer, &error_msg)) - << error_msg; + ASSERT_TRUE(RunValidation([](VerifierDeps::DexFileDeps&) {}, buffer)); } TEST_F(VerifierDepsTest, CompilerDriver) { diff --git a/dexopt_chroot_setup/Android.bp b/dexopt_chroot_setup/Android.bp index 22c3db6b83..23ba4beede 100644 --- a/dexopt_chroot_setup/Android.bp +++ b/dexopt_chroot_setup/Android.bp @@ -88,7 +88,6 @@ cc_fuzz { name: "dexopt_chroot_setup_fuzzer", defaults: [ "service_fuzzer_defaults", - "art_module_source_build_defaults", "dexopt_chroot_setup_defaults", ], srcs: ["dexopt_chroot_setup_fuzzer.cc"], diff --git a/dexopt_chroot_setup/dexopt_chroot_setup.cc b/dexopt_chroot_setup/dexopt_chroot_setup.cc index 967cb20c1e..35343c5b3c 100644 --- a/dexopt_chroot_setup/dexopt_chroot_setup.cc +++ b/dexopt_chroot_setup/dexopt_chroot_setup.cc @@ -66,7 +66,6 @@ using ::android::base::ReadFileToString; using ::android::base::Result; using ::android::base::SetProperty; using ::android::base::Split; -using ::android::base::StringReplace; using ::android::base::Tokenize; using ::android::base::WaitForProperty; using ::android::fs_mgr::FstabEntry; @@ -83,7 +82,7 @@ const NoDestructor<std::string> kBindMountTmpDir( constexpr mode_t kChrootDefaultMode = 0755; constexpr std::chrono::milliseconds kSnapshotCtlTimeout = std::chrono::seconds(60); -bool IsOtaUpdate(const std::optional<std::string> ota_slot) { return ota_slot.has_value(); } +bool IsOtaUpdate(const std::optional<std::string>& ota_slot) { return ota_slot.has_value(); } Result<void> Run(std::string_view log_name, const std::vector<std::string>& args) { LOG(INFO) << "Running " << log_name << ": " << Join(args, /*separator=*/" "); @@ -97,13 +96,17 @@ Result<void> Run(std::string_view log_name, const std::vector<std::string>& args return {}; } -Result<std::string> GetArtExec() { +Result<CmdlineBuilder> GetArtExecCmdlineBuilder() { std::string error_msg; std::string art_root = GetArtRootSafe(&error_msg); if (!error_msg.empty()) { return Error() << error_msg; } - return art_root + "/bin/art_exec"; + CmdlineBuilder args; + args.Add(art_root + "/bin/art_exec") + .Add("--chroot=%s", DexoptChrootSetup::CHROOT_DIR) + .Add("--process-name-suffix=Pre-reboot Dexopt chroot"); + return args; } Result<void> CreateDir(const std::string& path) { @@ -408,19 +411,15 @@ Result<void> DexoptChrootSetup::SetUpChroot(const std::optional<std::string>& ot PLOG(WARNING) << "Failed to generate empty linker config to suppress warnings"; } - CmdlineBuilder args; - args.Add(OR_RETURN(GetArtExec())) - .Add("--chroot=%s", CHROOT_DIR) - .Add("--") + CmdlineBuilder args = OR_RETURN(GetArtExecCmdlineBuilder()); + args.Add("--") .Add("/system/bin/apexd") .Add("--otachroot-bootstrap") .AddIf(!IsOtaUpdate(ota_slot), "--also-include-staged-apexes"); OR_RETURN(Run("apexd", args.Get())); - args = CmdlineBuilder(); - args.Add(OR_RETURN(GetArtExec())) - .Add("--chroot=%s", CHROOT_DIR) - .Add("--drop-capabilities") + args = OR_RETURN(GetArtExecCmdlineBuilder()); + args.Add("--drop-capabilities") .Add("--") .Add("/apex/com.android.runtime/bin/linkerconfig") .Add("--target") @@ -431,24 +430,24 @@ Result<void> DexoptChrootSetup::SetUpChroot(const std::optional<std::string>& ot } Result<void> DexoptChrootSetup::TearDownChroot() const { - if (OS::FileExists(PathInChroot("/system/bin/apexd").c_str())) { - CmdlineBuilder args; - args.Add(OR_RETURN(GetArtExec())) - .Add("--chroot=%s", CHROOT_DIR) - .Add("--") + std::vector<FstabEntry> apex_entries = + OR_RETURN(GetProcMountsDescendantsOfPath(PathInChroot("/apex"))); + // If there is only one entry, it's /apex itself. + bool has_apex = apex_entries.size() > 1; + + if (has_apex && OS::FileExists(PathInChroot("/system/bin/apexd").c_str())) { + // Delegate to apexd to unmount all APEXes. It also cleans up loop devices. + CmdlineBuilder args = OR_RETURN(GetArtExecCmdlineBuilder()); + args.Add("--") .Add("/system/bin/apexd") .Add("--unmount-all") .Add("--also-include-staged-apexes"); - if (Result<void> result = Run("apexd", args.Get()); !result.ok()) { - // Maybe apexd is not executable because a previous setup/teardown failed halfway (e.g., - // /system is currently mounted but /dev is not). We do a check below to see if there is any - // unmounted APEXes. - LOG(WARNING) << "Failed to run apexd: " << result.error().message(); - } + OR_RETURN(Run("apexd", args.Get())); } - std::vector<FstabEntry> apex_entries = - OR_RETURN(GetProcMountsDescendantsOfPath(PathInChroot("/apex"))); + // Double check to make sure all APEXes are unmounted, just in case apexd incorrectly reported + // success. + apex_entries = OR_RETURN(GetProcMountsDescendantsOfPath(PathInChroot("/apex"))); for (const FstabEntry& entry : apex_entries) { if (entry.mount_point != PathInChroot("/apex")) { return Errorf("apexd didn't unmount '{}'. See logs for details", entry.mount_point); diff --git a/dexopt_chroot_setup/dexopt_chroot_setup_test.cc b/dexopt_chroot_setup/dexopt_chroot_setup_test.cc index 9ee33dfece..bfa38e0aa0 100644 --- a/dexopt_chroot_setup/dexopt_chroot_setup_test.cc +++ b/dexopt_chroot_setup/dexopt_chroot_setup_test.cc @@ -74,7 +74,7 @@ class DexoptChrootSetupTest : public CommonArtTest { GTEST_SKIP() << "A real Pre-reboot Dexopt is running"; } - ASSERT_TRUE(WaitForProperty("dev.bootcomplete", "1", /*timeout=*/std::chrono::minutes(3))); + ASSERT_TRUE(WaitForProperty("dev.bootcomplete", "1", /*relative_timeout=*/std::chrono::minutes(3))); test_skipped = false; diff --git a/libartbase/base/arena_allocator.cc b/libartbase/base/arena_allocator.cc index d38a64e82a..0bbc816272 100644 --- a/libartbase/base/arena_allocator.cc +++ b/libartbase/base/arena_allocator.cc @@ -160,9 +160,7 @@ void ArenaAllocatorStatsImpl<kCount>::Dump(std::ostream& os, const Arena* first, } #pragma GCC diagnostic push -#if __clang_major__ >= 4 #pragma GCC diagnostic ignored "-Winstantiation-after-specialization" -#endif // We're going to use ArenaAllocatorStatsImpl<kArenaAllocatorCountAllocations> which needs // to be explicitly instantiated if kArenaAllocatorCountAllocations is true. Explicit // instantiation of the specialization ArenaAllocatorStatsImpl<false> does not do anything diff --git a/libartbase/base/memory_tool.h b/libartbase/base/memory_tool.h index 675ceb2cfd..4a93a9642f 100644 --- a/libartbase/base/memory_tool.h +++ b/libartbase/base/memory_tool.h @@ -68,10 +68,12 @@ constexpr size_t kMemoryToolStackGuardSizeScale = 1; #if __has_feature(hwaddress_sanitizer) # define HWADDRESS_SANITIZER +constexpr bool kHwAsanEnabled = true; // NB: The attribute also implies NO_INLINE. If inlined, the hwasan attribute would be lost. // If method is also separately marked as ALWAYS_INLINE, the NO_INLINE takes precedence. # define ATTRIBUTE_NO_SANITIZE_HWADDRESS __attribute__((no_sanitize("hwaddress"), noinline)) #else +constexpr bool kHwAsanEnabled = false; # define ATTRIBUTE_NO_SANITIZE_HWADDRESS #endif diff --git a/libartbase/base/unix_file/fd_file_test.cc b/libartbase/base/unix_file/fd_file_test.cc index 0f56f7b03f..d5c3056393 100644 --- a/libartbase/base/unix_file/fd_file_test.cc +++ b/libartbase/base/unix_file/fd_file_test.cc @@ -227,8 +227,8 @@ TEST_F(FdFileTest, Rename) { // Move the file via a rename. art::ScratchFile dest; - std::string new_filename = dest.GetFilename(); - std::string old_filename = src->GetFilename(); + const std::string& new_filename = dest.GetFilename(); + const std::string& old_filename = src->GetFilename(); ASSERT_TRUE(src->GetFile()->Rename(new_filename)); // Confirm the FdFile path has correctly updated. diff --git a/libartservice/service/Android.bp b/libartservice/service/Android.bp index fe0c3af5e0..ef28353666 100644 --- a/libartservice/service/Android.bp +++ b/libartservice/service/Android.bp @@ -82,6 +82,7 @@ java_defaults { libs: [ "androidx.annotation_annotation", "auto_value_annotations", + "sdk_module-lib_current_framework-configinfrastructure", "sdk_module-lib_current_framework-permission-s", // TODO(b/256866172): Transitive dependency, for r8 only. "framework-statsd.stubs.module_lib", @@ -230,9 +231,8 @@ android_test { "javatests/**/*.java", ], - // disable the target when prebuilt modules are used - defaults: [ - "art_module_source_build_java_defaults", + libs: [ + "sdk_module-lib_current_framework-configinfrastructure", ], static_libs: [ diff --git a/libartservice/service/java/com/android/server/art/ArtFileManager.java b/libartservice/service/java/com/android/server/art/ArtFileManager.java index 234ca01f7c..754b9ec1dd 100644 --- a/libartservice/service/java/com/android/server/art/ArtFileManager.java +++ b/libartservice/service/java/com/android/server/art/ArtFileManager.java @@ -29,7 +29,6 @@ import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.UserHandle; import android.os.UserManager; -import android.util.Log; import android.util.Pair; import androidx.annotation.RequiresApi; @@ -57,8 +56,6 @@ import java.util.Objects; */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public class ArtFileManager { - private static final String TAG = ArtManagerLocal.TAG; - @NonNull private final Injector mInjector; public ArtFileManager(@NonNull Context context) { @@ -170,8 +167,7 @@ public class ArtFileManager { } } } catch (ServiceSpecificException e) { - Log.e(TAG, - String.format( + AsLog.e(String.format( "Failed to get dexopt status [packageName = %s, dexPath = %s, " + "isa = %s, classLoaderContext = %s]", pkgState.getPackageName(), dexInfo.dexPath(), abi.isa(), diff --git a/libartservice/service/java/com/android/server/art/ArtJni.java b/libartservice/service/java/com/android/server/art/ArtJni.java index 61384ca5f6..d46b889460 100644 --- a/libartservice/service/java/com/android/server/art/ArtJni.java +++ b/libartservice/service/java/com/android/server/art/ArtJni.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Build; import android.os.RemoteException; -import android.util.Log; import androidx.annotation.RequiresApi; diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java index afe2ab0538..ffbd66a99a 100644 --- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java +++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java @@ -57,7 +57,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; import android.text.TextUtils; -import android.util.Log; import android.util.Pair; import androidx.annotation.RequiresApi; @@ -121,9 +120,6 @@ import java.util.stream.Stream; */ @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) public final class ArtManagerLocal { - /** @hide */ - public static final String TAG = "ArtService"; - private static final String[] CLASSPATHS_FOR_BOOT_IMAGE_PROFILE = { "BOOTCLASSPATH", "SYSTEMSERVERCLASSPATH", "STANDALONE_SYSTEMSERVER_JARS"}; @@ -490,8 +486,7 @@ public final class ArtManagerLocal { dexoptResults.put(ArtFlags.PASS_DOWNGRADE, downgradeResult); } } - Log.i(TAG, - "Dexopting " + params.getPackages().size() + " packages with reason=" + reason); + AsLog.i("Dexopting " + params.getPackages().size() + " packages with reason=" + reason); DexoptResult mainResult = mInjector.getDexoptHelper().dexopt(snapshot, params.getPackages(), params.getDexoptParams(), cancellationSignal, dexoptExecutor, progressCallbackExecutor, @@ -760,9 +755,8 @@ public final class ArtManagerLocal { PrimaryDexUtils.buildOutputProfile(pkgState, dexInfo, Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */, false /* isPreReboot */)); if (!result.externalProfileErrors().isEmpty()) { - Log.e(TAG, - "Error occurred when initializing from external profiles: " - + result.externalProfileErrors()); + AsLog.e("Error occurred when initializing from external profiles: " + + result.externalProfileErrors()); } ProfilePath refProfile = result.profile(); @@ -930,6 +924,11 @@ public final class ArtManagerLocal { @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) public void onApexStaged(@NonNull String[] stagedApexModuleNames) { // TODO(b/311377497): Check system requirements. + mInjector.getPreRebootDexoptJob().unschedule(); + // Although `unschedule` implies `cancel`, we explicitly call `cancel` here to wait for + // the job to exit, if it's running. + mInjector.getPreRebootDexoptJob().cancel(true /* blocking */); + mInjector.getPreRebootDexoptJob().updateOtaSlot(null); mInjector.getPreRebootDexoptJob().schedule(); } @@ -1093,8 +1092,9 @@ public final class ArtManagerLocal { runtimeArtifactsToKeep.addAll(artifactLists.runtimeArtifacts()); } } - return mInjector.getArtd().cleanup( - profilesToKeep, artifactsToKeep, vdexFilesToKeep, runtimeArtifactsToKeep); + return mInjector.getArtd().cleanup(profilesToKeep, artifactsToKeep, vdexFilesToKeep, + runtimeArtifactsToKeep, + SdkLevel.isAtLeastV() && mInjector.getPreRebootDexoptJob().hasStarted()); } catch (RemoteException e) { Utils.logArtdException(e); return 0; @@ -1140,8 +1140,7 @@ public final class ArtManagerLocal { // regression. mInjector.getArtd().commitPreRebootStagedFiles(artifacts, profiles); } catch (ServiceSpecificException e) { - Log.e(TAG, - "Failed to commit Pre-reboot staged files for package '" + AsLog.e("Failed to commit Pre-reboot staged files for package '" + pkgState.getPackageName() + "'", e); } @@ -1187,15 +1186,14 @@ public final class ArtManagerLocal { .filter(pkg -> !excludedPackages.contains(pkg)) .collect(Collectors.toList()); if (!packages.isEmpty()) { - Log.i(TAG, "Storage is low. Downgrading " + packages.size() + " inactive packages"); + AsLog.i("Storage is low. Downgrading " + packages.size() + " inactive packages"); DexoptParams params = new DexoptParams.Builder(ReasonMapping.REASON_INACTIVE).build(); 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"); + AsLog.i("Storage is low, but downgrading is disabled or there's nothing to " + + "downgrade"); } } return null; @@ -1207,7 +1205,7 @@ public final class ArtManagerLocal { return mInjector.getStorageManager().getAllocatableBytes(StorageManager.UUID_DEFAULT) < DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES; } catch (IOException e) { - Log.e(TAG, "Failed to check storage. Assuming storage not low", e); + AsLog.e("Failed to check storage. Assuming storage not low", e); return false; } } @@ -1247,9 +1245,8 @@ public final class ArtManagerLocal { ArtFlags.FLAG_FORCE_MERGE_PROFILE) .build(); - Log.i(TAG, - "Dexopting " + packageNames.size() + " packages with reason=" - + dexoptParams.getReason() + " (supplementary pass)"); + AsLog.i("Dexopting " + packageNames.size() + + " packages with reason=" + dexoptParams.getReason() + " (supplementary pass)"); return mInjector.getDexoptHelper().dexopt(snapshot, packageNames, dexoptParams, cancellationSignal, dexoptExecutor, progressCallbackExecutor, progressCallback); } @@ -1450,8 +1447,10 @@ public final class ArtManagerLocal { * 1. The default compiler filter for the given reason. * 2. The compiler filter set explicitly by {@link DexoptParams.Builder#setCompilerFilter}. * 3. ART Service's internal adjustments to upgrade the compiler filter, based on whether - * the package is System UI, etc. - * 4. The adjustments made by this callback. + * the package is System UI, etc. (Not applicable if the dexopt is initiated by a shell + * command with an explicit "-m" flag.) + * 4. The adjustments made by this callback. (Not applicable if the dexopt is initiated by a + * shell command with an explicit "-m" flag.) * 5. ART Service's internal adjustments to downgrade the compiler filter, based on whether * the profile is available, etc. * diff --git a/libartservice/service/java/com/android/server/art/ArtShellCommand.java b/libartservice/service/java/com/android/server/art/ArtShellCommand.java index 9203143abe..ee1aa88b50 100644 --- a/libartservice/service/java/com/android/server/art/ArtShellCommand.java +++ b/libartservice/service/java/com/android/server/art/ArtShellCommand.java @@ -40,7 +40,6 @@ import android.os.Process; import android.system.ErrnoException; import android.system.Os; import android.system.StructStat; -import android.util.Log; import androidx.annotation.RequiresApi; @@ -88,8 +87,6 @@ import java.util.stream.Collectors; */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public final class ArtShellCommand extends BasicShellCommandHandler { - private static final String TAG = ArtManagerLocal.TAG; - /** The default location for profile dumps. */ private final static String PROFILE_DEBUG_LOCATION = "/data/misc/profman"; @@ -190,6 +187,9 @@ public final class ArtShellCommand extends BasicShellCommandHandler { // TODO(b/311377497): Remove this command once the development is done. return handlePreRebootDexopt(pw); } + case "on-ota-staged": { + return handleOnOtaStaged(pw); + } default: pw.printf("Error: Unknown 'art' sub-command '%s'\n", subcmd); pw.println("See 'pm help' for help"); @@ -210,6 +210,7 @@ public final class ArtShellCommand extends BasicShellCommandHandler { boolean legacyClearProfile = false; boolean verbose = false; boolean forceMergeProfile = false; + boolean forceCompilerFilter = false; String opt; while ((opt = getNextOption()) != null) { @@ -222,6 +223,7 @@ public final class ArtShellCommand extends BasicShellCommandHandler { break; case "-m": compilerFilter = getNextArgRequired(); + forceCompilerFilter = true; break; case "-p": priorityClass = parsePriorityClass(getNextArgRequired()); @@ -312,6 +314,10 @@ public final class ArtShellCommand extends BasicShellCommandHandler { paramsBuilder.setFlags( ArtFlags.FLAG_FORCE_MERGE_PROFILE, ArtFlags.FLAG_FORCE_MERGE_PROFILE); } + if (forceCompilerFilter) { + paramsBuilder.setFlags( + ArtFlags.FLAG_FORCE_COMPILER_FILTER, ArtFlags.FLAG_FORCE_COMPILER_FILTER); + } if (splitArg != null) { if (scopeFlags != 0) { pw.println("Error: '--primary-dex', '--secondary-dex', " @@ -667,6 +673,67 @@ public final class ArtShellCommand extends BasicShellCommandHandler { } } + private int handleOnOtaStaged(@NonNull PrintWriter pw) { + if (!SdkLevel.isAtLeastV()) { + pw.println("Error: Unsupported command 'on-ota-staged'"); + return 1; + } + + int uid = Binder.getCallingUid(); + if (uid != Process.ROOT_UID) { + throw new SecurityException("Only root can call 'on-ota-staged'"); + } + + String otaSlot = null; + + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--slot": + otaSlot = getNextArgRequired(); + break; + default: + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + + if (otaSlot == null) { + pw.println("Error: '--slot' must be specified"); + return 1; + } + + int code; + + // This operation requires the uid to be "system" (1000). + long identityToken = Binder.clearCallingIdentity(); + try { + mArtManagerLocal.getPreRebootDexoptJob().unschedule(); + // Although `unschedule` implies `cancel`, we explicitly call `cancel` here to wait for + // the job to exit, if it's running. + mArtManagerLocal.getPreRebootDexoptJob().cancel(true /* blocking */); + mArtManagerLocal.getPreRebootDexoptJob().updateOtaSlot(otaSlot); + code = mArtManagerLocal.getPreRebootDexoptJob().schedule(); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + + switch (code) { + case ArtFlags.SCHEDULE_SUCCESS: + pw.println("Job scheduled"); + return 0; + case ArtFlags.SCHEDULE_DISABLED_BY_SYSPROP: + pw.println("Job disabled by system property"); + return 1; + case ArtFlags.SCHEDULE_JOB_SCHEDULER_FAILURE: + pw.println("Failed to schedule job"); + return 1; + default: + // Can't happen. + throw new IllegalStateException("Unknown result code: " + code); + } + } + @Override public void onHelp() { // No one should call this. The help text should be printed by the `onHelp` handler of `cmd @@ -816,6 +883,12 @@ public final class ArtShellCommand extends BasicShellCommandHandler { pw.println(" This command is different from 'pm compile -r REASON -a'. For example, it"); pw.println(" only dexopts a subset of apps, and it runs dexopt in parallel. See the"); pw.println(" API documentation for 'ArtManagerLocal.dexoptPackages' for details."); + pw.println(); + pw.println(" on-ota-staged --slot SLOT"); + pw.println(" Notifies ART Service that an OTA update is staged. A Pre-reboot Dexopt"); + pw.println(" job is scheduled for it."); + pw.println(" Options:"); + pw.println(" --slot SLOT The slot that contains the OTA update, '_a' or '_b'."); } private void enforceRootOrShell() { diff --git a/libartservice/service/java/com/android/server/art/ArtdRefCache.java b/libartservice/service/java/com/android/server/art/ArtdRefCache.java index c4ed71203c..148e4759b7 100644 --- a/libartservice/service/java/com/android/server/art/ArtdRefCache.java +++ b/libartservice/service/java/com/android/server/art/ArtdRefCache.java @@ -44,7 +44,6 @@ import java.util.concurrent.ScheduledExecutorService; */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public class ArtdRefCache { - private static final String TAG = ArtManagerLocal.TAG; // The 15s timeout is arbitrarily picked. // TODO(jiakaiz): Revisit this based on real CUJs. @VisibleForTesting public static final long CACHE_TIMEOUT_MS = 15_000; diff --git a/libartservice/service/java/com/android/server/art/AsLog.java b/libartservice/service/java/com/android/server/art/AsLog.java new file mode 100644 index 0000000000..ac6ae472cc --- /dev/null +++ b/libartservice/service/java/com/android/server/art/AsLog.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.art; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Build; +import android.util.Log; +import android.util.Slog; + +import androidx.annotation.RequiresApi; + +/** + * A log wrapper that logs messages with the appropriate tag. + * + * @hide + */ +@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) +public class AsLog { + private static final String TAG = "ArtService"; + private static final String PRE_REBOOT_TAG = "ArtServicePreReboot"; + + @NonNull + public static String getTag() { + return GlobalInjector.getInstance().isPreReboot() ? PRE_REBOOT_TAG : TAG; + } + + public static void v(@NonNull String msg) { + Log.v(getTag(), msg); + } + + public static void v(@NonNull String msg, @Nullable Throwable tr) { + Log.v(getTag(), msg, tr); + } + + public static void d(@NonNull String msg) { + Log.d(getTag(), msg); + } + + public static void d(@NonNull String msg, @Nullable Throwable tr) { + Log.d(getTag(), msg, tr); + } + + public static void i(@NonNull String msg) { + Log.i(getTag(), msg); + } + + public static void i(@NonNull String msg, @Nullable Throwable tr) { + Log.i(getTag(), msg, tr); + } + + public static void w(@NonNull String msg) { + Log.w(getTag(), msg); + } + + public static void w(@NonNull String msg, @Nullable Throwable tr) { + Log.w(getTag(), msg, tr); + } + + public static void e(@NonNull String msg) { + Log.e(getTag(), msg); + } + + public static void e(@NonNull String msg, @Nullable Throwable tr) { + Log.e(getTag(), msg, tr); + } + + public static void wtf(@NonNull String msg) { + Slog.wtf(getTag(), msg); + } + + public static void wtf(@NonNull String msg, @Nullable Throwable tr) { + Slog.wtf(getTag(), msg, tr); + } +} diff --git a/libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java b/libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java index 289c7cd235..6dda5c9980 100644 --- a/libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java +++ b/libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java @@ -32,8 +32,6 @@ import android.os.Build; import android.os.CancellationSignal; import android.os.SystemClock; import android.os.SystemProperties; -import android.util.Log; -import android.util.Slog; import androidx.annotation.RequiresApi; @@ -61,8 +59,6 @@ import java.util.function.Consumer; /** @hide */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public class BackgroundDexoptJob implements ArtServiceJobInterface { - private static final String TAG = ArtManagerLocal.TAG; - /** * "android" is the package name for a <service> declared in * frameworks/base/core/res/AndroidManifest.xml @@ -98,7 +94,7 @@ public class BackgroundDexoptJob implements ArtServiceJobInterface { writeStats(result); } catch (RuntimeException e) { // Not expected. Log wtf to surface it. - Slog.wtf(TAG, "Failed to write stats", e); + AsLog.wtf("Failed to write stats", e); } // This is a periodic job, where the interval is specified in the `JobInfo`. "true" @@ -132,7 +128,7 @@ public class BackgroundDexoptJob implements ArtServiceJobInterface { } if (SystemProperties.getBoolean("pm.dexopt.disable_bg_dexopt", false /* def */)) { - Log.i(TAG, "Job is disabled by system property 'pm.dexopt.disable_bg_dexopt'"); + AsLog.i("Job is disabled by system property 'pm.dexopt.disable_bg_dexopt'"); return ArtFlags.SCHEDULE_DISABLED_BY_SYSPROP; } @@ -177,17 +173,17 @@ public class BackgroundDexoptJob implements ArtServiceJobInterface { @NonNull public synchronized CompletableFuture<Result> start() { if (mRunningJob != null) { - Log.i(TAG, "Job is already running"); + AsLog.i("Job is already running"); return mRunningJob; } mCancellationSignal = new CancellationSignal(); mLastStopReason = Optional.empty(); mRunningJob = new CompletableFuture().supplyAsync(() -> { - try (var tracing = new Utils.TracingWithTimingLogging(TAG, "jobExecution")) { + try (var tracing = new Utils.TracingWithTimingLogging(AsLog.getTag(), "jobExecution")) { return run(mCancellationSignal); } catch (RuntimeException e) { - Log.e(TAG, "Fatal error", e); + AsLog.e("Fatal error", e); return new FatalErrorResult(); } finally { synchronized (this) { @@ -201,12 +197,12 @@ public class BackgroundDexoptJob implements ArtServiceJobInterface { public synchronized void cancel() { if (mRunningJob == null) { - Log.i(TAG, "Job is not running"); + AsLog.i("Job is not running"); return; } mCancellationSignal.cancel(); - Log.i(TAG, "Job cancelled"); + AsLog.i("Job cancelled"); } @Nullable @@ -247,7 +243,7 @@ public class BackgroundDexoptJob implements ArtServiceJobInterface { // lose some chance to dexopt when the storage is very low, but it's fine because we // can still dexopt in the next run. long freedBytes = mInjector.getArtManagerLocal().cleanup(snapshot); - Log.i(TAG, String.format("Freed %d bytes", freedBytes)); + AsLog.i(String.format("Freed %d bytes", freedBytes)); } } return CompletedResult.create(dexoptResultByPass, durationMsByPass); diff --git a/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java b/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java index def1feb353..106b1c6c2a 100644 --- a/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java +++ b/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.server.art; import static com.android.server.art.model.ArtFlags.BatchDexoptPass; @@ -18,7 +34,7 @@ import java.util.Optional; import java.util.stream.Collectors; /** - * This is an helper class to report the background DexOpt job metrics to StatsD. + * This is a helper class to report the background DexOpt job metrics to StatsD. * * @hide */ diff --git a/libartservice/service/java/com/android/server/art/DexMetadataHelper.java b/libartservice/service/java/com/android/server/art/DexMetadataHelper.java index 1252123479..99d92083d3 100644 --- a/libartservice/service/java/com/android/server/art/DexMetadataHelper.java +++ b/libartservice/service/java/com/android/server/art/DexMetadataHelper.java @@ -19,7 +19,6 @@ package com.android.server.art; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Build; -import android.util.Log; import androidx.annotation.RequiresApi; @@ -43,8 +42,6 @@ import java.util.zip.ZipFile; */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public class DexMetadataHelper { - private static final String TAG = ArtManagerLocal.TAG; - @NonNull private final Injector mInjector; public DexMetadataHelper() { @@ -73,7 +70,7 @@ public class DexMetadataHelper { } } catch (IOException e) { if (!(e instanceof FileNotFoundException || e instanceof NoSuchFileException)) { - Log.e(TAG, String.format("Failed to read dm file '%s'", realDmPath), e); + AsLog.e(String.format("Failed to read dm file '%s'", realDmPath), e); } return getDefaultDexMetadataInfo(); } diff --git a/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java b/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java index d5b0bab949..9ef804919a 100644 --- a/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java +++ b/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java @@ -30,7 +30,6 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.UserHandle; -import android.util.Log; import androidx.annotation.RequiresApi; @@ -94,7 +93,6 @@ import java.util.stream.Collectors; @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public class DexUseManagerLocal { - private static final String TAG = ArtManagerLocal.TAG; private static final String FILENAME = "/data/system/package-dex-usage.pb"; /** @@ -557,7 +555,7 @@ public class DexUseManagerLocal { } } } catch (IOException e) { - Log.e(TAG, "Failed to save dex use data", e); + AsLog.e("Failed to save dex use data", e); } finally { Utils.deleteIfExistsSafe(tempFile); } @@ -575,7 +573,7 @@ public class DexUseManagerLocal { proto = DexUseProto.parseFrom(in); } catch (IOException e) { // Nothing else we can do but to start from scratch. - Log.e(TAG, "Failed to load dex use data", e); + AsLog.e("Failed to load dex use data", e); } synchronized (mLock) { if (mDexUse != null) { @@ -636,7 +634,7 @@ public class DexUseManagerLocal { try { return mInjector.getArtd().getDexFileVisibility(dexPath); } catch (ServiceSpecificException | RemoteException e) { - Log.e(TAG, "Failed to get visibility of " + dexPath, e); + AsLog.e("Failed to get visibility of " + dexPath, e); return FileVisibility.NOT_FOUND; } } @@ -954,7 +952,7 @@ public class DexUseManagerLocal { // Skip invalid dex paths persisted by previous versions. String errorMsg = validateDexPath.apply(dexFile); if (errorMsg != null) { - Log.e(TAG, errorMsg); + AsLog.e(errorMsg); continue; } @@ -1018,7 +1016,7 @@ public class DexUseManagerLocal { String errorMsg = validateClassLoaderContext.apply( Utils.assertNonEmpty(recordProto.getClassLoaderContext())); if (errorMsg != null) { - Log.e(TAG, errorMsg); + AsLog.e(errorMsg); continue; } @@ -1027,10 +1025,9 @@ public class DexUseManagerLocal { if (!Utils.isNativeAbi(record.mAbiName)) { // The native ABI set has changed by an OTA since the ABI name was recorded. - Log.i(TAG, - String.format("Ignoring secondary dex use record with non-native ABI " - + "'%s' for '%s'", - record.mAbiName, proto.getDexFile())); + AsLog.i(String.format("Ignoring secondary dex use record with non-native ABI " + + "'%s' for '%s'", + record.mAbiName, proto.getDexFile())); continue; } diff --git a/libartservice/service/java/com/android/server/art/DexoptHelper.java b/libartservice/service/java/com/android/server/art/DexoptHelper.java index 8f402918f6..76edfd503f 100644 --- a/libartservice/service/java/com/android/server/art/DexoptHelper.java +++ b/libartservice/service/java/com/android/server/art/DexoptHelper.java @@ -69,8 +69,6 @@ import java.util.stream.Collectors; */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public class DexoptHelper { - private static final String TAG = ArtManagerLocal.TAG; - @NonNull private final Injector mInjector; public DexoptHelper(@NonNull Context context, @NonNull Config config) { diff --git a/libartservice/service/java/com/android/server/art/Dexopter.java b/libartservice/service/java/com/android/server/art/Dexopter.java index 69b861527e..b8aa912723 100644 --- a/libartservice/service/java/com/android/server/art/Dexopter.java +++ b/libartservice/service/java/com/android/server/art/Dexopter.java @@ -40,7 +40,6 @@ import android.os.SystemProperties; import android.os.UserManager; import android.os.storage.StorageManager; import android.text.TextUtils; -import android.util.Log; import android.util.Pair; import androidx.annotation.RequiresApi; @@ -69,7 +68,6 @@ import java.util.Objects; /** @hide */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public abstract class Dexopter<DexInfoType extends DetailedDexInfo> { - private static final String TAG = ArtManagerLocal.TAG; private static final List<String> ART_PACKAGE_NAMES = List.of("com.google.android.art", "com.android.art", "com.google.android.go.art"); @@ -88,10 +86,6 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> { mPkg = pkg; mParams = params; mCancellationSignal = cancellationSignal; - if (pkgState.getAppId() < 0) { - throw new IllegalStateException( - "Package '" + pkgState.getPackageName() + "' has invalid app ID"); - } } /** @@ -102,7 +96,7 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> { @NonNull public final List<DexContainerFileDexoptResult> dexopt() throws RemoteException { if (SystemProperties.getBoolean("dalvik.vm.disable-art-service-dexopt", false /* def */)) { - Log.i(TAG, "Dexopt skipped because it's disabled by system property"); + AsLog.i("Dexopt skipped because it's disabled by system property"); return List.of(); } @@ -135,7 +129,7 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> { if (!dmInfo.config().getEnableEmbeddedProfile()) { String dmPath = DexMetadataHelper.getDmPath( Objects.requireNonNull(dmInfo.dmPath())); - Log.i(TAG, "Embedded profile disabled by config in the dm file " + dmPath); + AsLog.i("Embedded profile disabled by config in the dm file " + dmPath); } if (needsToBeShared) { @@ -165,9 +159,12 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> { // and dex2oat already makes this transformation. However, we need to // explicitly make this transformation here to guide the later decisions // such as whether the artifacts can be public and whether dexopt is needed. - compilerFilter = needsToBeShared - ? ReasonMapping.getCompilerFilterForShared() - : "verify"; + compilerFilter = printAdjustCompilerFilterReason(compilerFilter, + needsToBeShared ? ReasonMapping.getCompilerFilterForShared() + : "verify", + "there is no valid profile" + + (needsToBeShared ? " and the package needs to be shared" + : "")); } } boolean isProfileGuidedCompilerFilter = @@ -228,7 +225,7 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> { continue; } } catch (IOException e) { - Log.e(TAG, "Failed to check storage. Assuming storage not low", e); + AsLog.e("Failed to check storage. Assuming storage not low", e); } IArtdCancellationSignal artdCancellationSignal = @@ -237,8 +234,7 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> { try { artdCancellationSignal.cancel(); } catch (RemoteException e) { - Log.e(TAG, "An error occurred when sending a cancellation signal", - e); + AsLog.e("An error occurred when sending a cancellation signal", e); } }); @@ -257,8 +253,7 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> { } } catch (ServiceSpecificException e) { // Log the error and continue. - Log.e(TAG, - String.format("Failed to dexopt [packageName = %s, dexPath = %s, " + AsLog.e(String.format("Failed to dexopt [packageName = %s, dexPath = %s, " + "isa = %s, classLoaderContext = %s]", mPkgState.getPackageName(), dexInfo.dexPath(), abi.isa(), dexInfo.classLoaderContext()), @@ -272,9 +267,8 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> { abi.isPrimaryAbi(), abi.name(), compilerFilter, status, wallTimeMs, cpuTimeMs, sizeBytes, sizeBeforeBytes, extendedStatusFlags, externalProfileErrors); - Log.i(TAG, - String.format("Dexopt result: [packageName = %s] %s", - mPkgState.getPackageName(), result)); + AsLog.i(String.format("Dexopt result: [packageName = %s] %s", + mPkgState.getPackageName(), result)); results.add(result); if (status != DexoptResult.DEXOPT_SKIPPED && status != DexoptResult.DEXOPT_PERFORMED) { @@ -323,24 +317,29 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> { @NonNull private String adjustCompilerFilter( @NonNull String targetCompilerFilter, @NonNull DexInfoType dexInfo) { - if (mInjector.isSystemUiPackage(mPkgState.getPackageName())) { - String systemUiCompilerFilter = getSystemUiCompilerFilter(); - if (!systemUiCompilerFilter.isEmpty()) { - targetCompilerFilter = systemUiCompilerFilter; + if ((mParams.getFlags() & ArtFlags.FLAG_FORCE_COMPILER_FILTER) == 0) { + if (mInjector.isSystemUiPackage(mPkgState.getPackageName())) { + String systemUiCompilerFilter = getSystemUiCompilerFilter(); + if (!systemUiCompilerFilter.isEmpty()) { + targetCompilerFilter = printAdjustCompilerFilterReason(targetCompilerFilter, + systemUiCompilerFilter, "the package is System UI"); + } + } else if (mInjector.isLauncherPackage(mPkgState.getPackageName())) { + targetCompilerFilter = printAdjustCompilerFilterReason( + targetCompilerFilter, "speed-profile", "the package is a launcher package"); } - } else if (mInjector.isLauncherPackage(mPkgState.getPackageName())) { - targetCompilerFilter = "speed-profile"; - } - Callback<AdjustCompilerFilterCallback, Void> callback = - mInjector.getConfig().getAdjustCompilerFilterCallback(); - if (callback != null) { - // Local variables passed to the lambda must be final or effectively final. - final String originalCompilerFilter = targetCompilerFilter; - targetCompilerFilter = Utils.executeAndWait(callback.executor(), () -> { - return callback.get().onAdjustCompilerFilter( - mPkgState.getPackageName(), originalCompilerFilter, mParams.getReason()); - }); + Callback<AdjustCompilerFilterCallback, Void> callback = + mInjector.getConfig().getAdjustCompilerFilterCallback(); + if (callback != null) { + // Local variables passed to the lambda must be final or effectively final. + final String originalCompilerFilter = targetCompilerFilter; + targetCompilerFilter = printAdjustCompilerFilterReason( + targetCompilerFilter, Utils.executeAndWait(callback.executor(), () -> { + return callback.get().onAdjustCompilerFilter(mPkgState.getPackageName(), + originalCompilerFilter, mParams.getReason()); + }), "of AdjustCompilerFilterCallback"); + } } // Code below should only downgrade the compiler filter. Don't upgrade the compiler filter @@ -354,13 +353,17 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> { // are done via adb shell commands). This is okay because the runtime will ignore the // compiled code anyway. if (mPkg.isVmSafeMode() || mPkg.isDebuggable()) { - targetCompilerFilter = DexFile.getSafeModeCompilerFilter(targetCompilerFilter); + targetCompilerFilter = printAdjustCompilerFilterReason(targetCompilerFilter, + DexFile.getSafeModeCompilerFilter(targetCompilerFilter), + mPkg.isVmSafeMode() ? "the package requests VM safe mode" + : "the package is debuggable"); } // We cannot do AOT compilation if we don't have a valid class loader context. if (dexInfo.classLoaderContext() == null && DexFile.isOptimizedCompilerFilter(targetCompilerFilter)) { - targetCompilerFilter = "verify"; + targetCompilerFilter = printAdjustCompilerFilterReason( + targetCompilerFilter, "verify", "there is no valid class loader context"); } // This application wants to use the embedded dex in the APK, rather than extracted or @@ -369,12 +372,14 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> { // won't extract the dex code because the APK is uncompressed, and the assumption is that // such applications always use uncompressed APKs. if (mPkg.isUseEmbeddedDex() && DexFile.isOptimizedCompilerFilter(targetCompilerFilter)) { - targetCompilerFilter = "verify"; + targetCompilerFilter = printAdjustCompilerFilterReason( + targetCompilerFilter, "verify", "the package requests to use embedded dex"); } if ((mParams.getFlags() & ArtFlags.FLAG_IGNORE_PROFILE) != 0 && DexFile.isProfileGuidedCompilerFilter(targetCompilerFilter)) { - targetCompilerFilter = "verify"; + targetCompilerFilter = printAdjustCompilerFilterReason( + targetCompilerFilter, "verify", "the user requests to ignore the profile"); } return targetCompilerFilter; @@ -390,6 +395,16 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> { return compilerFilter; } + private @NonNull String printAdjustCompilerFilterReason(@NonNull String oldCompilerFilter, + @NonNull String newCompilerFilter, @NonNull String reason) { + if (!oldCompilerFilter.equals(newCompilerFilter)) { + AsLog.i(String.format( + "Adjusting the compiler filter for '%s' from '%s' to '%s' because %s", + mPkgState.getPackageName(), oldCompilerFilter, newCompilerFilter, reason)); + } + return newCompilerFilter; + } + /** @see Utils#getOrInitReferenceProfile */ @Nullable private InitProfileResult getOrInitReferenceProfile( @@ -568,8 +583,7 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> { mInjector.getArtd().commitTmpProfile(profile); return true; } catch (ServiceSpecificException e) { - Log.e(TAG, "Failed to commit profile changes " + AidlUtils.toString(profile.finalPath), - e); + AsLog.e("Failed to commit profile changes " + AidlUtils.toString(profile.finalPath), e); return false; } } @@ -588,8 +602,7 @@ public abstract class Dexopter<DexInfoType extends DetailedDexInfo> { return ProfilePath.tmpProfilePath(output.profilePath); } } catch (ServiceSpecificException e) { - Log.e(TAG, - "Failed to merge profiles " + AidlUtils.toString(output.profilePath.finalPath), + AsLog.e("Failed to merge profiles " + AidlUtils.toString(output.profilePath.finalPath), e); } diff --git a/libartservice/service/java/com/android/server/art/DumpHelper.java b/libartservice/service/java/com/android/server/art/DumpHelper.java index 8bd2210f33..709c742231 100644 --- a/libartservice/service/java/com/android/server/art/DumpHelper.java +++ b/libartservice/service/java/com/android/server/art/DumpHelper.java @@ -24,7 +24,6 @@ import android.annotation.NonNull; import android.os.Build; import android.os.RemoteException; import android.os.ServiceSpecificException; -import android.util.Log; import androidx.annotation.RequiresApi; @@ -54,8 +53,6 @@ import java.util.stream.Collectors; */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public class DumpHelper { - private static final String TAG = ArtManagerLocal.TAG; - @NonNull private final Injector mInjector; public DumpHelper(@NonNull ArtManagerLocal artManagerLocal) { @@ -85,9 +82,7 @@ public class DumpHelper { public void dumpPackage(@NonNull PrintWriter pw, @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull PackageState pkgState) { - // An APEX has a uid of -1. - // TODO(b/256637152): Consider using `isApex` instead. - if (pkgState.getAppId() <= 0 || pkgState.getAndroidPackage() == null) { + if (pkgState.isApex() || pkgState.getAndroidPackage() == null) { return; } diff --git a/libartservice/service/java/com/android/server/art/PreRebootDexoptJob.java b/libartservice/service/java/com/android/server/art/PreRebootDexoptJob.java index b8a55af7e0..c890198d96 100644 --- a/libartservice/service/java/com/android/server/art/PreRebootDexoptJob.java +++ b/libartservice/service/java/com/android/server/art/PreRebootDexoptJob.java @@ -19,15 +19,28 @@ package com.android.server.art; import static com.android.server.art.model.ArtFlags.ScheduleStatus; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.job.JobInfo; import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.content.ComponentName; import android.content.Context; import android.os.Build; +import android.os.CancellationSignal; +import android.os.SystemProperties; +import android.provider.DeviceConfig; import androidx.annotation.RequiresApi; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.art.model.ArtFlags; import com.android.server.art.model.ArtServiceJobInterface; +import com.android.server.art.prereboot.PreRebootDriver; + +import java.time.Duration; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; /** * The Pre-reboot Dexopt job. @@ -38,8 +51,6 @@ import com.android.server.art.model.ArtServiceJobInterface; */ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) public class PreRebootDexoptJob implements ArtServiceJobInterface { - private static final String TAG = ArtManagerLocal.TAG; - /** * "android" is the package name for a <service> declared in * frameworks/base/core/res/AndroidManifest.xml @@ -50,6 +61,23 @@ public class PreRebootDexoptJob implements ArtServiceJobInterface { @NonNull private final Injector mInjector; + // Job state variables. + @GuardedBy("this") @Nullable private CompletableFuture<Void> mRunningJob = null; + @GuardedBy("this") @Nullable private CancellationSignal mCancellationSignal = null; + + /** The slot that contains the OTA update, "_a" or "_b", or null for a Mainline update. */ + @GuardedBy("this") @Nullable private String mOtaSlot = null; + + /** + * Whether the job has started at least once, meaning the device is expected to have staged + * files, no matter it succeed, failed, or cancelled. + * + * Note that this flag is not persisted across system server restarts. It's possible that the + * value is lost due to a system server restart caused by a crash, but this is a minor case, so + * we don't handle it here for simplicity. + */ + @GuardedBy("this") private boolean mHasStarted = false; + public PreRebootDexoptJob(@NonNull Context context) { this(new Injector(context)); } @@ -62,19 +90,140 @@ public class PreRebootDexoptJob implements ArtServiceJobInterface { @Override public boolean onStartJob( @NonNull BackgroundDexoptJobService jobService, @NonNull JobParameters params) { + // No need to handle exceptions thrown by the future because exceptions are handled inside + // the future itself. + var unused = start().thenRunAsync(() -> { + try { + // If it failed, it means something went wrong, so we don't reschedule the job + // because it will likely fail again. If it's cancelled, the job will be rescheduled + // because the return value of `onStopJob` will be respected, and this call will be + // ignored. + jobService.jobFinished(params, false /* wantsReschedule */); + } catch (RuntimeException e) { + AsLog.wtf("Unexpected exception", e); + } + }); // "true" means the job will continue running until `jobFinished` is called. - return false; + return true; } @Override public boolean onStopJob(@NonNull JobParameters params) { - // "true" means to execute again in the same interval with the default retry policy. + cancel(false /* blocking */); + // "true" means to execute again with the default retry policy. return true; } public @ScheduleStatus int schedule() { - // TODO(b/311377497): Schedule the job. - return ArtFlags.SCHEDULE_SUCCESS; + if (this != BackgroundDexoptJobService.getJob(JOB_ID)) { + throw new IllegalStateException("This job cannot be scheduled"); + } + + if (!SystemProperties.getBoolean("dalvik.vm.enable_pr_dexopt", false /* def */) + && !DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME, "enable_pr_dexopt", + false /* defaultValue */)) { + AsLog.i("Pre-reboot Dexopt Job is not enabled by system property"); + return ArtFlags.SCHEDULE_DISABLED_BY_SYSPROP; + } + + // If `pm.dexopt.disable_bg_dexopt` is set, the user probably means to disable any dexopt + // jobs in the background. + if (SystemProperties.getBoolean("pm.dexopt.disable_bg_dexopt", false /* def */)) { + AsLog.i("Pre-reboot Dexopt Job is disabled by system property " + + "'pm.dexopt.disable_bg_dexopt'"); + return ArtFlags.SCHEDULE_DISABLED_BY_SYSPROP; + } + + JobInfo info = new JobInfo + .Builder(JOB_ID, + new ComponentName(JOB_PKG_NAME, + BackgroundDexoptJobService.class.getName())) + .setRequiresDeviceIdle(true) + .setRequiresCharging(true) + .setRequiresBatteryNotLow(true) + // The latency is to wait for update_engine to finish. + .setMinimumLatency(Duration.ofMinutes(10).toMillis()) + .build(); + + /* @JobScheduler.Result */ int result = mInjector.getJobScheduler().schedule(info); + if (result == JobScheduler.RESULT_SUCCESS) { + AsLog.i("Pre-reboot Dexopt Job scheduled"); + return ArtFlags.SCHEDULE_SUCCESS; + } else { + AsLog.i("Failed to schedule Pre-reboot Dexopt Job"); + return ArtFlags.SCHEDULE_JOB_SCHEDULER_FAILURE; + } + } + + public void unschedule() { + if (this != BackgroundDexoptJobService.getJob(JOB_ID)) { + throw new IllegalStateException("This job cannot be unscheduled"); + } + + mInjector.getJobScheduler().cancel(JOB_ID); + } + + @NonNull + public synchronized CompletableFuture<Void> start() { + if (mRunningJob != null) { + AsLog.i("Job is already running"); + return mRunningJob; + } + + String otaSlot = mOtaSlot; + var cancellationSignal = mCancellationSignal = new CancellationSignal(); + mHasStarted = true; + mRunningJob = new CompletableFuture().runAsync(() -> { + try { + // TODO(b/336239721): Consume the result and report metrics. + mInjector.getPreRebootDriver().run(otaSlot, cancellationSignal); + } catch (RuntimeException e) { + AsLog.e("Fatal error", e); + } finally { + synchronized (this) { + mRunningJob = null; + mCancellationSignal = null; + } + } + }); + return mRunningJob; + } + + /** + * Cancels the job. + * + * @param blocking whether to wait for the job to exit. + */ + public void cancel(boolean blocking) { + CompletableFuture<Void> runningJob = null; + synchronized (this) { + if (mRunningJob == null) { + return; + } + + mCancellationSignal.cancel(); + AsLog.i("Job cancelled"); + runningJob = mRunningJob; + } + if (blocking) { + Utils.getFuture(runningJob); + } + } + + public synchronized void updateOtaSlot(@NonNull String value) { + Utils.check(value == null || value.equals("_a") || value.equals("_b")); + // It's not possible that this method is called with two different slots. + Utils.check(mOtaSlot == null || value == null || Objects.equals(mOtaSlot, value)); + // An OTA update has a higher priority than a Mainline update. When there are both a pending + // OTA update and a pending Mainline update, the system discards the Mainline update on the + // reboot. + if (mOtaSlot == null && value != null) { + mOtaSlot = value; + } + } + + public synchronized boolean hasStarted() { + return mHasStarted; } /** @@ -89,5 +238,15 @@ public class PreRebootDexoptJob implements ArtServiceJobInterface { Injector(@NonNull Context context) { mContext = context; } + + @NonNull + public JobScheduler getJobScheduler() { + return Objects.requireNonNull(mContext.getSystemService(JobScheduler.class)); + } + + @NonNull + public PreRebootDriver getPreRebootDriver() { + return new PreRebootDriver(mContext); + } } } diff --git a/libartservice/service/java/com/android/server/art/PrimaryDexopter.java b/libartservice/service/java/com/android/server/art/PrimaryDexopter.java index 6ac7988f9e..e4475d8503 100644 --- a/libartservice/service/java/com/android/server/art/PrimaryDexopter.java +++ b/libartservice/service/java/com/android/server/art/PrimaryDexopter.java @@ -31,7 +31,6 @@ import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.UserHandle; import android.text.TextUtils; -import android.util.Log; import androidx.annotation.RequiresApi; @@ -56,8 +55,6 @@ import java.util.Objects; /** @hide */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public class PrimaryDexopter extends Dexopter<DetailedPrimaryDexInfo> { - private static final String TAG = ArtManagerLocal.TAG; - private final int mSharedGid; public PrimaryDexopter(@NonNull Context context, @NonNull Config config, @@ -72,7 +69,11 @@ public class PrimaryDexopter extends Dexopter<DetailedPrimaryDexInfo> { @NonNull CancellationSignal cancellationSignal) { super(injector, pkgState, pkg, params, cancellationSignal); - mSharedGid = UserHandle.getSharedAppGid(pkgState.getAppId()); + if (pkgState.getAppId() < 0) { + mSharedGid = Process.SYSTEM_UID; + } else { + mSharedGid = UserHandle.getSharedAppGid(pkgState.getAppId()); + } if (mSharedGid < 0) { throw new IllegalStateException( String.format("Unable to get shared gid for package '%s' (app ID: %d)", diff --git a/libartservice/service/java/com/android/server/art/SecondaryDexopter.java b/libartservice/service/java/com/android/server/art/SecondaryDexopter.java index fe0cdb0091..07c6c0d654 100644 --- a/libartservice/service/java/com/android/server/art/SecondaryDexopter.java +++ b/libartservice/service/java/com/android/server/art/SecondaryDexopter.java @@ -40,8 +40,6 @@ import java.util.List; /** @hide */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public class SecondaryDexopter extends Dexopter<CheckedSecondaryDexInfo> { - private static final String TAG = ArtManagerLocal.TAG; - public SecondaryDexopter(@NonNull Context context, @NonNull Config config, @NonNull PackageState pkgState, @NonNull AndroidPackage pkg, @NonNull DexoptParams params, @NonNull CancellationSignal cancellationSignal) { @@ -53,6 +51,10 @@ public class SecondaryDexopter extends Dexopter<CheckedSecondaryDexInfo> { @NonNull AndroidPackage pkg, @NonNull DexoptParams params, @NonNull CancellationSignal cancellationSignal) { super(injector, pkgState, pkg, params, cancellationSignal); + if (pkgState.getAppId() < 0) { + throw new IllegalStateException( + "Package '" + pkgState.getPackageName() + "' has invalid app ID"); + } } @Override diff --git a/libartservice/service/java/com/android/server/art/Utils.java b/libartservice/service/java/com/android/server/art/Utils.java index 6c01bbcb40..8132dbbab2 100644 --- a/libartservice/service/java/com/android/server/art/Utils.java +++ b/libartservice/service/java/com/android/server/art/Utils.java @@ -37,7 +37,6 @@ import android.os.UserManager; import android.text.TextUtils; import android.util.Log; import android.util.Pair; -import android.util.Slog; import android.util.SparseArray; import androidx.annotation.RequiresApi; @@ -73,7 +72,6 @@ import java.util.stream.Collectors; /** @hide */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public final class Utils { - public static final String TAG = ArtManagerLocal.TAG; public static final String PLATFORM_PACKAGE_NAME = "android"; /** A copy of {@link android.os.Trace.TRACE_TAG_DALVIK}. */ @@ -217,7 +215,7 @@ public final class Utils { // This should never happen. Ignore the error and conservatively use dalvik-cache to // minimize the risk. // TODO(jiakaiz): Throw the error instead of ignoring it. - Log.e(TAG, "Failed to determine the location of the artifacts", e); + AsLog.e("Failed to determine the location of the artifacts", e); return true; } } @@ -343,7 +341,7 @@ public final class Utils { try { Files.deleteIfExists(path); } catch (IOException e) { - Log.e(TAG, "Failed to delete file '" + path + "'", e); + AsLog.e("Failed to delete file '" + path + "'", e); } } @@ -390,8 +388,7 @@ public final class Utils { refProfile, isOtherReadable, List.of() /* externalProfileErrors */); } } catch (ServiceSpecificException e) { - Log.e(TAG, - "Failed to use the existing reference profile " + AsLog.e("Failed to use the existing reference profile " + AidlUtils.toString(refProfile), e); } @@ -440,7 +437,7 @@ public final class Utils { externalProfileErrors.add(result.errorMsg); } } catch (ServiceSpecificException e) { - Log.e(TAG, "Failed to initialize profile from " + pair.first, e); + AsLog.e("Failed to initialize profile from " + pair.first, e); } } @@ -457,10 +454,10 @@ public final class Utils { // exception is expected. // In either case, we don't need to surface the exception from here. // The Java stack trace is intentionally omitted because it's not helpful. - Log.e(TAG, message); + AsLog.e(message); } else { // Not expected. Log wtf to surface it. - Slog.wtf(TAG, message, e); + AsLog.wtf(message, e); } } @@ -473,7 +470,7 @@ public final class Utils { try { Thread.sleep(millis); } catch (InterruptedException e) { - Slog.wtf(TAG, "Sleep interrupted", e); + AsLog.wtf("Sleep interrupted", 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 d0b8b11876..0a1d52bcbc 100644 --- a/libartservice/service/java/com/android/server/art/model/ArtFlags.java +++ b/libartservice/service/java/com/android/server/art/model/ArtFlags.java @@ -95,6 +95,15 @@ public class ArtFlags { * @hide */ public static final int FLAG_FORCE_MERGE_PROFILE = 1 << 8; + /** + * Whether to force the specified compiler filter. If true, the compiler filter cannot be + * overridden through {@link ArtManagerLocal#setAdjustCompilerFilterCallback}. ART Service will + * not adjust the compiler filter either, unless dexopt cannot be performed with the specified + * compiler filter (e.g., the filter is "speed-profile" while no profile is available). + * + * @hide + */ + public static final int FLAG_FORCE_COMPILER_FILTER = 1 << 9; /** * Flags for {@link @@ -137,6 +146,7 @@ public class ArtFlags { FLAG_SKIP_IF_STORAGE_LOW, FLAG_IGNORE_PROFILE, FLAG_FORCE_MERGE_PROFILE, + FLAG_FORCE_COMPILER_FILTER, }) // clang-format on @Retention(RetentionPolicy.SOURCE) diff --git a/libartservice/service/java/com/android/server/art/prereboot/PreRebootDriver.java b/libartservice/service/java/com/android/server/art/prereboot/PreRebootDriver.java index 2e43383161..44809b1c50 100644 --- a/libartservice/service/java/com/android/server/art/prereboot/PreRebootDriver.java +++ b/libartservice/service/java/com/android/server/art/prereboot/PreRebootDriver.java @@ -26,14 +26,13 @@ import android.os.Build; import android.os.CancellationSignal; import android.os.RemoteException; import android.os.ServiceSpecificException; -import android.util.Log; -import android.util.Slog; import androidx.annotation.RequiresApi; import com.android.internal.annotations.VisibleForTesting; import com.android.server.art.ArtManagerLocal; import com.android.server.art.ArtModuleServiceInitializer; +import com.android.server.art.AsLog; import com.android.server.art.GlobalInjector; import com.android.server.art.IDexoptChrootSetup; import com.android.server.art.Utils; @@ -49,8 +48,6 @@ import dalvik.system.DelegateLastClassLoader; */ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) public class PreRebootDriver { - private static final String TAG = ArtManagerLocal.TAG; - @NonNull private final Injector mInjector; public PreRebootDriver(@NonNull Context context) { @@ -76,9 +73,9 @@ public class PreRebootDriver { } catch (RemoteException e) { Utils.logArtdException(e); } catch (ServiceSpecificException e) { - Log.e(TAG, "Failed to set up chroot", e); + AsLog.e("Failed to set up chroot", e); } catch (ReflectiveOperationException e) { - Log.e(TAG, "Failed to run pre-reboot dexopt", e); + AsLog.e("Failed to run pre-reboot dexopt", e); } finally { tearDown(); } @@ -107,14 +104,14 @@ public class PreRebootDriver { } catch (RemoteException e) { Utils.logArtdException(e); } catch (ServiceSpecificException e) { - Log.e(TAG, "Failed to tear down chroot", e); + AsLog.e("Failed to tear down chroot", e); } catch (IllegalStateException e) { // Not expected, but we still want retries in such an extreme case. - Slog.wtf(TAG, "Unexpected exception", e); + AsLog.wtf("Unexpected exception", e); } if (--numRetries > 0) { - Log.i(TAG, "Retrying...."); + AsLog.i("Retrying...."); Utils.sleep(30000); } } diff --git a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java index 098c28b108..1fd7e4bcfc 100644 --- a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java +++ b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java @@ -137,6 +137,7 @@ public class ArtManagerLocalTest { @Mock private ArtdRefCache.Pin mArtdPin; @Mock private DexMetadataHelper.Injector mDexMetadataHelperInjector; @Mock private Context mContext; + @Mock private PreRebootDexoptJob mPreRebootDexoptJob; private PackageState mPkgState1; private AndroidPackage mPkg1; private CheckedSecondaryDexInfo mPkg1SecondaryDexInfo1; @@ -180,6 +181,7 @@ public class ArtManagerLocalTest { .thenReturn(new ArtFileManager(mArtFileManagerInjector)); lenient().when(mInjector.getDexMetadataHelper()).thenReturn(mDexMetadataHelper); lenient().when(mInjector.getContext()).thenReturn(mContext); + lenient().when(mInjector.getPreRebootDexoptJob()).thenReturn(mPreRebootDexoptJob); lenient().when(mArtFileManagerInjector.getArtd()).thenReturn(mArtd); lenient().when(mArtFileManagerInjector.getUserManager()).thenReturn(mUserManager); @@ -1132,7 +1134,18 @@ public class ArtManagerLocalTest { } @Test - public void testCleanup() throws Exception { + public void testCleanupKeepPreRebootStagedFiles() throws Exception { + when(mPreRebootDexoptJob.hasStarted()).thenReturn(true); + testCleanup(true /* keepPreRebootStagedFiles */); + } + + @Test + public void testCleanupRemovePreRebootStagedFiles() throws Exception { + when(mPreRebootDexoptJob.hasStarted()).thenReturn(false); + testCleanup(false /* keepPreRebootStagedFiles */); + } + + private void testCleanup(boolean keepPreRebootStagedFiles) throws Exception { // It should keep all artifacts, but not runtime images. doReturn(createGetDexoptStatusResult( "speed-profile", "bg-dexopt", "location", ArtifactsLocation.NEXT_TO_DEX)) @@ -1190,7 +1203,8 @@ public class ArtManagerLocalTest { inAnyOrderDeepEquals(AidlUtils.buildRuntimeArtifactsPath( PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm64"), AidlUtils.buildRuntimeArtifactsPath( - PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm"))); + PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm")), + eq(keepPreRebootStagedFiles)); } @Test diff --git a/libartservice/service/javatests/com/android/server/art/DumpHelperTest.java b/libartservice/service/javatests/com/android/server/art/DumpHelperTest.java index 520cda0835..45a7010d09 100644 --- a/libartservice/service/javatests/com/android/server/art/DumpHelperTest.java +++ b/libartservice/service/javatests/com/android/server/art/DumpHelperTest.java @@ -60,6 +60,7 @@ import java.util.Set; public class DumpHelperTest { private static final String PKG_NAME_FOO = "com.example1.foo"; private static final String PKG_NAME_BAR = "com.example2.bar"; + private static final String PKG_NAME_SDK = "com.example3.sdk"; @Rule public StaticMockitoRule mockitoRule = @@ -96,6 +97,7 @@ public class DumpHelperTest { setUpForFoo(); setUpForBar(); + setUpForSdk(); mDumpHelper = new DumpHelper(mInjector); } @@ -134,6 +136,12 @@ public class DumpHelperTest { + " [location is /somewhere/app/bar/oat/arm/base.odex]\n" + " arm64: [status=verify] [reason=install]\n" + " [location is /somewhere/app/bar/oat/arm64/base.odex]\n" + + "[com.example3.sdk]\n" + + " path: /somewhere/app/sdk/base.apk\n" + + " arm: [status=verify] [reason=install] [primary-abi]\n" + + " [location is /somewhere/app/sdk/oat/arm/base.odex]\n" + + " arm64: [status=verify] [reason=install]\n" + + " [location is /somewhere/app/sdk/oat/arm64/base.odex]\n" + "\n" + "Current GC: CollectorTypeCMC\n"; @@ -142,11 +150,12 @@ public class DumpHelperTest { assertThat(stringWriter.toString()).isEqualTo(expected); } - private PackageState createPackageState(@NonNull String packageName, int appId, + private PackageState createPackageState(@NonNull String packageName, int appId, boolean isApex, boolean hasPackage, @NonNull String primaryAbi, @NonNull String secondaryAbi) { var pkgState = mock(PackageState.class); lenient().when(pkgState.getPackageName()).thenReturn(packageName); lenient().when(pkgState.getAppId()).thenReturn(appId); + lenient().when(pkgState.isApex()).thenReturn(isApex); lenient() .when(pkgState.getAndroidPackage()) .thenReturn(hasPackage ? mock(AndroidPackage.class) : null); @@ -158,19 +167,22 @@ public class DumpHelperTest { private Map<String, PackageState> createPackageStates() { var pkgStates = new HashMap<String, PackageState>(); pkgStates.put(PKG_NAME_FOO, - createPackageState(PKG_NAME_FOO, 10001 /* appId */, true /* hasPackage */, - "arm64-v8a", "armeabi-v7a")); + createPackageState(PKG_NAME_FOO, 10001 /* appId */, false /* isApex */, + true /* hasPackage */, "arm64-v8a", "armeabi-v7a")); pkgStates.put(PKG_NAME_BAR, - createPackageState(PKG_NAME_BAR, 10003 /* appId */, true /* hasPackage */, - "armeabi-v7a", "arm64-v8a")); - // This should not be included in the output because it has a negative app id. + createPackageState(PKG_NAME_BAR, 10003 /* appId */, false /* isApex */, + true /* hasPackage */, "armeabi-v7a", "arm64-v8a")); + pkgStates.put(PKG_NAME_SDK, + createPackageState(PKG_NAME_SDK, -1 /* appId */, false /* isApex */, + true /* hasPackage */, "armeabi-v7a", "arm64-v8a")); + // This should not be included in the output because it is APEX. pkgStates.put("com.android.art", - createPackageState("com.android.art", -1 /* appId */, true /* hasPackage */, - "arm64-v8a", "armeabi-v7a")); + createPackageState("com.android.art", -1 /* appId */, true /* isApex */, + true /* hasPackage */, "arm64-v8a", "armeabi-v7a")); // This should not be included in the output because it does't have AndroidPackage. pkgStates.put("com.example.null", - createPackageState("com.example.null", 10010 /* appId */, false /* hasPackage */, - "arm64-v8a", "armeabi-v7a")); + createPackageState("com.example.null", 10010 /* appId */, false /* isApex */, + false /* hasPackage */, "arm64-v8a", "armeabi-v7a")); return pkgStates; } @@ -286,4 +298,23 @@ public class DumpHelperTest { PKG_NAME_BAR, "/somewhere/app/bar/base.apk")) .thenReturn(Set.of()); } + + private void setUpForSdk() { + var status = DexoptStatus.create( + List.of(DexContainerFileDexoptStatus.create("/somewhere/app/sdk/base.apk", + true /* isPrimaryDex */, true /* isPrimaryAbi */, "armeabi-v7a", + "verify", "install", "/somewhere/app/sdk/oat/arm/base.odex"), + DexContainerFileDexoptStatus.create("/somewhere/app/sdk/base.apk", + true /* isPrimaryDex */, false /* isPrimaryAbi */, "arm64-v8a", + "verify", "install", "/somewhere/app/sdk/oat/arm64/base.odex"))); + + lenient() + .when(mArtManagerLocal.getDexoptStatus(any(), eq(PKG_NAME_SDK))) + .thenReturn(status); + + lenient() + .when(mDexUseManagerLocal.getPrimaryDexLoaders( + PKG_NAME_SDK, "/somewhere/app/sdk/base.apk")) + .thenReturn(Set.of()); + } } diff --git a/libartservice/service/javatests/com/android/server/art/PreRebootDexoptJobTest.java b/libartservice/service/javatests/com/android/server/art/PreRebootDexoptJobTest.java new file mode 100644 index 0000000000..c555879792 --- /dev/null +++ b/libartservice/service/javatests/com/android/server/art/PreRebootDexoptJobTest.java @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.art; + +import static com.android.server.art.PreRebootDexoptJob.JOB_ID; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyBoolean; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.isNull; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.job.JobInfo; +import android.app.job.JobScheduler; +import android.os.CancellationSignal; +import android.os.SystemProperties; +import android.provider.DeviceConfig; + +import androidx.test.filters.SmallTest; + +import com.android.server.art.model.ArtFlags; +import com.android.server.art.prereboot.PreRebootDriver; +import com.android.server.art.testing.StaticMockitoRule; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +@SmallTest +@RunWith(MockitoJUnitRunner.StrictStubs.class) +public class PreRebootDexoptJobTest { + private static final long TIMEOUT_SEC = 10; + + @Rule + public StaticMockitoRule mockitoRule = new StaticMockitoRule( + SystemProperties.class, BackgroundDexoptJobService.class, DeviceConfig.class); + + @Mock private PreRebootDexoptJob.Injector mInjector; + @Mock private JobScheduler mJobScheduler; + @Mock private PreRebootDriver mPreRebootDriver; + private PreRebootDexoptJob mPreRebootDexoptJob; + + @Before + public void setUp() throws Exception { + // By default, the job is enabled by a build-time flag. + lenient() + .when(SystemProperties.getBoolean(eq("pm.dexopt.disable_bg_dexopt"), anyBoolean())) + .thenReturn(false); + lenient() + .when(SystemProperties.getBoolean(eq("dalvik.vm.enable_pr_dexopt"), anyBoolean())) + .thenReturn(true); + lenient() + .when(DeviceConfig.getBoolean( + eq(DeviceConfig.NAMESPACE_RUNTIME), eq("enable_pr_dexopt"), anyBoolean())) + .thenReturn(false); + + lenient().when(mInjector.getJobScheduler()).thenReturn(mJobScheduler); + lenient().when(mInjector.getPreRebootDriver()).thenReturn(mPreRebootDriver); + + mPreRebootDexoptJob = new PreRebootDexoptJob(mInjector); + lenient().when(BackgroundDexoptJobService.getJob(JOB_ID)).thenReturn(mPreRebootDexoptJob); + } + + @Test + public void testSchedule() throws Exception { + var captor = ArgumentCaptor.forClass(JobInfo.class); + when(mJobScheduler.schedule(captor.capture())).thenReturn(JobScheduler.RESULT_SUCCESS); + + assertThat(mPreRebootDexoptJob.schedule()).isEqualTo(ArtFlags.SCHEDULE_SUCCESS); + + JobInfo jobInfo = captor.getValue(); + assertThat(jobInfo.isPeriodic()).isFalse(); + assertThat(jobInfo.isRequireDeviceIdle()).isTrue(); + assertThat(jobInfo.isRequireCharging()).isTrue(); + assertThat(jobInfo.isRequireBatteryNotLow()).isTrue(); + } + + @Test + public void testScheduleDisabled() { + when(SystemProperties.getBoolean(eq("pm.dexopt.disable_bg_dexopt"), anyBoolean())) + .thenReturn(true); + + assertThat(mPreRebootDexoptJob.schedule()).isEqualTo(ArtFlags.SCHEDULE_DISABLED_BY_SYSPROP); + + verify(mJobScheduler, never()).schedule(any()); + } + + @Test + public void testScheduleNotEnabled() { + when(SystemProperties.getBoolean(eq("dalvik.vm.enable_pr_dexopt"), anyBoolean())) + .thenReturn(false); + + assertThat(mPreRebootDexoptJob.schedule()).isEqualTo(ArtFlags.SCHEDULE_DISABLED_BY_SYSPROP); + + verify(mJobScheduler, never()).schedule(any()); + } + + @Test + public void testScheduleEnabledByPhenotypeFlag() { + lenient() + .when(SystemProperties.getBoolean(eq("dalvik.vm.enable_pr_dexopt"), anyBoolean())) + .thenReturn(false); + lenient() + .when(DeviceConfig.getBoolean( + eq(DeviceConfig.NAMESPACE_RUNTIME), eq("enable_pr_dexopt"), anyBoolean())) + .thenReturn(true); + when(mJobScheduler.schedule(any())).thenReturn(JobScheduler.RESULT_SUCCESS); + + assertThat(mPreRebootDexoptJob.schedule()).isEqualTo(ArtFlags.SCHEDULE_SUCCESS); + + verify(mJobScheduler).schedule(any()); + } + + @Test + public void testUnschedule() { + mPreRebootDexoptJob.unschedule(); + verify(mJobScheduler).cancel(JOB_ID); + } + + @Test + public void testStart() { + when(mPreRebootDriver.run(any(), any())).thenReturn(true); + + assertThat(mPreRebootDexoptJob.hasStarted()).isFalse(); + Future<Void> future = mPreRebootDexoptJob.start(); + assertThat(mPreRebootDexoptJob.hasStarted()).isTrue(); + + Utils.getFuture(future); + } + + @Test + public void testStartAlreadyRunning() { + Semaphore dexoptDone = new Semaphore(0); + when(mPreRebootDriver.run(any(), any())).thenAnswer(invocation -> { + assertThat(dexoptDone.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue(); + return true; + }); + + Future<Void> future1 = mPreRebootDexoptJob.start(); + Future<Void> future2 = mPreRebootDexoptJob.start(); + assertThat(future1).isSameInstanceAs(future2); + + dexoptDone.release(); + Utils.getFuture(future1); + + verify(mPreRebootDriver, times(1)).run(any(), any()); + } + + @Test + public void testStartAnother() { + when(mPreRebootDriver.run(any(), any())).thenReturn(true); + + Future<Void> future1 = mPreRebootDexoptJob.start(); + Utils.getFuture(future1); + Future<Void> future2 = mPreRebootDexoptJob.start(); + Utils.getFuture(future2); + assertThat(future1).isNotSameInstanceAs(future2); + } + + @Test + public void testCancel() { + Semaphore dexoptCancelled = new Semaphore(0); + Semaphore jobExited = new Semaphore(0); + when(mPreRebootDriver.run(any(), any())).thenAnswer(invocation -> { + assertThat(dexoptCancelled.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue(); + var cancellationSignal = invocation.<CancellationSignal>getArgument(1); + assertThat(cancellationSignal.isCanceled()).isTrue(); + jobExited.release(); + return true; + }); + + var unused = mPreRebootDexoptJob.start(); + Future<Void> future = new CompletableFuture().runAsync(() -> { + mPreRebootDexoptJob.cancel(false /* blocking */); + dexoptCancelled.release(); + mPreRebootDexoptJob.cancel(true /* blocking */); + }); + Utils.getFuture(future); + // Check that `cancel(true)` is really blocking. If it wasn't, the check below might still + // pass due to a race, but we would have a flaky test. + assertThat(jobExited.tryAcquire()).isTrue(); + } + + @Test + public void testUpdateOtaSlotOtaThenMainline() { + mPreRebootDexoptJob.updateOtaSlot("_b"); + mPreRebootDexoptJob.updateOtaSlot(null); + + when(mPreRebootDriver.run(eq("_b"), any())).thenReturn(true); + + Utils.getFuture(mPreRebootDexoptJob.start()); + } + + @Test + public void testUpdateOtaSlotMainlineThenOta() { + mPreRebootDexoptJob.updateOtaSlot(null); + mPreRebootDexoptJob.updateOtaSlot("_a"); + + when(mPreRebootDriver.run(eq("_a"), any())).thenReturn(true); + + Utils.getFuture(mPreRebootDexoptJob.start()); + } + + @Test + public void testUpdateOtaSlotMainlineThenMainline() { + mPreRebootDexoptJob.updateOtaSlot(null); + mPreRebootDexoptJob.updateOtaSlot(null); + + when(mPreRebootDriver.run(isNull(), any())).thenReturn(true); + + Utils.getFuture(mPreRebootDexoptJob.start()); + } + + @Test + public void testUpdateOtaSlotOtaThenOta() { + mPreRebootDexoptJob.updateOtaSlot("_b"); + mPreRebootDexoptJob.updateOtaSlot("_b"); + + when(mPreRebootDriver.run(eq("_b"), any())).thenReturn(true); + + Utils.getFuture(mPreRebootDexoptJob.start()); + } + + @Test(expected = IllegalStateException.class) + public void testUpdateOtaSlotOtaThenOtaDifferentSlots() { + mPreRebootDexoptJob.updateOtaSlot("_b"); + mPreRebootDexoptJob.updateOtaSlot("_a"); + } + + @Test(expected = IllegalStateException.class) + public void testUpdateOtaSlotOtaBogusSlot() { + mPreRebootDexoptJob.updateOtaSlot("_bogus"); + } +} diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java index 30595aafd2..864390c217 100644 --- a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java +++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java @@ -83,6 +83,11 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { params = new Params(); list.add(params); + // Sandbox SdkLib. + params = new Params(); + params.mIsSanboxSdkLib = true; + list.add(params); + params = new Params(); params.mRequestedCompilerFilter = "speed"; params.mExpectedCompilerFilter = "speed"; @@ -201,6 +206,21 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { params.mExpectedDeletesRuntimeArtifacts = false; list.add(params); + params = new Params(); + params.mIsSystemUi = true; + params.mForceCompilerFilter = true; + params.mRequestedCompilerFilter = "verify"; + params.mExpectedCompilerFilter = "verify"; + list.add(params); + + params = new Params(); + params.mForceCompilerFilter = true; + params.mRequestedCompilerFilter = "verify"; + params.mExpectedCallbackInputCompilerFilter = "verify"; + params.mCallbackReturnedCompilerFilter = "speed"; + params.mExpectedCompilerFilter = "verify"; + list.add(params); + return list; } @@ -243,6 +263,10 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { }); } + if (mParams.mIsSanboxSdkLib) { + lenient().when(mPkgState.getAppId()).thenReturn(-1); + } + mDexoptParams = new DexoptParams.Builder("install") .setCompilerFilter(mParams.mRequestedCompilerFilter) @@ -254,6 +278,9 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { ArtFlags.FLAG_SKIP_IF_STORAGE_LOW) .setFlags(mParams.mIgnoreProfile ? ArtFlags.FLAG_IGNORE_PROFILE : 0, ArtFlags.FLAG_IGNORE_PROFILE) + .setFlags(mParams.mForceCompilerFilter ? ArtFlags.FLAG_FORCE_COMPILER_FILTER + : 0, + ArtFlags.FLAG_FORCE_COMPILER_FILTER) .build(); mPrimaryDexopter = @@ -262,10 +289,11 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { @Test public void testDexopt() throws Exception { + int sharedGid = mParams.mIsSanboxSdkLib ? Process.SYSTEM_UID : SHARED_GID; PermissionSettings permissionSettings = buildPermissionSettings( buildFsPermission(Process.SYSTEM_UID /* uid */, Process.SYSTEM_UID /* gid */, false /* isOtherReadable */, true /* isOtherExecutable */), - buildFsPermission(Process.SYSTEM_UID /* uid */, SHARED_GID /* gid */, + buildFsPermission(Process.SYSTEM_UID /* uid */, sharedGid /* gid */, true /* isOtherReadable */), null /* seContext */); @@ -389,6 +417,7 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { public boolean mIsSystemUi = false; public boolean mIsLauncher = false; public boolean mIsUseEmbeddedDex = false; + public boolean mIsSanboxSdkLib = false; // Options. public String mRequestedCompilerFilter = "verify"; @@ -398,6 +427,7 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { public boolean mSkipIfStorageLow = false; public boolean mIgnoreProfile = false; public boolean mIsPreReboot = false; + public boolean mForceCompilerFilter = false; // System properties. public boolean mAlwaysDebuggable = false; @@ -420,6 +450,7 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { + "isSystemUi=%b," + "isLauncher=%b," + "isUseEmbeddedDex=%b," + + "isSanboxSdkLib=%b," + "requestedCompilerFilter=%s," + "callbackReturnedCompilerFilter=%s," + "force=%b," @@ -427,6 +458,7 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { + "skipIfStorageLow=%b," + "ignoreProfile=%b," + "isPreReboot=%b," + + "forceCompilerFilter=%b," + "alwaysDebuggable=%b" + " => " + "expectedCallbackInputCompilerFilter=%s," @@ -437,11 +469,11 @@ public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { + "expectedOutputIsPreReboot=%b," + "expectedDeleteRuntimeArtifacts=%b", mIsInDalvikCache, mHiddenApiEnforcementPolicy, mIsVmSafeMode, mIsDebuggable, - mIsSystemUi, mIsLauncher, mIsUseEmbeddedDex, mRequestedCompilerFilter, - mCallbackReturnedCompilerFilter, mForce, mShouldDowngrade, mSkipIfStorageLow, - mIgnoreProfile, mIsPreReboot, mAlwaysDebuggable, - mExpectedCallbackInputCompilerFilter, mExpectedCompilerFilter, - mExpectedDexoptTrigger, mExpectedIsDebuggable, + mIsSystemUi, mIsLauncher, mIsUseEmbeddedDex, mIsSanboxSdkLib, + mRequestedCompilerFilter, mCallbackReturnedCompilerFilter, mForce, + mShouldDowngrade, mSkipIfStorageLow, mIgnoreProfile, mIsPreReboot, + mForceCompilerFilter, mAlwaysDebuggable, mExpectedCallbackInputCompilerFilter, + mExpectedCompilerFilter, mExpectedDexoptTrigger, mExpectedIsDebuggable, mExpectedIsHiddenApiPolicyEnabled, mExpectedOutputIsPreReboot, mExpectedDeletesRuntimeArtifacts); } diff --git a/libarttools/art_exec.cc b/libarttools/art_exec.cc index 704b25dddb..dfd0ca5bf9 100644 --- a/libarttools/art_exec.cc +++ b/libarttools/art_exec.cc @@ -67,6 +67,8 @@ Supported options: --keep-fds=FILE_DESCRIPTORS: A semicolon-separated list of file descriptors to keep open. --env=KEY=VALUE: Set an environment variable. This flag can be passed multiple times to set multiple environment variables. + --process-name-suffix=SUFFIX: Add a suffix in parentheses to argv[0] when calling `execv`. This + suffix will show up as part of the process name in tombstone when the process crashes. )"; constexpr int kErrorUsage = 100; diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp index 5aeabb6c26..37f17308cd 100644 --- a/libdexfile/Android.bp +++ b/libdexfile/Android.bp @@ -377,9 +377,6 @@ cc_library_headers { art_cc_defaults { name: "art_libdexfile_external_tests_defaults", - defaults: [ - "art_module_source_build_defaults", - ], test_suites: ["general-tests"], srcs: [ "external/dex_file_ext_c_test.c", @@ -459,9 +456,6 @@ art_cc_library_static { art_cc_defaults { name: "art_libdexfile_support_tests_defaults", - defaults: [ - "art_module_source_build_defaults", - ], test_suites: ["general-tests"], srcs: [ "external/dex_file_supp_test.cc", @@ -538,7 +532,6 @@ art_cc_test { name: "art_libdexfile_support_static_tests", test_suites: ["general-tests"], defaults: [ - "art_module_source_build_defaults", "art_test_defaults", "libdexfile_support_static_defaults", ], diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h index 2cd3781023..ad6c8c31ba 100644 --- a/libdexfile/dex/dex_file-inl.h +++ b/libdexfile/dex/dex_file-inl.h @@ -32,7 +32,22 @@ namespace art { -inline std::string_view StringViewFromUtf16Length(const char* utf8_data, size_t utf16_length) { +inline int DexFile::CompareDescriptors(std::string_view lhs, std::string_view rhs) { + // Note: `std::string_view::compare()` uses lexicographical comparison and treats the `char` + // as unsigned; for Modified-UTF-8 without embedded nulls this is consistent with the + // `CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues()` ordering. + return lhs.compare(rhs); +} + +inline int DexFile::CompareMemberNames(std::string_view lhs, std::string_view rhs) { + // Note: `std::string_view::compare()` uses lexicographical comparison and treats the `char` + // as unsigned; for Modified-UTF-8 without embedded nulls this is consistent with the + // `CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues()` ordering. + return lhs.compare(rhs); +} + +inline std::string_view DexFile::StringViewFromUtf16Length(const char* utf8_data, + size_t utf16_length) { size_t utf8_length = LIKELY(utf8_data[utf16_length] == 0) // Is ASCII? ? utf16_length : utf16_length + strlen(utf8_data + utf16_length); @@ -100,28 +115,71 @@ inline std::string_view DexFile::GetTypeDescriptorView(dex::TypeIndex type_idx) return GetTypeDescriptorView(GetTypeId(type_idx)); } +inline const char* DexFile::GetFieldDeclaringClassDescriptor(const dex::FieldId& field_id) const { + return GetTypeDescriptor(field_id.class_idx_); +} + +inline const char* DexFile::GetFieldDeclaringClassDescriptor(uint32_t field_idx) const { + return GetFieldDeclaringClassDescriptor(GetFieldId(field_idx)); +} + +inline std::string_view DexFile::GetFieldDeclaringClassDescriptorView( + const dex::FieldId& field_id) const { + return GetTypeDescriptorView(field_id.class_idx_); +} + +inline std::string_view DexFile::GetFieldDeclaringClassDescriptorView(uint32_t field_idx) const { + return GetFieldDeclaringClassDescriptorView(GetFieldId(field_idx)); +} + inline const char* DexFile::GetFieldTypeDescriptor(const dex::FieldId& field_id) const { - const dex::TypeId& type_id = GetTypeId(field_id.type_idx_); - return GetTypeDescriptor(type_id); + return GetTypeDescriptor(field_id.type_idx_); +} + +inline const char* DexFile::GetFieldTypeDescriptor(uint32_t field_idx) const { + return GetFieldTypeDescriptor(GetFieldId(field_idx)); } inline std::string_view DexFile::GetFieldTypeDescriptorView(const dex::FieldId& field_id) const { - const dex::TypeId& type_id = GetTypeId(field_id.type_idx_); - return GetTypeDescriptorView(type_id); + return GetTypeDescriptorView(field_id.type_idx_); +} + +inline std::string_view DexFile::GetFieldTypeDescriptorView(uint32_t field_idx) const { + return GetFieldTypeDescriptorView(GetFieldId(field_idx)); } inline const char* DexFile::GetFieldName(const dex::FieldId& field_id) const { return GetStringData(field_id.name_idx_); } +inline const char* DexFile::GetFieldName(uint32_t field_idx) const { + return GetFieldName(GetFieldId(field_idx)); +} + inline std::string_view DexFile::GetFieldNameView(const dex::FieldId& field_id) const { return GetStringView(field_id.name_idx_); } -inline const char* DexFile::GetMethodDeclaringClassDescriptor(const dex::MethodId& method_id) - const { - const dex::TypeId& type_id = GetTypeId(method_id.class_idx_); - return GetTypeDescriptor(type_id); +inline std::string_view DexFile::GetFieldNameView(uint32_t field_idx) const { + return GetFieldNameView(GetFieldId(field_idx)); +} + +inline const char* DexFile::GetMethodDeclaringClassDescriptor( + const dex::MethodId& method_id) const { + return GetTypeDescriptor(method_id.class_idx_); +} + +inline const char* DexFile::GetMethodDeclaringClassDescriptor(uint32_t method_idx) const { + return GetMethodDeclaringClassDescriptor(GetMethodId(method_idx)); +} + +inline std::string_view DexFile::GetMethodDeclaringClassDescriptorView( + const dex::MethodId& method_id) const { + return GetTypeDescriptorView(method_id.class_idx_); +} + +inline std::string_view DexFile::GetMethodDeclaringClassDescriptorView(uint32_t method_idx) const { + return GetMethodDeclaringClassDescriptorView(GetMethodId(method_idx)); } inline const Signature DexFile::GetMethodSignature(const dex::MethodId& method_id) const { @@ -141,8 +199,8 @@ inline const char* DexFile::GetMethodName(const dex::MethodId& method_id, uint32 return GetStringDataAndUtf16Length(method_id.name_idx_, utf_length); } -inline const char* DexFile::GetMethodName(uint32_t idx) const { - return GetStringData(GetMethodId(idx).name_idx_); +inline const char* DexFile::GetMethodName(uint32_t method_idx) const { + return GetStringData(GetMethodId(method_idx).name_idx_); } inline const char* DexFile::GetMethodName(uint32_t idx, uint32_t* utf_length) const { @@ -155,8 +213,8 @@ inline std::string_view DexFile::GetMethodNameView(const dex::MethodId& method_i } ALWAYS_INLINE -inline std::string_view DexFile::GetMethodNameView(uint32_t idx) const { - return GetMethodNameView(GetMethodId(idx)); +inline std::string_view DexFile::GetMethodNameView(uint32_t method_idx) const { + return GetMethodNameView(GetMethodId(method_idx)); } inline const char* DexFile::GetMethodShorty(uint32_t idx) const { diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index 9940cc0458..2b68cfad0d 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -463,15 +463,14 @@ const StringId* DexFile::FindStringId(const char* string) const { return nullptr; } -const TypeId* DexFile::FindTypeId(const char* string) const { +const TypeId* DexFile::FindTypeId(std::string_view descriptor) const { int32_t lo = 0; int32_t hi = NumTypeIds() - 1; while (hi >= lo) { int32_t mid = (hi + lo) / 2; const TypeId& type_id = GetTypeId(dex::TypeIndex(mid)); - const StringId& str_id = GetStringId(type_id.descriptor_idx_); - const char* str = GetStringData(str_id); - int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str); + std::string_view mid_descriptor = GetTypeDescriptorView(type_id); + int compare = CompareDescriptors(descriptor, mid_descriptor); if (compare > 0) { lo = mid + 1; } else if (compare < 0) { @@ -571,9 +570,8 @@ bool DexFile::CreateTypeList(std::string_view signature, offset++; } while (c != ';'); } - // TODO: avoid creating a std::string just to get a 0-terminated char array - std::string descriptor(signature.data() + start_offset, offset - start_offset); - const TypeId* type_id = FindTypeId(descriptor.c_str()); + std::string_view descriptor(signature.data() + start_offset, offset - start_offset); + const TypeId* type_id = FindTypeId(descriptor); if (type_id == nullptr) { return false; } diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index 5d67c6cca4..6c41849616 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -359,11 +359,6 @@ class DexFile { // Looks up a string id for a given modified utf8 string. const dex::StringId* FindStringId(const char* string) const; - const dex::TypeId* FindTypeId(const char* string) const; - const dex::TypeId* FindTypeId(std::string_view string) const { - return FindTypeId(std::string(string).c_str()); - } - // Returns the number of type identifiers in the .dex file. uint32_t NumTypeIds() const { DCHECK(header_ != nullptr) << GetLocation(); @@ -394,6 +389,8 @@ class DexFile { std::string_view GetTypeDescriptorView(const dex::TypeId& type_id) const; std::string_view GetTypeDescriptorView(dex::TypeIndex type_idx) const; + const dex::TypeId* FindTypeId(std::string_view descriptor) const; + // Looks up a type for the given string index const dex::TypeId* FindTypeId(dex::StringIndex string_idx) const; @@ -433,18 +430,22 @@ class DexFile { virtual uint32_t GetCodeItemSize(const dex::CodeItem& disk_code_item) const = 0; // Returns the declaring class descriptor string of a field id. - const char* GetFieldDeclaringClassDescriptor(const dex::FieldId& field_id) const { - const dex::TypeId& type_id = GetTypeId(field_id.class_idx_); - return GetTypeDescriptor(type_id); - } + const char* GetFieldDeclaringClassDescriptor(const dex::FieldId& field_id) const; + const char* GetFieldDeclaringClassDescriptor(uint32_t field_idx) const; + std::string_view GetFieldDeclaringClassDescriptorView(const dex::FieldId& field_id) const; + std::string_view GetFieldDeclaringClassDescriptorView(uint32_t field_idx) const; // Returns the class descriptor string of a field id. const char* GetFieldTypeDescriptor(const dex::FieldId& field_id) const; + const char* GetFieldTypeDescriptor(uint32_t field_idx) const; std::string_view GetFieldTypeDescriptorView(const dex::FieldId& field_id) const; + std::string_view GetFieldTypeDescriptorView(uint32_t field_idx) const; // Returns the name of a field id. const char* GetFieldName(const dex::FieldId& field_id) const; + const char* GetFieldName(uint32_t field_idx) const; std::string_view GetFieldNameView(const dex::FieldId& field_id) const; + std::string_view GetFieldNameView(uint32_t field_idx) const; // Returns the number of method identifiers in the .dex file. size_t NumMethodIds() const { @@ -475,6 +476,9 @@ class DexFile { // Returns the declaring class descriptor string of a method id. const char* GetMethodDeclaringClassDescriptor(const dex::MethodId& method_id) const; + const char* GetMethodDeclaringClassDescriptor(uint32_t method_idx) const; + std::string_view GetMethodDeclaringClassDescriptorView(const dex::MethodId& method_id) const; + std::string_view GetMethodDeclaringClassDescriptorView(uint32_t method_idx) const; // Returns the prototype of a method id. const dex::ProtoId& GetMethodPrototype(const dex::MethodId& method_id) const { @@ -490,10 +494,10 @@ class DexFile { // Returns the name of a method id. const char* GetMethodName(const dex::MethodId& method_id) const; const char* GetMethodName(const dex::MethodId& method_id, uint32_t* utf_length) const; - const char* GetMethodName(uint32_t idx) const; - const char* GetMethodName(uint32_t idx, uint32_t* utf_length) const; + const char* GetMethodName(uint32_t method_idx) const; + const char* GetMethodName(uint32_t method_idx, uint32_t* utf_length) const; std::string_view GetMethodNameView(const dex::MethodId& method_id) const; - std::string_view GetMethodNameView(uint32_t idx) const; + std::string_view GetMethodNameView(uint32_t method_idx) const; // Returns the shorty of a method by its index. const char* GetMethodShorty(uint32_t idx) const; @@ -904,6 +908,11 @@ class DexFile { static inline bool StringEquals(const DexFile* df1, dex::StringIndex sidx1, const DexFile* df2, dex::StringIndex sidx2); + static int CompareDescriptors(std::string_view lhs, std::string_view rhs); + static int CompareMemberNames(std::string_view lhs, std::string_view rhs); + + static std::string_view StringViewFromUtf16Length(const char* utf8_data, size_t utf16_length); + protected: // First Dex format version supporting default methods. static constexpr uint32_t kDefaultMethodsVersion = 37; diff --git a/libdexfile/dex/dex_file_loader.h b/libdexfile/dex/dex_file_loader.h index ec7f8ba977..9b5f7d56cd 100644 --- a/libdexfile/dex/dex_file_loader.h +++ b/libdexfile/dex/dex_file_loader.h @@ -94,7 +94,7 @@ class DexFileLoader { std::optional<uint32_t> checksum; for (; *i < dex_files.size(); ++(*i)) { const auto* dex_file = &*dex_files[*i]; - bool is_primary_dex = !IsMultiDexLocation(dex_file->GetLocation().c_str()); + bool is_primary_dex = !IsMultiDexLocation(dex_file->GetLocation()); if (!checksum.has_value()) { // First dex file. CHECK(is_primary_dex) << dex_file->GetLocation(); // Expect primary dex. } else if (is_primary_dex) { // Later dex file. diff --git a/libdexfile/dex/fuzzer_corpus_test.cc b/libdexfile/dex/fuzzer_corpus_test.cc index 6b1bb130c9..15aa6dd657 100644 --- a/libdexfile/dex/fuzzer_corpus_test.cc +++ b/libdexfile/dex/fuzzer_corpus_test.cc @@ -113,7 +113,7 @@ TEST_F(FuzzerCorpusTest, VerifyCorpusDexFiles) { const bool expected_success = valid_dex_files.find(name) != valid_dex_files.end(); VerifyDexFile( - reinterpret_cast<const uint8_t*>(data.data()), data.size(), name.c_str(), expected_success); + reinterpret_cast<const uint8_t*>(data.data()), data.size(), name, expected_success); } ASSERT_TRUE(error >= -1) << "failed iterating " << filename << " : " << ErrorCodeString(error); diff --git a/libdexfile/dex/method_reference.h b/libdexfile/dex/method_reference.h index 0472f2d492..209ab7a994 100644 --- a/libdexfile/dex/method_reference.h +++ b/libdexfile/dex/method_reference.h @@ -56,16 +56,15 @@ struct MethodReferenceValueComparator { // Compare the class descriptors first. const dex::MethodId& mid1 = mr1.GetMethodId(); const dex::MethodId& mid2 = mr2.GetMethodId(); - // Note: `std::string_view::compare()` uses lexicographical comparison and treats the `char` - // as unsigned; for Modified-UTF-8 without embedded nulls this is consistent with the - // `CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues()` ordering. - int descriptor_diff = mr1.dex_file->GetTypeDescriptorView(mid1.class_idx_).compare( - mr2.dex_file->GetTypeDescriptorView(mid2.class_idx_)); + int descriptor_diff = DexFile::CompareDescriptors( + mr1.dex_file->GetTypeDescriptorView(mid1.class_idx_), + mr2.dex_file->GetTypeDescriptorView(mid2.class_idx_)); if (descriptor_diff != 0) { return descriptor_diff < 0; } // Compare names second. - int name_diff = strcmp(mr1.dex_file->GetMethodName(mid1), mr2.dex_file->GetMethodName(mid2)); + int name_diff = DexFile::CompareMemberNames(mr1.dex_file->GetMethodNameView(mid1), + mr2.dex_file->GetMethodNameView(mid2)); if (name_diff != 0) { return name_diff < 0; } diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h index 1307e0eae5..e4fc74a3e7 100644 --- a/libdexfile/dex/modifiers.h +++ b/libdexfile/dex/modifiers.h @@ -57,7 +57,7 @@ static constexpr uint32_t kAccSkipHiddenapiChecks = 0x00100000; // class (run // declaring-class/super-class are to be considered obsolete, meaning they should not be used by. static constexpr uint32_t kAccObsoleteObject = 0x00200000; // class (runtime) // Set during boot image compilation to indicate that the class is -// not initialized at compile tile and not in the list of preloaded classes. +// not initialized at compile time and not in the list of preloaded classes. static constexpr uint32_t kAccInBootImageAndNotInPreloadedClasses = 0x00400000; // class (runtime) // This is set by the class linker during LinkInterfaceMethods. It is used by a method // to represent that it was copied from its declaring class into another class. diff --git a/libdexfile/dex/proto_reference.h b/libdexfile/dex/proto_reference.h index dc9a447e63..a12091cc46 100644 --- a/libdexfile/dex/proto_reference.h +++ b/libdexfile/dex/proto_reference.h @@ -62,7 +62,7 @@ struct ProtoReferenceValueComparator { // Compare return type first. const dex::ProtoId& prid1 = lhs.ProtoId(); const dex::ProtoId& prid2 = rhs.ProtoId(); - int return_type_diff = lhs.ReturnType().compare(rhs.ReturnType()); + int return_type_diff = DexFile::CompareDescriptors(lhs.ReturnType(), rhs.ReturnType()); if (return_type_diff != 0) { return return_type_diff < 0; } @@ -77,7 +77,7 @@ struct ProtoReferenceValueComparator { std::string_view r_param = rhs.dex_file->GetTypeDescriptorView( rhs.dex_file->GetTypeId(params2->GetTypeItem(i).type_idx_)); - int param_diff = l_param.compare(r_param); + int param_diff = DexFile::CompareDescriptors(l_param, r_param); if (param_diff != 0) { return param_diff < 0; } diff --git a/libdexfile/dex/signature-inl.h b/libdexfile/dex/signature-inl.h index f2760c3002..27906d3829 100644 --- a/libdexfile/dex/signature-inl.h +++ b/libdexfile/dex/signature-inl.h @@ -86,7 +86,7 @@ inline int Signature::Compare(const Signature& rhs) const { dex_file_->GetTypeId(proto_id_->return_type_idx_)); std::string_view rhs_return_type = rhs.dex_file_->GetTypeDescriptorView( rhs.dex_file_->GetTypeId(rhs.proto_id_->return_type_idx_)); - int cmp_result = lhs_return_type.compare(rhs_return_type); + int cmp_result = DexFile::CompareDescriptors(lhs_return_type, rhs_return_type); if (cmp_result != 0) { return cmp_result; } @@ -105,7 +105,7 @@ inline int Signature::Compare(const Signature& rhs) const { dex_file_->GetTypeId(lhs_params->GetTypeItem(i - 1u).type_idx_)); std::string_view rhs_param_type = rhs.dex_file_->GetTypeDescriptorView( rhs.dex_file_->GetTypeId(rhs_params->GetTypeItem(i - 1u).type_idx_)); - int cmp_result = lhs_param_type.compare(rhs_param_type); + int cmp_result = DexFile::CompareDescriptors(lhs_param_type, rhs_param_type); if (cmp_result != 0) { return cmp_result; } diff --git a/libdexfile/dex/type_lookup_table.cc b/libdexfile/dex/type_lookup_table.cc index fa5352e74c..f4e2313445 100644 --- a/libdexfile/dex/type_lookup_table.cc +++ b/libdexfile/dex/type_lookup_table.cc @@ -177,7 +177,7 @@ std::string_view TypeLookupTable::GetStringData(const Entry& entry) const { DCHECK(dex_data_begin_ != nullptr); const uint8_t* ptr = dex_data_begin_ + entry.GetStringOffset(); uint32_t utf16_length = DecodeUnsignedLeb128(&ptr); - return StringViewFromUtf16Length(reinterpret_cast<const char*>(ptr), utf16_length); + return DexFile::StringViewFromUtf16Length(reinterpret_cast<const char*>(ptr), utf16_length); } } // namespace art diff --git a/libelffile/elf/elf_builder.h b/libelffile/elf/elf_builder.h index 908ad5cb89..bb82b109bd 100644 --- a/libelffile/elf/elf_builder.h +++ b/libelffile/elf/elf_builder.h @@ -641,6 +641,7 @@ class ElfBuilder final { Elf_Word rodata_size, Elf_Word text_size, Elf_Word data_img_rel_ro_size, + Elf_Word data_img_rel_ro_app_image_offset, Elf_Word bss_size, Elf_Word bss_methods_offset, Elf_Word bss_roots_offset, @@ -682,6 +683,7 @@ class ElfBuilder final { Elf_Word oatlastword_address = rodata_.GetAddress() + rodata_size - 4; dynsym_.Add(oatlastword, &rodata_, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); } + DCHECK_LE(data_img_rel_ro_app_image_offset, data_img_rel_ro_size); if (data_img_rel_ro_size != 0u) { Elf_Word oatdataimgrelro = dynstr_.Add("oatdataimgrelro"); dynsym_.Add(oatdataimgrelro, @@ -691,14 +693,21 @@ class ElfBuilder final { STB_GLOBAL, STT_OBJECT); Elf_Word oatdataimgrelrolastword = dynstr_.Add("oatdataimgrelrolastword"); - Elf_Word oatdataimgrelrolastword_address = - data_img_rel_ro_.GetAddress() + data_img_rel_ro_size - 4; dynsym_.Add(oatdataimgrelrolastword, &data_img_rel_ro_, - oatdataimgrelrolastword_address, + data_img_rel_ro_.GetAddress() + data_img_rel_ro_size - 4, 4, STB_GLOBAL, STT_OBJECT); + if (data_img_rel_ro_app_image_offset != data_img_rel_ro_size) { + Elf_Word oatdataimgrelroappimage = dynstr_.Add("oatdataimgrelroappimage"); + dynsym_.Add(oatdataimgrelroappimage, + &data_img_rel_ro_, + data_img_rel_ro_.GetAddress() + data_img_rel_ro_app_image_offset, + data_img_rel_ro_app_image_offset, + STB_GLOBAL, + STT_OBJECT); + } } DCHECK_LE(bss_roots_offset, bss_size); if (bss_size != 0u) { diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp index ca054516f6..3dbb79b730 100644 --- a/libnativebridge/tests/Android.bp +++ b/libnativebridge/tests/Android.bp @@ -27,8 +27,9 @@ cc_defaults { name: "libnativebridge-test-case-defaults", defaults: [ "art_defaults", - "art_test_defaults", + "art_test_common_defaults", ], + host_supported: true, // TODO(mast): Split up art_gtest_defaults so that it can be used for the // following without pulling in lots of libs. target: { @@ -101,7 +102,7 @@ cc_test { name: "libnativebridge-tests", defaults: [ "art_defaults", - "art_test_defaults", + "art_test_internal_library_defaults", ], target: { @@ -115,19 +116,10 @@ cc_test { }, }, - // native_bridge.cc doesn't support reloading the native bridge after - // unloading, so each test needs to be its own process. - test_per_src: true, - - // Disable pre-submit host unit-testing for this test module, as - // it is not compatible with TradeFed (because of the use of the - // `test_per_src` feature above) and meant to be executed with the - // `runtests.sh` script instead. - test_options: { - unit_test: false, - }, + isolated: true, srcs: [ + "NativeBridgeTest.cpp", "NativeBridgeApi.c", "CodeCacheCreate_test.cpp", "CodeCacheExists_test.cpp", @@ -153,8 +145,25 @@ cc_test { ], shared_libs: [ + "libbase", "liblog", "libnativebridge", + "libnativebridge6prezygotefork", + "libnativebridge7criticalnative", + + // Ideally these would only need to be listed in data_libs, but they + // are dlopen'd by libnativebridge, not by libnativebridge-tests, + // and the linker can't find them relative to /system/lib64/libnativebridge.so. + // Linking them here causes them to be loaded from alongside + // libnativebridge-tests when it is executed, and then the later dlopen + // returns the handle to the already-loaded library. + "libnativebridge-test-case", + "libnativebridge2-test-case", + "libnativebridge3-test-case", + "libnativebridge6-test-case", + "libnativebridge7-test-case", + ], + data_libs: [ "libnativebridge-test-case", "libnativebridge2-test-case", "libnativebridge3-test-case", @@ -163,7 +172,10 @@ cc_test { "libnativebridge6prezygotefork", "libnativebridge7criticalnative", ], - header_libs: ["libbase_headers"], + + test_suites: [ + "general-tests", + ], } // Very basic tests in CTS to verify backed-by API coverage of the exported API diff --git a/libnativebridge/tests/CodeCacheCreate_test.cpp b/libnativebridge/tests/CodeCacheCreate_test.cpp index 1bd309c5c8..57b1abc4c5 100644 --- a/libnativebridge/tests/CodeCacheCreate_test.cpp +++ b/libnativebridge/tests/CodeCacheCreate_test.cpp @@ -26,25 +26,25 @@ namespace android { // exist. TEST_F(NativeBridgeTest, CodeCacheCreate) { // Make sure that code_cache does not exist - rmdir(kCodeCache); + rmdir(codeCache()); struct stat st; - ASSERT_EQ(-1, stat(kCodeCache, &st)); + ASSERT_EQ(-1, stat(codeCache(), &st)); ASSERT_EQ(ENOENT, errno); // Init ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr)); - ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(PreInitializeNativeBridge(appDataDir(), "isa")); ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); ASSERT_FALSE(NativeBridgeError()); // Check that code_cache was created - ASSERT_EQ(0, stat(kCodeCache, &st)); + ASSERT_EQ(0, stat(codeCache(), &st)); ASSERT_TRUE(S_ISDIR(st.st_mode)); // Clean up UnloadNativeBridge(); - ASSERT_EQ(0, rmdir(kCodeCache)); + ASSERT_EQ(0, rmdir(codeCache())); ASSERT_FALSE(NativeBridgeError()); } diff --git a/libnativebridge/tests/CodeCacheExists_test.cpp b/libnativebridge/tests/CodeCacheExists_test.cpp index 8ba01586b5..c740bec79c 100644 --- a/libnativebridge/tests/CodeCacheExists_test.cpp +++ b/libnativebridge/tests/CodeCacheExists_test.cpp @@ -27,26 +27,26 @@ namespace android { TEST_F(NativeBridgeTest, CodeCacheExists) { // Make sure that code_cache does not exists struct stat st; - ASSERT_EQ(-1, stat(kCodeCache, &st)); + ASSERT_EQ(-1, stat(codeCache(), &st)); ASSERT_EQ(ENOENT, errno); // Create the code_cache - ASSERT_EQ(0, mkdir(kCodeCache, S_IRWXU | S_IRWXG | S_IXOTH)); + ASSERT_EQ(0, mkdir(codeCache(), S_IRWXU | S_IRWXG | S_IXOTH)); // Init ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr)); - ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(PreInitializeNativeBridge(appDataDir(), "isa")); ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); ASSERT_FALSE(NativeBridgeError()); // Check that the code cache is still there - ASSERT_EQ(0, stat(kCodeCache, &st)); + ASSERT_EQ(0, stat(codeCache(), &st)); ASSERT_TRUE(S_ISDIR(st.st_mode)); // Clean up UnloadNativeBridge(); - ASSERT_EQ(0, rmdir(kCodeCache)); + ASSERT_EQ(0, rmdir(codeCache())); ASSERT_FALSE(NativeBridgeError()); } diff --git a/libnativebridge/tests/CodeCacheStatFail_test.cpp b/libnativebridge/tests/CodeCacheStatFail_test.cpp index 4ea519ee96..768f07944c 100644 --- a/libnativebridge/tests/CodeCacheStatFail_test.cpp +++ b/libnativebridge/tests/CodeCacheStatFail_test.cpp @@ -26,26 +26,26 @@ namespace android { // Tests that the bridge is initialized without errors if the code_cache is // existed as a file. TEST_F(NativeBridgeTest, CodeCacheStatFail) { - int fd = creat(kCodeCache, O_RDWR); - ASSERT_NE(-1, fd); - close(fd); - - struct stat st; - ASSERT_EQ(-1, stat(kCodeCacheStatFail, &st)); - ASSERT_EQ(ENOTDIR, errno); - - // Init - ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr)); - ASSERT_TRUE(PreInitializeNativeBridge(kCodeCacheStatFail, "isa")); - ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); - ASSERT_TRUE(NativeBridgeAvailable()); - ASSERT_FALSE(NativeBridgeError()); - - // Clean up - UnloadNativeBridge(); - - ASSERT_FALSE(NativeBridgeError()); - unlink(kCodeCache); + int fd = creat(codeCache(), O_RDWR); + ASSERT_NE(-1, fd); + close(fd); + + struct stat st; + ASSERT_EQ(-1, stat(codeCacheStatFail(), &st)); + ASSERT_EQ(ENOTDIR, errno); + + // Init + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr)); + ASSERT_TRUE(PreInitializeNativeBridge(codeCacheStatFail(), "isa")); + ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_FALSE(NativeBridgeError()); + + // Clean up + UnloadNativeBridge(); + + ASSERT_FALSE(NativeBridgeError()); + unlink(codeCache()); } } // namespace android diff --git a/libnativebridge/tests/CompleteFlow_test.cpp b/libnativebridge/tests/CompleteFlow_test.cpp index b033792301..a4a6f20df9 100644 --- a/libnativebridge/tests/CompleteFlow_test.cpp +++ b/libnativebridge/tests/CompleteFlow_test.cpp @@ -24,7 +24,7 @@ TEST_F(NativeBridgeTest, CompleteFlow) { // Init ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); - ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(PreInitializeNativeBridge(appDataDir(), "isa")); ASSERT_TRUE(NativeBridgeAvailable()); ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); @@ -41,7 +41,7 @@ TEST_F(NativeBridgeTest, CompleteFlow) { ASSERT_FALSE(NativeBridgeError()); // Clean-up code_cache - ASSERT_EQ(0, rmdir(kCodeCache)); + ASSERT_EQ(0, rmdir(codeCache())); } } // namespace android diff --git a/libnativebridge/tests/NativeBridge2Signal_test.cpp b/libnativebridge/tests/NativeBridge2Signal_test.cpp index 0573a5a680..a1663caeba 100644 --- a/libnativebridge/tests/NativeBridge2Signal_test.cpp +++ b/libnativebridge/tests/NativeBridge2Signal_test.cpp @@ -25,7 +25,7 @@ TEST_F(NativeBridgeTest, V2_Signal) { // Init ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary2, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); - ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(PreInitializeNativeBridge(appDataDir(), "isa")); ASSERT_TRUE(NativeBridgeAvailable()); ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); @@ -34,7 +34,7 @@ TEST_F(NativeBridgeTest, V2_Signal) { ASSERT_NE(nullptr, NativeBridgeGetSignalHandler(SIGSEGV)); // Clean-up code_cache - ASSERT_EQ(0, rmdir(kCodeCache)); + ASSERT_EQ(0, rmdir(codeCache())); } } // namespace android diff --git a/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp b/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp index db7dd31cfb..2b709e05ab 100644 --- a/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp +++ b/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp @@ -22,7 +22,7 @@ TEST_F(NativeBridgeTest, V3_CreateNamespace) { // Init ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); - ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(PreInitializeNativeBridge(appDataDir(), "isa")); ASSERT_TRUE(NativeBridgeAvailable()); ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); @@ -32,7 +32,7 @@ TEST_F(NativeBridgeTest, V3_CreateNamespace) { 0, nullptr, nullptr)); // Clean-up code_cache - ASSERT_EQ(0, rmdir(kCodeCache)); + ASSERT_EQ(0, rmdir(codeCache())); } } // namespace android diff --git a/libnativebridge/tests/NativeBridge3GetError_test.cpp b/libnativebridge/tests/NativeBridge3GetError_test.cpp index afd0a7d511..e74e664881 100644 --- a/libnativebridge/tests/NativeBridge3GetError_test.cpp +++ b/libnativebridge/tests/NativeBridge3GetError_test.cpp @@ -22,7 +22,7 @@ TEST_F(NativeBridgeTest, V3_GetError) { // Init ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); - ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(PreInitializeNativeBridge(appDataDir(), "isa")); ASSERT_TRUE(NativeBridgeAvailable()); ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); @@ -31,7 +31,7 @@ TEST_F(NativeBridgeTest, V3_GetError) { ASSERT_EQ(nullptr, NativeBridgeGetError()); // Clean-up code_cache - ASSERT_EQ(0, rmdir(kCodeCache)); + ASSERT_EQ(0, rmdir(codeCache())); } } // namespace android diff --git a/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp index f82c9e9e4c..2449939362 100644 --- a/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp +++ b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp @@ -22,7 +22,7 @@ TEST_F(NativeBridgeTest, V3_InitAnonymousNamespace) { // Init ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); - ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(PreInitializeNativeBridge(appDataDir(), "isa")); ASSERT_TRUE(NativeBridgeAvailable()); ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); @@ -31,7 +31,7 @@ TEST_F(NativeBridgeTest, V3_InitAnonymousNamespace) { ASSERT_EQ(true, NativeBridgeInitAnonymousNamespace(nullptr, nullptr)); // Clean-up code_cache - ASSERT_EQ(0, rmdir(kCodeCache)); + ASSERT_EQ(0, rmdir(codeCache())); } } // namespace android diff --git a/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp b/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp index 4cbc0e8758..fd162c099d 100644 --- a/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp +++ b/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp @@ -22,7 +22,7 @@ TEST_F(NativeBridgeTest, V3_IsPathSupported) { // Init ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); - ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(PreInitializeNativeBridge(appDataDir(), "isa")); ASSERT_TRUE(NativeBridgeAvailable()); ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); @@ -31,7 +31,7 @@ TEST_F(NativeBridgeTest, V3_IsPathSupported) { ASSERT_EQ(true, NativeBridgeIsPathSupported(nullptr)); // Clean-up code_cache - ASSERT_EQ(0, rmdir(kCodeCache)); + ASSERT_EQ(0, rmdir(codeCache())); } } // namespace android diff --git a/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp b/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp index 3d6676811a..eeb27e8431 100644 --- a/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp +++ b/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp @@ -22,7 +22,7 @@ TEST_F(NativeBridgeTest, V3_LoadLibraryExt) { // Init ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); - ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(PreInitializeNativeBridge(appDataDir(), "isa")); ASSERT_TRUE(NativeBridgeAvailable()); ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); @@ -31,7 +31,7 @@ TEST_F(NativeBridgeTest, V3_LoadLibraryExt) { ASSERT_EQ(nullptr, NativeBridgeLoadLibraryExt(nullptr, 0, nullptr)); // Clean-up code_cache - ASSERT_EQ(0, rmdir(kCodeCache)); + ASSERT_EQ(0, rmdir(codeCache())); } } // namespace android diff --git a/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp b/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp index a366edcdce..39bf251f65 100644 --- a/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp +++ b/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp @@ -22,7 +22,7 @@ TEST_F(NativeBridgeTest, V3_UnloadLibrary) { // Init ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); - ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(PreInitializeNativeBridge(appDataDir(), "isa")); ASSERT_TRUE(NativeBridgeAvailable()); ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); @@ -31,7 +31,7 @@ TEST_F(NativeBridgeTest, V3_UnloadLibrary) { ASSERT_EQ(0, NativeBridgeUnloadLibrary(nullptr)); // Clean-up code_cache - ASSERT_EQ(0, rmdir(kCodeCache)); + ASSERT_EQ(0, rmdir(codeCache())); } } // namespace android diff --git a/libnativebridge/tests/NativeBridge6PreZygoteFork_test.cpp b/libnativebridge/tests/NativeBridge6PreZygoteFork_test.cpp index ba6a007a93..b40e7bd79e 100644 --- a/libnativebridge/tests/NativeBridge6PreZygoteFork_test.cpp +++ b/libnativebridge/tests/NativeBridge6PreZygoteFork_test.cpp @@ -23,7 +23,7 @@ TEST_F(NativeBridgeTest, V6_PreZygoteFork) { // Init ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary6, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); - ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(PreInitializeNativeBridge(appDataDir(), "isa")); ASSERT_TRUE(NativeBridgeAvailable()); ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); diff --git a/libnativebridge/tests/NativeBridge7CriticalNative_test.cpp b/libnativebridge/tests/NativeBridge7CriticalNative_test.cpp index 5662214f40..a8c9bb65c4 100644 --- a/libnativebridge/tests/NativeBridge7CriticalNative_test.cpp +++ b/libnativebridge/tests/NativeBridge7CriticalNative_test.cpp @@ -23,7 +23,7 @@ TEST_F(NativeBridgeTest, V7_CriticalNative) { // Init ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary7, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); - ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(PreInitializeNativeBridge(appDataDir(), "isa")); ASSERT_TRUE(NativeBridgeAvailable()); ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); ASSERT_TRUE(NativeBridgeAvailable()); diff --git a/libnativebridge/tests/NativeBridgeTest.cpp b/libnativebridge/tests/NativeBridgeTest.cpp new file mode 100644 index 0000000000..633ff79fcb --- /dev/null +++ b/libnativebridge/tests/NativeBridgeTest.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <stdio.h> + +extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) { + // Disable default parallelism to make debugging easier, these tests are small. + static const char* initial_args[1] = {"-j1"}; + *args = initial_args; + *num_args = 1; + return true; +} diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h index 6413233006..782e58424b 100644 --- a/libnativebridge/tests/NativeBridgeTest.h +++ b/libnativebridge/tests/NativeBridgeTest.h @@ -19,12 +19,13 @@ #define LOG_TAG "NativeBridge_test" -#include <nativebridge/native_bridge.h> +#include <android-base/file.h> #include <gtest/gtest.h> +#include <nativebridge/native_bridge.h> + +#include <string> constexpr const char* kNativeBridgeLibrary = "libnativebridge-test-case.so"; -constexpr const char* kCodeCache = "./code_cache"; -constexpr const char* kCodeCacheStatFail = "./code_cache/temp"; constexpr const char* kNativeBridgeLibrary2 = "libnativebridge2-test-case.so"; constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-test-case.so"; constexpr const char* kNativeBridgeLibrary6 = "libnativebridge6-test-case.so"; @@ -33,6 +34,23 @@ constexpr const char* kNativeBridgeLibrary7 = "libnativebridge7-test-case.so"; namespace android { class NativeBridgeTest : public testing::Test { + protected: + NativeBridgeTest() : tempDir() { + appDataDir_ = std::string(tempDir.path); + codeCache_ = appDataDir_ + "/code_cache"; + codeCacheStatFail_ = codeCache_ + "/temp"; + } + + const char* appDataDir() { return appDataDir_.c_str(); } + + const char* codeCache() { return codeCache_.c_str(); } + + const char* codeCacheStatFail() { return codeCacheStatFail_.c_str(); } + + TemporaryDir tempDir; + std::string appDataDir_; + std::string codeCache_; + std::string codeCacheStatFail_; }; }; // namespace android diff --git a/libnativebridge/tests/preupload_check_test_tag.sh b/libnativebridge/tests/preupload_check_test_tag.sh deleted file mode 100755 index 1c8cd4b62c..0000000000 --- a/libnativebridge/tests/preupload_check_test_tag.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -# A simple preupload check that the runtests.sh script has been run for the -# libnativebridge tests if that library has been changed. -# TODO(b/189484095): Port these tests to atest and enable in presubmit so we -# don't need the custom script to run them. - -commit_message="$1" -shift - -nativebridge_change=false -for file; do - [[ $file = libnativebridge/* ]] && nativebridge_change=true -done - -if $nativebridge_change; then - if grep '^Test: art/libnativebridge/tests/runtests.sh' <<< $commit_message ; then :; else - echo "Please run art/libnativebridge/tests/runtests.sh and add the tag:" 1>&2 - echo "Test: art/libnativebridge/tests/runtests.sh [--skip-host|--skip-target]" 1>&2 - exit 1 - fi -fi diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp index 50d243095c..236f27fb92 100644 --- a/libnativeloader/Android.bp +++ b/libnativeloader/Android.bp @@ -66,19 +66,6 @@ art_cc_library { "libbase", ], target: { - // Library search path needed for running host tests remotely (from testcases directory). - linux_glibc_x86: { - ldflags: [ - "-Wl,-rpath,$ORIGIN/../art_common/out/host/linux-x86/lib", - "-Wl,--enable-new-dtags", - ], - }, - linux_glibc_x86_64: { - ldflags: [ - "-Wl,-rpath,$ORIGIN/../art_common/out/host/linux-x86/lib64", - "-Wl,--enable-new-dtags", - ], - }, android: { srcs: [ "library_namespaces.cpp", @@ -126,7 +113,6 @@ cc_library { art_cc_test { name: "libnativeloader_test", defaults: [ - "art_module_source_build_defaults", // Cannot use art_standalone_gtest_defaults because it makes us link // libnativebridge statically through libart-gtest, but we need to mock // its symbols here. diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp index ea06b2041f..13a4c2a482 100644 --- a/libnativeloader/native_loader.cpp +++ b/libnativeloader/native_loader.cpp @@ -420,8 +420,8 @@ void* OpenNativeLibrary(JNIEnv* env, /*is_shared=*/false, empty_dex_path, library_path_j, - /*permitted_path=*/nullptr, - /*uses_library_list=*/nullptr); + /*permitted_path_j=*/nullptr, + /*uses_library_list_j=*/nullptr); if (!isolated_ns.ok()) { ALOGD("Failed to create isolated ns for %s (caller=%s)", path, diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp index 390c2987d6..53fcd2f35b 100644 --- a/libnativeloader/public_libraries.cpp +++ b/libnativeloader/public_libraries.cpp @@ -34,6 +34,7 @@ #include <log/log.h> #if defined(ART_TARGET_ANDROID) +#include <android-modules-utils/sdk_level.h> #include <android/sysprop/VndkProperties.sysprop.h> #endif @@ -166,8 +167,8 @@ static std::string InitDefaultPublicLibraries(bool for_preload) { if (!for_preload) { // Remove the public libs provided by apexes because these libs are available // from apex namespaces. - for (const std::pair<std::string, std::string>& p : apex_public_libraries()) { - std::vector<std::string> public_libs = base::Split(p.second, ":"); + for (const auto& [_, library_list] : apex_public_libraries()) { + std::vector<std::string> public_libs = base::Split(library_list, ":"); sonames->erase(std::remove_if(sonames->begin(), sonames->end(), [&public_libs](const std::string& v) { @@ -426,12 +427,11 @@ const std::map<std::string, std::string>& apex_public_libraries() { bool is_product_treblelized() { #if defined(ART_TARGET_ANDROID) - // 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; + // Product is treblelized iff the sdk version is newer than U + // or launching version is R or newer or ro.product.vndk.version is defined + return android::modules::sdklevel::IsAtLeastV() || + android::base::GetIntProperty("ro.product.first_api_level", 0) >= __ANDROID_API_R__ || + android::sysprop::VndkProperties::product_vndk_version().has_value(); #else return false; #endif diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp index 6166725af7..4396917a57 100644 --- a/libnativeloader/test/Android.bp +++ b/libnativeloader/test/Android.bp @@ -55,7 +55,6 @@ cc_library { // library in the host test. It's not actually installed or started. android_test_helper_app { name: "library_container_app", - defaults: ["art_module_source_build_java_defaults"], min_sdk_version: "31", manifest: "library_container_app_manifest.xml", compile_multilib: "both", @@ -120,7 +119,6 @@ java_library { java_defaults { name: "loadlibrarytest_app_defaults", - defaults: ["art_module_source_build_java_defaults"], min_sdk_version: "31", // Don't let targetSdkVersion become the latest codename, because // PackageManager refuses to install the app on released platform images @@ -188,7 +186,6 @@ android_test_helper_app { java_test_host { name: "libnativeloader_e2e_tests", - defaults: ["art_module_source_build_java_defaults"], srcs: ["src/android/test/hostside/*.java"], libs: [ "compatibility-tradefed", diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc index 39c1438d69..631bf1148b 100644 --- a/libprofile/profile/profile_compilation_info.cc +++ b/libprofile/profile/profile_compilation_info.cc @@ -696,12 +696,12 @@ dex::TypeIndex ProfileCompilationInfo::FindOrCreateTypeIndex(const DexFile& dex_ return class_ref.TypeIndex(); } // Try to find a `TypeId` in the method's dex file. - const char* descriptor = class_ref.dex_file->GetTypeDescriptor(class_ref.TypeIndex()); + std::string_view descriptor = class_ref.dex_file->GetTypeDescriptorView(class_ref.TypeIndex()); return FindOrCreateTypeIndex(dex_file, descriptor); } dex::TypeIndex ProfileCompilationInfo::FindOrCreateTypeIndex(const DexFile& dex_file, - const char* descriptor) { + std::string_view descriptor) { const dex::TypeId* type_id = dex_file.FindTypeId(descriptor); if (type_id != nullptr) { return dex_file.GetIndexForTypeId(*type_id); @@ -709,12 +709,11 @@ dex::TypeIndex ProfileCompilationInfo::FindOrCreateTypeIndex(const DexFile& dex_ // Try to find an existing extra descriptor. uint32_t num_type_ids = dex_file.NumTypeIds(); uint32_t max_artificial_ids = DexFile::kDexNoIndex16 - num_type_ids; - std::string_view descriptor_view(descriptor); // Check descriptor length for "extra descriptor". We are using `uint16_t` as prefix. - if (UNLIKELY(descriptor_view.size() > kMaxExtraDescriptorLength)) { + if (UNLIKELY(descriptor.size() > kMaxExtraDescriptorLength)) { return dex::TypeIndex(); // Invalid. } - auto it = extra_descriptors_indexes_.find(descriptor_view); + auto it = extra_descriptors_indexes_.find(descriptor); if (it != extra_descriptors_indexes_.end()) { return (*it < max_artificial_ids) ? dex::TypeIndex(num_type_ids + *it) : dex::TypeIndex(); } @@ -723,13 +722,13 @@ dex::TypeIndex ProfileCompilationInfo::FindOrCreateTypeIndex(const DexFile& dex_ return dex::TypeIndex(); // Invalid. } // Add the descriptor to extra descriptors and return the artificial type index. - ExtraDescriptorIndex new_extra_descriptor_index = AddExtraDescriptor(descriptor_view); + ExtraDescriptorIndex new_extra_descriptor_index = AddExtraDescriptor(descriptor); DCHECK_LT(new_extra_descriptor_index, max_artificial_ids); return dex::TypeIndex(num_type_ids + new_extra_descriptor_index); } bool ProfileCompilationInfo::AddClass(const DexFile& dex_file, - const char* descriptor, + std::string_view descriptor, const ProfileSampleAnnotation& annotation) { DexFileData* const data = GetOrAddDexFileData(&dex_file, annotation); if (data == nullptr) { // checksum mismatch diff --git a/libprofile/profile/profile_compilation_info.h b/libprofile/profile/profile_compilation_info.h index bb16cf8dea..104198f1fa 100644 --- a/libprofile/profile/profile_compilation_info.h +++ b/libprofile/profile/profile_compilation_info.h @@ -336,7 +336,7 @@ class ProfileCompilationInfo { // The returned type index can be used, if valid, for `AddClass()` or (TODO) as // a type index for inline caches. dex::TypeIndex FindOrCreateTypeIndex(const DexFile& dex_file, TypeReference class_ref); - dex::TypeIndex FindOrCreateTypeIndex(const DexFile& dex_file, const char* descriptor); + dex::TypeIndex FindOrCreateTypeIndex(const DexFile& dex_file, std::string_view descriptor); // Add a class with the specified `type_index` to the profile. The `type_index` // can be either a normal index for a `TypeId` in the dex file, or an artificial @@ -371,18 +371,8 @@ class ProfileCompilationInfo { // Add a class with the specified `descriptor` to the profile. // Returns `true` on success, `false` on failure. bool AddClass(const DexFile& dex_file, - const char* descriptor, - const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone); - bool AddClass(const DexFile& dex_file, - const std::string& descriptor, - const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) { - return AddClass(dex_file, descriptor.c_str(), annotation); - } - bool AddClass(const DexFile& dex_file, std::string_view descriptor, - const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) { - return AddClass(dex_file, std::string(descriptor).c_str(), annotation); - } + const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone); // Add multiple type ids for classes in a single dex file. Iterator is for type_ids not // class_defs. diff --git a/libprofile/profile/profile_compilation_info_test.cc b/libprofile/profile/profile_compilation_info_test.cc index 25c560b78e..f2df896c70 100644 --- a/libprofile/profile/profile_compilation_info_test.cc +++ b/libprofile/profile/profile_compilation_info_test.cc @@ -1498,7 +1498,7 @@ TEST_F(ProfileCompilationInfoTest, AddMethodsProfileMethodInfoInlineCaches) { // Add inline caches with the methods. The profile should record only the one for the hot method. std::vector<TypeReference> types = {}; - ProfileMethodInfo::ProfileInlineCache ic(/*dex_pc=*/ 0, /*missing_types=*/ true, types); + ProfileMethodInfo::ProfileInlineCache ic(/*pc=*/0, /*missing_types=*/true, types); std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches = {ic}; info.AddMethod(ProfileMethodInfo(hot, inline_caches), Hotness::kFlagHot, diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 2c17845417..983316e9a2 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -183,6 +183,7 @@ class OatSymbolizer final { rodata_size, text_size, oat_file_->DataImgRelRoSize(), + oat_file_->DataImgRelRoAppImageOffset(), oat_file_->BssSize(), oat_file_->BssMethodsOffset(), oat_file_->BssRootsOffset(), diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index d0ce6a1f49..3d0c05734c 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -2555,14 +2555,14 @@ void Redefiner::ClassRedefinition::UpdateMethods(art::ObjPtr<art::mirror::Class> method.CopyFrom(&method, image_pointer_size); const art::dex::StringId* new_name_id = dex_file_->FindStringId(method.GetName()); art::dex::TypeIndex method_return_idx = - dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(method.GetReturnTypeDescriptor())); + dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(method.GetReturnTypeDescriptorView())); const auto* old_type_list = method.GetParameterTypeList(); std::vector<art::dex::TypeIndex> new_type_list; for (uint32_t i = 0; old_type_list != nullptr && i < old_type_list->Size(); i++) { new_type_list.push_back( dex_file_->GetIndexForTypeId( *dex_file_->FindTypeId( - old_dex_file.GetTypeDescriptor( + old_dex_file.GetTypeDescriptorView( old_dex_file.GetTypeId( old_type_list->GetTypeItem(i).type_idx_))))); } @@ -2589,11 +2589,10 @@ void Redefiner::ClassRedefinition::UpdateFields(art::ObjPtr<art::mirror::Class> // TODO The IFields & SFields pointers should be combined like the methods_ arrays were. for (auto fields_iter : {mclass->GetIFields(), mclass->GetSFields()}) { for (art::ArtField& field : fields_iter) { - std::string declaring_class_name; const art::dex::TypeId* new_declaring_id = - dex_file_->FindTypeId(field.GetDeclaringClass()->GetDescriptor(&declaring_class_name)); + dex_file_->FindTypeId(field.GetDeclaringClassDescriptorView()); const art::dex::StringId* new_name_id = dex_file_->FindStringId(field.GetName()); - const art::dex::TypeId* new_type_id = dex_file_->FindTypeId(field.GetTypeDescriptor()); + const art::dex::TypeId* new_type_id = dex_file_->FindTypeId(field.GetTypeDescriptorView()); CHECK(new_name_id != nullptr && new_type_id != nullptr && new_declaring_id != nullptr); const art::dex::FieldId* new_field_id = dex_file_->FindFieldId(*new_declaring_id, *new_name_id, *new_type_id); diff --git a/profman/profman.cc b/profman/profman.cc index f8266697ed..b56fdf2523 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -1260,7 +1260,7 @@ class ProfMan final { return !receiver_.has_value(); } - const std::string_view& GetReceiverType() const { + std::string_view GetReceiverType() const { DCHECK(!IsSingleReceiver()); return *receiver_; } @@ -1550,9 +1550,8 @@ class ProfMan final { } } else { // Get the type-ref the method code will use. - std::string receiver_str(segment.GetReceiverType()); - const dex::TypeId *type_id = - class_ref.dex_file->FindTypeId(receiver_str.c_str()); + std::string_view receiver_descriptor = segment.GetReceiverType(); + const dex::TypeId *type_id = class_ref.dex_file->FindTypeId(receiver_descriptor); if (type_id == nullptr) { LOG(WARNING) << "Could not find class: " << segment.GetReceiverType() << " in dex-file " diff --git a/runtime/Android.bp b/runtime/Android.bp index e8fb912789..35f37aca54 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -652,6 +652,7 @@ cc_defaults { "liblz4", "liblzma", // libelffile dependency; must be repeated here since it's a static lib. "libnativebridge", + "libnativeloader", "libodrstatslog", ], target: { @@ -661,6 +662,11 @@ cc_defaults { "-Wno-unused-command-line-argument", ], }, + android: { + whole_static_libs: [ + "libPlatformProperties", // libnativeloader dependency. + ], + }, }, } @@ -677,7 +683,6 @@ cc_defaults { "libart-runtime", "libelffile", "libsigchain_fake", - "libnativeloader", ], } @@ -694,7 +699,6 @@ cc_defaults { "libartd-runtime", "libelffiled", "libsigchain_fake", - "libnativeloader", ], } @@ -945,6 +949,7 @@ art_cc_defaults { ], srcs: [ "common_runtime_test.cc", + "common_transaction_test.cc", "dexopt_test.cc", ], static_libs: [ @@ -1090,6 +1095,7 @@ art_cc_defaults { "intern_table_test.cc", "interpreter/safe_math_test.cc", "interpreter/unstarted_runtime_test.cc", + "interpreter/unstarted_runtime_transaction_test.cc", "jit/jit_memory_region_test.cc", "jit/profile_saver_test.cc", "jit/profiling_info_test.cc", diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index eca5c4b042..6f4bf8ce2b 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1416,7 +1416,7 @@ ENTRY art_quick_generic_jni_trampoline vmov d0, r0, r1 LOAD_RUNTIME_INSTANCE r2 - ldr r2, [r2, #RUN_EXIT_HOOKS_OFFSET_FROM_RUNTIME_INSTANCE] + ldrb r2, [r2, #RUN_EXIT_HOOKS_OFFSET_FROM_RUNTIME_INSTANCE] CFI_REMEMBER_STATE cbnz r2, .Lcall_method_exit_hook .Lcall_method_exit_hook_done: diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index 7137658387..cbe38db80e 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -318,18 +318,27 @@ inline void ArtField::SetObject(ObjPtr<mirror::Object> object, ObjPtr<mirror::Ob SetObj<kTransactionActive>(object, l); } -inline const char* ArtField::GetName() REQUIRES_SHARED(Locks::mutator_lock_) { +inline const char* ArtField::GetName() { uint32_t field_index = GetDexFieldIndex(); if (UNLIKELY(IsProxyField())) { DCHECK(IsStatic()); DCHECK_LT(field_index, 2U); return field_index == 0 ? "interfaces" : "throws"; } - const DexFile* dex_file = GetDexFile(); - return dex_file->GetFieldName(dex_file->GetFieldId(field_index)); + return GetDexFile()->GetFieldName(field_index); } -inline const char* ArtField::GetTypeDescriptor() REQUIRES_SHARED(Locks::mutator_lock_) { +inline std::string_view ArtField::GetNameView() { + uint32_t field_index = GetDexFieldIndex(); + if (UNLIKELY(IsProxyField())) { + DCHECK(IsStatic()); + DCHECK_LT(field_index, 2U); + return field_index == 0 ? "interfaces" : "throws"; + } + return GetDexFile()->GetFieldNameView(field_index); +} + +inline const char* ArtField::GetTypeDescriptor() { uint32_t field_index = GetDexFieldIndex(); if (UNLIKELY(IsProxyField())) { DCHECK(IsStatic()); @@ -337,17 +346,25 @@ inline const char* ArtField::GetTypeDescriptor() REQUIRES_SHARED(Locks::mutator_ // 0 == Class[] interfaces; 1 == Class[][] throws; return field_index == 0 ? "[Ljava/lang/Class;" : "[[Ljava/lang/Class;"; } - const DexFile* dex_file = GetDexFile(); - const dex::FieldId& field_id = dex_file->GetFieldId(field_index); - return dex_file->GetFieldTypeDescriptor(field_id); + return GetDexFile()->GetFieldTypeDescriptor(field_index); } -inline Primitive::Type ArtField::GetTypeAsPrimitiveType() - REQUIRES_SHARED(Locks::mutator_lock_) { +inline std::string_view ArtField::GetTypeDescriptorView() { + uint32_t field_index = GetDexFieldIndex(); + if (UNLIKELY(IsProxyField())) { + DCHECK(IsStatic()); + DCHECK_LT(field_index, 2U); + // 0 == Class[] interfaces; 1 == Class[][] throws; + return field_index == 0 ? "[Ljava/lang/Class;" : "[[Ljava/lang/Class;"; + } + return GetDexFile()->GetFieldTypeDescriptorView(field_index); +} + +inline Primitive::Type ArtField::GetTypeAsPrimitiveType() { return Primitive::GetType(GetTypeDescriptor()[0]); } -inline bool ArtField::IsPrimitiveType() REQUIRES_SHARED(Locks::mutator_lock_) { +inline bool ArtField::IsPrimitiveType() { return GetTypeAsPrimitiveType() != Primitive::kPrimNot; } @@ -372,20 +389,30 @@ inline ObjPtr<mirror::Class> ArtField::ResolveType() { return type; } -inline size_t ArtField::FieldSize() REQUIRES_SHARED(Locks::mutator_lock_) { +inline size_t ArtField::FieldSize() { return Primitive::ComponentSize(GetTypeAsPrimitiveType()); } template <ReadBarrierOption kReadBarrierOption> -inline ObjPtr<mirror::DexCache> ArtField::GetDexCache() REQUIRES_SHARED(Locks::mutator_lock_) { +inline ObjPtr<mirror::DexCache> ArtField::GetDexCache() { ObjPtr<mirror::Class> klass = GetDeclaringClass<kReadBarrierOption>(); return klass->GetDexCache<kDefaultVerifyFlags, kReadBarrierOption>(); } -inline const DexFile* ArtField::GetDexFile() REQUIRES_SHARED(Locks::mutator_lock_) { +inline const DexFile* ArtField::GetDexFile() { return GetDexCache<kWithoutReadBarrier>()->GetDexFile(); } +inline const char* ArtField::GetDeclaringClassDescriptor() { + DCHECK(!IsProxyField()); + return GetDexFile()->GetFieldDeclaringClassDescriptor(GetDexFieldIndex()); +} + +inline std::string_view ArtField::GetDeclaringClassDescriptorView() { + DCHECK(!IsProxyField()); + return GetDexFile()->GetFieldDeclaringClassDescriptorView(GetDexFieldIndex()); +} + inline ObjPtr<mirror::String> ArtField::ResolveNameString() { uint32_t dex_field_index = GetDexFieldIndex(); CHECK_NE(dex_field_index, dex::kDexNoIndex); diff --git a/runtime/art_field.cc b/runtime/art_field.cc index dcfd7582fe..70d6d2ba94 100644 --- a/runtime/art_field.cc +++ b/runtime/art_field.cc @@ -60,6 +60,7 @@ std::string ArtField::PrettyField(bool with_type) { result += PrettyDescriptor(GetTypeDescriptor()); result += ' '; } + // Note: `GetDeclaringClassDescriptor()` does not support proxy classes. std::string temp; result += PrettyDescriptor(GetDeclaringClass()->GetDescriptor(&temp)); result += '.'; diff --git a/runtime/art_field.h b/runtime/art_field.h index 49899dafd6..141de5650d 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -214,11 +214,13 @@ class EXPORT ArtField final { REQUIRES_SHARED(Locks::mutator_lock_); const char* GetName() REQUIRES_SHARED(Locks::mutator_lock_); + std::string_view GetNameView() REQUIRES_SHARED(Locks::mutator_lock_); // Resolves / returns the name from the dex cache. ObjPtr<mirror::String> ResolveNameString() REQUIRES_SHARED(Locks::mutator_lock_); const char* GetTypeDescriptor() REQUIRES_SHARED(Locks::mutator_lock_); + std::string_view GetTypeDescriptorView() REQUIRES_SHARED(Locks::mutator_lock_); Primitive::Type GetTypeAsPrimitiveType() REQUIRES_SHARED(Locks::mutator_lock_); @@ -234,6 +236,9 @@ class EXPORT ArtField final { const DexFile* GetDexFile() REQUIRES_SHARED(Locks::mutator_lock_); + const char* GetDeclaringClassDescriptor() REQUIRES_SHARED(Locks::mutator_lock_); + std::string_view GetDeclaringClassDescriptorView() REQUIRES_SHARED(Locks::mutator_lock_); + GcRoot<mirror::Class>& DeclaringClassRoot() { return declaring_class_; } diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 2c6a07126b..b2711d968a 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -381,13 +381,15 @@ inline const DexFile* ArtMethod::GetDexFile() { } inline const char* ArtMethod::GetDeclaringClassDescriptor() { - uint32_t dex_method_idx = GetDexMethodIndex(); - if (UNLIKELY(dex_method_idx == dex::kDexNoIndex)) { - return "<runtime method>"; - } + DCHECK(!IsRuntimeMethod()); DCHECK(!IsProxyMethod()); - const DexFile* dex_file = GetDexFile(); - return dex_file->GetMethodDeclaringClassDescriptor(dex_file->GetMethodId(dex_method_idx)); + return GetDexFile()->GetMethodDeclaringClassDescriptor(GetDexMethodIndex()); +} + +inline std::string_view ArtMethod::GetDeclaringClassDescriptorView() { + DCHECK(!IsRuntimeMethod()); + DCHECK(!IsProxyMethod()); + return GetDexFile()->GetMethodDeclaringClassDescriptorView(GetDexMethodIndex()); } inline const char* ArtMethod::GetShorty() { @@ -515,9 +517,11 @@ inline size_t ArtMethod::GetNumberOfParameters() { } inline const char* ArtMethod::GetReturnTypeDescriptor() { - DCHECK(!IsProxyMethod()); - const DexFile* dex_file = GetDexFile(); - return dex_file->GetTypeDescriptor(dex_file->GetTypeId(GetReturnTypeIndex())); + return GetDexFile()->GetTypeDescriptor(GetReturnTypeIndex()); +} + +inline std::string_view ArtMethod::GetReturnTypeDescriptorView() { + return GetDexFile()->GetTypeDescriptorView(GetReturnTypeIndex()); } inline Primitive::Type ArtMethod::GetReturnTypePrimitive() { @@ -573,6 +577,7 @@ inline ArtMethod* ArtMethod::GetInterfaceMethodIfProxy(PointerSize pointer_size) } inline dex::TypeIndex ArtMethod::GetReturnTypeIndex() { + DCHECK(!IsRuntimeMethod()); DCHECK(!IsProxyMethod()); const DexFile* dex_file = GetDexFile(); const dex::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex()); diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 0b6b883bab..11df3e6106 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -302,7 +302,7 @@ uint32_t ArtMethod::FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfil if (dexfile == &other_dexfile) { return dex_method_idx; } - const char* mid_declaring_class_descriptor = dexfile->GetTypeDescriptor(mid.class_idx_); + std::string_view mid_declaring_class_descriptor = dexfile->GetTypeDescriptorView(mid.class_idx_); const dex::TypeId* other_type_id = other_dexfile.FindTypeId(mid_declaring_class_descriptor); if (other_type_id != nullptr) { const dex::MethodId* other_mid = other_dexfile.FindMethodId( @@ -487,9 +487,8 @@ static const OatFile::OatMethod FindOatMethodFromDexFileFor(ArtMethod* method, b const DexFile* dex_file = method->GetDexFile(); // recreate the class_def_index from the descriptor. - std::string descriptor_storage; const dex::TypeId* declaring_class_type_id = - dex_file->FindTypeId(method->GetDeclaringClass()->GetDescriptor(&descriptor_storage)); + dex_file->FindTypeId(method->GetDeclaringClassDescriptorView()); CHECK(declaring_class_type_id != nullptr); dex::TypeIndex declaring_class_type_index = dex_file->GetIndexForTypeId(*declaring_class_type_id); const dex::ClassDef* declaring_class_type_def = @@ -848,8 +847,7 @@ std::string ArtMethod::PrettyMethod(ArtMethod* m, bool with_signature) { std::string ArtMethod::PrettyMethod(bool with_signature) { if (UNLIKELY(IsRuntimeMethod())) { - std::string result = GetDeclaringClassDescriptor(); - result += '.'; + std::string result = "<runtime method>."; result += GetName(); // Do not add "<no signature>" even if `with_signature` is true. return result; @@ -924,19 +922,32 @@ ALWAYS_INLINE static inline void DoGetAccessFlagsHelper(ArtMethod* method) method->GetDeclaringClass<kReadBarrierOption>()->IsErroneous()); } +template <typename T> +const void* Exchange(uintptr_t ptr, uintptr_t new_value) { + std::atomic<T>* atomic_addr = reinterpret_cast<std::atomic<T>*>(ptr); + return reinterpret_cast<const void*>( + atomic_addr->exchange(dchecked_integral_cast<T>(new_value), std::memory_order_relaxed)); +} + void ArtMethod::SetEntryPointFromQuickCompiledCodePtrSize( const void* entry_point_from_quick_compiled_code, PointerSize pointer_size) { const void* current_entry_point = GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); if (current_entry_point == entry_point_from_quick_compiled_code) { return; } + + // Do an atomic exchange to avoid potentially unregistering JIT code twice. + MemberOffset offset = EntryPointFromQuickCompiledCodeOffset(pointer_size); + uintptr_t new_value = reinterpret_cast<uintptr_t>(entry_point_from_quick_compiled_code); + uintptr_t ptr = reinterpret_cast<uintptr_t>(this) + offset.Uint32Value(); + const void* old_value = (pointer_size == PointerSize::k32) + ? Exchange<uint32_t>(ptr, new_value) + : Exchange<uint64_t>(ptr, new_value); + jit::Jit* jit = Runtime::Current()->GetJit(); - SetNativePointer(EntryPointFromQuickCompiledCodeOffset(pointer_size), - entry_point_from_quick_compiled_code, - pointer_size); if (jit != nullptr && - jit->GetCodeCache()->ContainsPc(current_entry_point)) { - jit->GetCodeCache()->AddZombieCode(this, current_entry_point); + jit->GetCodeCache()->ContainsPc(old_value)) { + jit->GetCodeCache()->AddZombieCode(this, old_value); } } diff --git a/runtime/art_method.h b/runtime/art_method.h index 97e1e6fa25..bf1b4631c6 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -903,6 +903,7 @@ class EXPORT ArtMethod final { const DexFile* GetDexFile() REQUIRES_SHARED(Locks::mutator_lock_); const char* GetDeclaringClassDescriptor() REQUIRES_SHARED(Locks::mutator_lock_); + std::string_view GetDeclaringClassDescriptorView() REQUIRES_SHARED(Locks::mutator_lock_); ALWAYS_INLINE const char* GetShorty() REQUIRES_SHARED(Locks::mutator_lock_); @@ -937,6 +938,7 @@ class EXPORT ArtMethod final { ALWAYS_INLINE size_t GetNumberOfParameters() REQUIRES_SHARED(Locks::mutator_lock_); const char* GetReturnTypeDescriptor() REQUIRES_SHARED(Locks::mutator_lock_); + std::string_view GetReturnTypeDescriptorView() REQUIRES_SHARED(Locks::mutator_lock_); ALWAYS_INLINE Primitive::Type GetReturnTypePrimitive() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 5b00a87217..7359056d32 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -5857,13 +5857,13 @@ bool ClassLinker::InitializeClass(Thread* self, WrapExceptionInInitializer(klass); mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); success = false; - } else if (Runtime::Current()->IsTransactionAborted()) { + } else if (Runtime::Current()->IsActiveTransaction() && IsTransactionAborted()) { // The exception thrown when the transaction aborted has been caught and cleared // so we need to throw it again now. VLOG(compiler) << "Return from class initializer of " << mirror::Class::PrettyDescriptor(klass.Get()) << " without exception while transaction was aborted: re-throw it now."; - runtime->ThrowTransactionAbortError(self); + ThrowTransactionAbortError(self); mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); success = false; } else { @@ -6403,7 +6403,7 @@ bool ClassLinker::LinkClass(Thread* self, const ObjPtr<mirror::ClassLoader> class_loader = h_new_class.Get()->GetClassLoader(); ClassTable* const table = InsertClassTableForClassLoader(class_loader); const ObjPtr<mirror::Class> existing = - table->UpdateClass(descriptor, h_new_class.Get(), ComputeModifiedUtf8Hash(descriptor)); + table->UpdateClass(h_new_class.Get(), ComputeModifiedUtf8Hash(descriptor)); CHECK_EQ(existing, klass.Get()); WriteBarrierOnClassLoaderLocked(class_loader, h_new_class.Get()); } @@ -11154,7 +11154,8 @@ bool ClassLinker::DenyAccessBasedOnPublicSdk([[maybe_unused]] ArtField* art_fiel UNREACHABLE(); } -bool ClassLinker::DenyAccessBasedOnPublicSdk([[maybe_unused]] const char* type_descriptor) const { +bool ClassLinker::DenyAccessBasedOnPublicSdk( + [[maybe_unused]] std::string_view type_descriptor) const { // Should not be called on ClassLinker, only on AotClassLinker that overrides this. LOG(FATAL) << "UNREACHABLE"; UNREACHABLE(); @@ -11166,6 +11167,167 @@ void ClassLinker::SetEnablePublicSdkChecks([[maybe_unused]] bool enabled) { UNREACHABLE(); } +bool ClassLinker::TransactionWriteConstraint( + [[maybe_unused]] Thread* self, [[maybe_unused]] ObjPtr<mirror::Object> obj) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +bool ClassLinker::TransactionWriteValueConstraint( + [[maybe_unused]] Thread* self, [[maybe_unused]] ObjPtr<mirror::Object> value) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +bool ClassLinker::TransactionAllocationConstraint( + [[maybe_unused]] Thread* self, [[maybe_unused]] ObjPtr<mirror::Class> klass) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::RecordWriteFieldBoolean([[maybe_unused]] mirror::Object* obj, + [[maybe_unused]] MemberOffset field_offset, + [[maybe_unused]] uint8_t value, + [[maybe_unused]] bool is_volatile) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::RecordWriteFieldByte([[maybe_unused]] mirror::Object* obj, + [[maybe_unused]] MemberOffset field_offset, + [[maybe_unused]] int8_t value, + [[maybe_unused]] bool is_volatile) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::RecordWriteFieldChar([[maybe_unused]] mirror::Object* obj, + [[maybe_unused]] MemberOffset field_offset, + [[maybe_unused]] uint16_t value, + [[maybe_unused]] bool is_volatile) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::RecordWriteFieldShort([[maybe_unused]] mirror::Object* obj, + [[maybe_unused]] MemberOffset field_offset, + [[maybe_unused]] int16_t value, + [[maybe_unused]] bool is_volatile) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::RecordWriteField32([[maybe_unused]] mirror::Object* obj, + [[maybe_unused]] MemberOffset field_offset, + [[maybe_unused]] uint32_t value, + [[maybe_unused]] bool is_volatile) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::RecordWriteField64([[maybe_unused]] mirror::Object* obj, + [[maybe_unused]] MemberOffset field_offset, + [[maybe_unused]] uint64_t value, + [[maybe_unused]] bool is_volatile) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::RecordWriteFieldReference([[maybe_unused]] mirror::Object* obj, + [[maybe_unused]] MemberOffset field_offset, + [[maybe_unused]] ObjPtr<mirror::Object> value, + [[maybe_unused]] bool is_volatile) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::RecordWriteArray([[maybe_unused]] mirror::Array* array, + [[maybe_unused]] size_t index, + [[maybe_unused]] uint64_t value) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::RecordStrongStringInsertion([[maybe_unused]] ObjPtr<mirror::String> s) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::RecordWeakStringInsertion([[maybe_unused]] ObjPtr<mirror::String> s) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::RecordStrongStringRemoval([[maybe_unused]] ObjPtr<mirror::String> s) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::RecordWeakStringRemoval([[maybe_unused]] ObjPtr<mirror::String> s) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::RecordResolveString([[maybe_unused]] ObjPtr<mirror::DexCache> dex_cache, + [[maybe_unused]] dex::StringIndex string_idx) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::RecordResolveMethodType([[maybe_unused]] ObjPtr<mirror::DexCache> dex_cache, + [[maybe_unused]] dex::ProtoIndex proto_idx) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::ThrowTransactionAbortError([[maybe_unused]] Thread* self) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::AbortTransactionF( + [[maybe_unused]] Thread* self, [[maybe_unused]] const char* fmt, ...) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::AbortTransactionV([[maybe_unused]] Thread* self, + [[maybe_unused]] const char* fmt, + [[maybe_unused]] va_list args) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +bool ClassLinker::IsTransactionAborted() const { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::VisitTransactionRoots([[maybe_unused]] RootVisitor* visitor) { + // Nothing to do for normal `ClassLinker`, only `AotClassLinker` handles transactions. +} + void ClassLinker::RemoveDexFromCaches(const DexFile& dex_file) { ReaderMutexLock mu(Thread::Current(), *Locks::dex_lock_); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index f2c08b93c5..daf9534c3a 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -64,7 +64,6 @@ class OatFile; template<class T> class ObjectLock; class Runtime; class ScopedObjectAccessAlreadyRunnable; -class SdkChecker; template<size_t kNumReferences> class PACKED(4) StackHandleScope; class Thread; class VariableSizedHandleScope; @@ -880,16 +879,85 @@ class EXPORT ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - // Verifies if the method is accesible according to the SdkChecker (if installed). + // Verifies if the method is accessible according to the SdkChecker (if installed). virtual bool DenyAccessBasedOnPublicSdk(ArtMethod* art_method) const REQUIRES_SHARED(Locks::mutator_lock_); - // Verifies if the field is accesible according to the SdkChecker (if installed). + // Verifies if the field is accessible according to the SdkChecker (if installed). virtual bool DenyAccessBasedOnPublicSdk(ArtField* art_field) const REQUIRES_SHARED(Locks::mutator_lock_); - // Verifies if the descriptor is accesible according to the SdkChecker (if installed). - virtual bool DenyAccessBasedOnPublicSdk(const char* type_descriptor) const; + // Verifies if the descriptor is accessible according to the SdkChecker (if installed). + virtual bool DenyAccessBasedOnPublicSdk(std::string_view type_descriptor) const; // Enable or disable public sdk checks. virtual void SetEnablePublicSdkChecks(bool enabled); + + // Transaction constraint checks for AOT compilation. + virtual bool TransactionWriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) + REQUIRES_SHARED(Locks::mutator_lock_); + virtual bool TransactionWriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value) + REQUIRES_SHARED(Locks::mutator_lock_); + virtual bool TransactionAllocationConstraint(Thread* self, ObjPtr<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Transaction bookkeeping for AOT compilation. + virtual void RecordWriteFieldBoolean(mirror::Object* obj, + MemberOffset field_offset, + uint8_t value, + bool is_volatile); + virtual void RecordWriteFieldByte(mirror::Object* obj, + MemberOffset field_offset, + int8_t value, + bool is_volatile); + virtual void RecordWriteFieldChar(mirror::Object* obj, + MemberOffset field_offset, + uint16_t value, + bool is_volatile); + virtual void RecordWriteFieldShort(mirror::Object* obj, + MemberOffset field_offset, + int16_t value, + bool is_volatile); + virtual void RecordWriteField32(mirror::Object* obj, + MemberOffset field_offset, + uint32_t value, + bool is_volatile); + virtual void RecordWriteField64(mirror::Object* obj, + MemberOffset field_offset, + uint64_t value, + bool is_volatile); + virtual void RecordWriteFieldReference(mirror::Object* obj, + MemberOffset field_offset, + ObjPtr<mirror::Object> value, + bool is_volatile) + REQUIRES_SHARED(Locks::mutator_lock_); + virtual void RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) + REQUIRES_SHARED(Locks::mutator_lock_); + virtual void RecordStrongStringInsertion(ObjPtr<mirror::String> s) + REQUIRES(Locks::intern_table_lock_); + virtual void RecordWeakStringInsertion(ObjPtr<mirror::String> s) + REQUIRES(Locks::intern_table_lock_); + virtual void RecordStrongStringRemoval(ObjPtr<mirror::String> s) + REQUIRES(Locks::intern_table_lock_); + virtual void RecordWeakStringRemoval(ObjPtr<mirror::String> s) + REQUIRES(Locks::intern_table_lock_); + virtual void RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, dex::StringIndex string_idx) + REQUIRES_SHARED(Locks::mutator_lock_); + virtual void RecordResolveMethodType(ObjPtr<mirror::DexCache> dex_cache, + dex::ProtoIndex proto_idx) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Aborting transactions for AOT compilation. + virtual void ThrowTransactionAbortError(Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_); + virtual void AbortTransactionF(Thread* self, const char* fmt, ...) + __attribute__((__format__(__printf__, 3, 4))) + REQUIRES_SHARED(Locks::mutator_lock_); + virtual void AbortTransactionV(Thread* self, const char* fmt, va_list args) + REQUIRES_SHARED(Locks::mutator_lock_); + virtual bool IsTransactionAborted() const; + + // Vist transaction roots for AOT compilation. + virtual void VisitTransactionRoots(RootVisitor* visitor) + REQUIRES_SHARED(Locks::mutator_lock_); + void RemoveDexFromCaches(const DexFile& dex_file); ClassTable* GetBootClassTable() REQUIRES_SHARED(Locks::classlinker_classes_lock_) { return boot_class_table_.get(); diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h index 8a8487c412..2ea49dbef0 100644 --- a/runtime/class_table-inl.h +++ b/runtime/class_table-inl.h @@ -32,8 +32,9 @@ inline ClassTable::TableSlot::TableSlot(ObjPtr<mirror::Class> klass) : TableSlot(klass, klass->DescriptorHash()) {} inline uint32_t ClassTable::ClassDescriptorHash::operator()(const TableSlot& slot) const { - // No read barriers needed, we're reading a chain of constant references for comparison with null - // and retrieval of constant primitive data. See `ReadBarrierOption` and `Class::DescriptorHash()`. + // No read barriers needed, we're reading a chain of constant references + // for comparison with null and retrieval of constant primitive data. + // See `ReadBarrierOption` and `Class::DescriptorHash()`. return slot.Read<kWithoutReadBarrier>()->DescriptorHash(); } @@ -44,17 +45,14 @@ inline uint32_t ClassTable::ClassDescriptorHash::operator()(const DescriptorHash inline bool ClassTable::ClassDescriptorEquals::operator()(const TableSlot& a, const TableSlot& b) const { - // No read barrier needed, we're reading a chain of constant references for comparison - // with null and retrieval of constant primitive data. See ReadBarrierOption. + // No read barrier needed, we're reading a chain of constant references + // for comparison with null and retrieval of constant primitive data. + // See ReadBarrierOption and `Class::DescriptorEquals()`. if (a.Hash() != b.Hash()) { - std::string temp; - DCHECK(!a.Read<kWithoutReadBarrier>()->DescriptorEquals( - b.Read<kWithoutReadBarrier>()->GetDescriptor(&temp))); + DCHECK(!a.Read<kWithoutReadBarrier>()->DescriptorEquals(b.Read<kWithoutReadBarrier>())); return false; } - std::string temp; - return a.Read<kWithoutReadBarrier>()->DescriptorEquals( - b.Read<kWithoutReadBarrier>()->GetDescriptor(&temp)); + return a.Read<kWithoutReadBarrier>()->DescriptorEquals(b.Read<kWithoutReadBarrier>()); } inline bool ClassTable::ClassDescriptorEquals::operator()(const TableSlot& a, diff --git a/runtime/class_table.cc b/runtime/class_table.cc index f66bd1e7ad..35d3537478 100644 --- a/runtime/class_table.cc +++ b/runtime/class_table.cc @@ -38,30 +38,30 @@ void ClassTable::FreezeSnapshot() { classes_.push_back(std::move(new_set)); } -ObjPtr<mirror::Class> ClassTable::UpdateClass(const char* descriptor, - ObjPtr<mirror::Class> klass, - size_t hash) { +ObjPtr<mirror::Class> ClassTable::UpdateClass(ObjPtr<mirror::Class> klass, size_t hash) { WriterMutexLock mu(Thread::Current(), lock_); // Should only be updating latest table. - DescriptorHashPair pair(descriptor, hash); - auto existing_it = classes_.back().FindWithHash(pair, hash); - if (existing_it == classes_.back().end()) { + TableSlot slot(klass, hash); + auto existing_it = classes_.back().FindWithHash(slot, hash); + if (UNLIKELY(existing_it == classes_.back().end())) { for (const ClassSet& class_set : classes_) { - if (class_set.FindWithHash(pair, hash) != class_set.end()) { - LOG(FATAL) << "Updating class found in frozen table " << descriptor; + if (class_set.FindWithHash(slot, hash) != class_set.end()) { + LOG(FATAL) << "Updating class found in frozen table " << klass->PrettyDescriptor(); + UNREACHABLE(); } } - LOG(FATAL) << "Updating class not found " << descriptor; + LOG(FATAL) << "Updating class not found " << klass->PrettyDescriptor(); + UNREACHABLE(); } const ObjPtr<mirror::Class> existing = existing_it->Read(); - CHECK_NE(existing, klass) << descriptor; - CHECK(!existing->IsResolved()) << descriptor; - CHECK_EQ(klass->GetStatus(), ClassStatus::kResolving) << descriptor; - CHECK(!klass->IsTemp()) << descriptor; + CHECK_NE(existing, klass) << klass->PrettyDescriptor(); + CHECK(!existing->IsResolved()) << klass->PrettyDescriptor(); + CHECK_EQ(klass->GetStatus(), ClassStatus::kResolving) << klass->PrettyDescriptor(); + CHECK(!klass->IsTemp()) << klass->PrettyDescriptor(); VerifyObject(klass); // Update the element in the hash set with the new class. This is safe to do since the descriptor // doesn't change. - *existing_it = TableSlot(klass, hash); + *existing_it = slot; return existing; } diff --git a/runtime/class_table.h b/runtime/class_table.h index 1c9b0ce2e9..3374622f00 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -187,9 +187,7 @@ class ClassTable { REQUIRES_SHARED(Locks::mutator_lock_); // Update a class in the table with the new class. Returns the existing class which was replaced. - ObjPtr<mirror::Class> UpdateClass(const char* descriptor, - ObjPtr<mirror::Class> new_klass, - size_t hash) + ObjPtr<mirror::Class> UpdateClass(ObjPtr<mirror::Class> new_klass, size_t hash) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h index 58b54af078..67d38ec105 100644 --- a/runtime/common_dex_operations.h +++ b/runtime/common_dex_operations.h @@ -255,12 +255,12 @@ ALWAYS_INLINE bool DoFieldPutCommon(Thread* self, } if (UNLIKELY(!reg->VerifierInstanceOf(field_class))) { // This should never happen. - std::string temp1, temp2, temp3; + std::string temp1, temp2; self->ThrowNewExceptionF("Ljava/lang/InternalError;", "Put '%s' that is not instance of field '%s' in '%s'", reg->GetClass()->GetDescriptor(&temp1), field_class->GetDescriptor(&temp2), - field->GetDeclaringClass()->GetDescriptor(&temp3)); + field->GetDeclaringClassDescriptor()); return false; } } diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 922d678920..311391dbbf 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -58,6 +58,7 @@ #include "mirror/object_array-alloc-inl.h" #include "native/dalvik_system_DexFile.h" #include "noop_compiler_callbacks.h" +#include "oat/aot_class_linker.h" #include "profile/profile_compilation_info.h" #include "runtime-inl.h" #include "runtime_intrinsics.h" @@ -543,25 +544,6 @@ std::string CommonRuntimeTestImpl::GetSystemImageFile() { return GetImageDirectory() + "/" + isa + "/boot.art"; } -void CommonRuntimeTestImpl::EnterTransactionMode() { - CHECK(!Runtime::Current()->IsActiveTransaction()); - Runtime::Current()->EnterTransactionMode(/*strict=*/ false, /*root=*/ nullptr); -} - -void CommonRuntimeTestImpl::ExitTransactionMode() { - Runtime::Current()->ExitTransactionMode(); - CHECK(!Runtime::Current()->IsActiveTransaction()); -} - -void CommonRuntimeTestImpl::RollbackAndExitTransactionMode() { - Runtime::Current()->RollbackAndExitTransactionMode(); - CHECK(!Runtime::Current()->IsActiveTransaction()); -} - -bool CommonRuntimeTestImpl::IsTransactionAborted() { - return Runtime::Current()->IsTransactionAborted(); -} - void CommonRuntimeTestImpl::VisitDexes(ArrayRef<const std::string> dexes, const std::function<void(MethodReference)>& method_visitor, const std::function<void(TypeReference)>& class_visitor, diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 8f9d7a80ff..87c8d111f0 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -223,11 +223,6 @@ class CommonRuntimeTestImpl : public CommonArtTestImpl { // Returns the directory where the pre-compiled boot.art can be found. static std::string GetImageLocation(); static std::string GetSystemImageFile(); - - static void EnterTransactionMode() REQUIRES_SHARED(Locks::mutator_lock_); - static void ExitTransactionMode(); - static void RollbackAndExitTransactionMode() REQUIRES_SHARED(Locks::mutator_lock_); - static bool IsTransactionAborted(); }; template <typename TestType> diff --git a/runtime/common_throws.h b/runtime/common_throws.h index aeba3bb1bb..71b0968fc0 100644 --- a/runtime/common_throws.h +++ b/runtime/common_throws.h @@ -35,6 +35,9 @@ class DexFile; enum InvokeType : uint32_t; class Signature; +// The descriptor of the transaction abort exception. +constexpr const char kTransactionAbortErrorDescriptor[] = "Ldalvik/system/TransactionAbortError;"; + // AbstractMethodError void ThrowAbstractMethodError(ArtMethod* method) diff --git a/runtime/common_transaction_test.cc b/runtime/common_transaction_test.cc new file mode 100644 index 0000000000..85f176944a --- /dev/null +++ b/runtime/common_transaction_test.cc @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common_transaction_test.h" + +#include "oat/aot_class_linker.h" +#include "runtime.h" + +namespace art HIDDEN { + +void CommonTransactionTestImpl::EnterTransactionMode() { + CHECK(!Runtime::Current()->IsActiveTransaction()); + AotClassLinker* class_linker = down_cast<AotClassLinker*>(Runtime::Current()->GetClassLinker()); + class_linker->EnterTransactionMode(/*strict=*/ false, /*root=*/ nullptr); +} + +void CommonTransactionTestImpl::ExitTransactionMode() { + AotClassLinker* class_linker = down_cast<AotClassLinker*>(Runtime::Current()->GetClassLinker()); + class_linker->ExitTransactionMode(); + CHECK(!Runtime::Current()->IsActiveTransaction()); +} + +void CommonTransactionTestImpl::RollbackAndExitTransactionMode() { + AotClassLinker* class_linker = down_cast<AotClassLinker*>(Runtime::Current()->GetClassLinker()); + class_linker->RollbackAndExitTransactionMode(); + CHECK(!Runtime::Current()->IsActiveTransaction()); +} + +bool CommonTransactionTestImpl::IsTransactionAborted() { + if (!Runtime::Current()->IsActiveTransaction()) { + return false; + } + AotClassLinker* class_linker = down_cast<AotClassLinker*>(Runtime::Current()->GetClassLinker()); + return class_linker->IsTransactionAborted(); +} + +} // namespace art diff --git a/runtime/common_transaction_test.h b/runtime/common_transaction_test.h new file mode 100644 index 0000000000..937662009f --- /dev/null +++ b/runtime/common_transaction_test.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_COMMON_TRANSACTION_TEST_H_ +#define ART_RUNTIME_COMMON_TRANSACTION_TEST_H_ + +#include "common_runtime_test.h" + +namespace art HIDDEN { + +class CommonTransactionTestImpl { + protected: + static void EnterTransactionMode() REQUIRES_SHARED(Locks::mutator_lock_); + static void ExitTransactionMode(); + static void RollbackAndExitTransactionMode() REQUIRES_SHARED(Locks::mutator_lock_); + static bool IsTransactionAborted(); +}; + +template <typename TestType> +class CommonTransactionTestBase : public TestType, public CommonTransactionTestImpl {}; + +using CommonTransactionTest = CommonTransactionTestBase<CommonRuntimeTest>; + +} // namespace art + +#endif // ART_RUNTIME_COMMON_TRANSACTION_TEST_H_ diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index d02701880b..e4b308e846 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -583,7 +583,7 @@ class ConcurrentCopying::FlipCallback : public Closure { if (UNLIKELY(runtime->IsActiveTransaction())) { CHECK(runtime->IsAotCompiler()); TimingLogger::ScopedTiming split3("(Paused)VisitTransactionRoots", cc->GetTimings()); - runtime->VisitTransactionRoots(cc); + runtime->GetClassLinker()->VisitTransactionRoots(cc); } if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects) { cc->GrayAllNewlyDirtyImmuneObjects(); diff --git a/runtime/gc/collector/mark_compact-inl.h b/runtime/gc/collector/mark_compact-inl.h index 13f11300fa..24d47638b1 100644 --- a/runtime/gc/collector/mark_compact-inl.h +++ b/runtime/gc/collector/mark_compact-inl.h @@ -266,9 +266,10 @@ inline bool MarkCompact::VerifyRootSingleUpdate(void* root, mirror::Object* old_ref, const RootInfo& info) { // ASAN promotes stack-frames to heap in order to detect - // stack-use-after-return issues. So skip using this double-root update - // detection on ASAN as well. - if (kIsDebugBuild && !kMemoryToolIsAvailable) { + // stack-use-after-return issues. And HWASAN has pointers tagged, which makes + // it difficult to recognize and prevent stack pointers from being checked. + // So skip using double-root update detection on ASANs. + if (kIsDebugBuild && !kMemoryToolIsAvailable && !kHwAsanEnabled) { void* stack_low_addr = stack_low_addr_; void* stack_high_addr = stack_high_addr_; if (!HasAddress(old_ref)) { @@ -279,12 +280,21 @@ inline bool MarkCompact::VerifyRootSingleUpdate(void* root, stack_low_addr = self->GetStackEnd(); stack_high_addr = reinterpret_cast<char*>(stack_low_addr) + self->GetStackSize(); } - if (root < stack_low_addr || root > stack_high_addr) { - MutexLock mu(self, lock_); - auto ret = updated_roots_->insert(root); - DCHECK(ret.second) << "root=" << root << " old_ref=" << old_ref - << " stack_low_addr=" << stack_low_addr - << " stack_high_addr=" << stack_high_addr; + if (std::less<void*>{}(root, stack_low_addr) || std::greater<void*>{}(root, stack_high_addr)) { + bool inserted; + { + MutexLock mu(self, lock_); + inserted = updated_roots_->insert(root).second; + } + if (!inserted) { + std::ostringstream oss; + heap_->DumpSpaces(oss); + MemMap::DumpMaps(oss, /* terse= */ true); + CHECK(inserted) << "root=" << root << " old_ref=" << old_ref + << " stack_low_addr=" << stack_low_addr + << " stack_high_addr=" << stack_high_addr << " maps\n" + << oss.str(); + } } DCHECK(reinterpret_cast<uint8_t*>(old_ref) >= black_allocations_begin_ || live_words_bitmap_->Test(old_ref)) diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc index 46eb3ad522..5d07ff6081 100644 --- a/runtime/gc/collector/mark_compact.cc +++ b/runtime/gc/collector/mark_compact.cc @@ -84,6 +84,13 @@ #endif // __NR_userfaultfd #endif // __BIONIC__ +// See aosp/2996596 for where these values came from. +#ifndef UFFDIO_COPY_MODE_MMAP_TRYLOCK +#define UFFDIO_COPY_MODE_MMAP_TRYLOCK (static_cast<uint64_t>(1) << 63) +#endif +#ifndef UFFDIO_ZEROPAGE_MODE_MMAP_TRYLOCK +#define UFFDIO_ZEROPAGE_MODE_MMAP_TRYLOCK (static_cast<uint64_t>(1) << 63) +#endif #ifdef ART_TARGET_ANDROID namespace { @@ -110,6 +117,8 @@ static bool HaveMremapDontunmap() { return false; } } + +static bool gUffdSupportsMmapTrylock = false; // We require MREMAP_DONTUNMAP functionality of the mremap syscall, which was // introduced in 5.13 kernel version. But it was backported to GKI kernels. static bool gHaveMremapDontunmap = IsKernelVersionAtLeast(5, 13) || HaveMremapDontunmap(); @@ -142,6 +151,37 @@ bool KernelSupportsUffd() { struct uffdio_api api = {.api = UFFD_API, .features = 0, .ioctls = 0}; CHECK_EQ(ioctl(fd, UFFDIO_API, &api), 0) << "ioctl_userfaultfd : API:" << strerror(errno); gUffdFeatures = api.features; + // MMAP_TRYLOCK is available only in 5.10 and 5.15 GKI kernels. The higher + // versions will have per-vma locks. The lower ones don't support + // userfaultfd. + if (kIsTargetAndroid && !IsKernelVersionAtLeast(5, 16)) { + // Check if MMAP_TRYLOCK feature is supported + const size_t page_size = GetPageSizeSlow(); + void* mem = + mmap(nullptr, page_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + CHECK_NE(mem, MAP_FAILED) << " errno: " << errno; + + struct uffdio_zeropage uffd_zeropage; + uffd_zeropage.mode = UFFDIO_ZEROPAGE_MODE_MMAP_TRYLOCK; + uffd_zeropage.range.start = reinterpret_cast<uintptr_t>(mem); + uffd_zeropage.range.len = page_size; + uffd_zeropage.zeropage = 0; + // The ioctl will definitely fail as mem is not registered with uffd. + CHECK_EQ(ioctl(fd, UFFDIO_ZEROPAGE, &uffd_zeropage), -1); + // uffd ioctls return EINVAL for several reasons. We make sure with + // (proper alignment of 'mem' and 'len') that, before updating + // uffd_zeropage.zeropage (with error), it fails with EINVAL only if + // `trylock` isn't available. + if (uffd_zeropage.zeropage == 0 && errno == EINVAL) { + LOG(INFO) << "MMAP_TRYLOCK is not supported in uffd addr:" << mem + << " page-size:" << page_size; + } else { + gUffdSupportsMmapTrylock = true; + LOG(INFO) << "MMAP_TRYLOCK is supported in uffd errno:" << errno << " addr:" << mem + << " size:" << page_size; + } + munmap(mem, page_size); + } close(fd); // Minimum we need is sigbus feature for using userfaultfd. return (api.features & kUffdFeaturesForSigbus) == kUffdFeaturesForSigbus; @@ -459,13 +499,14 @@ MarkCompact::MarkCompact(Heap* heap) // minor-fault. Eventually, a cleanup of linear-alloc update logic to only // use private anonymous would be ideal. CHECK(!uffd_minor_fault_supported_); + uint8_t* moving_space_begin = bump_pointer_space_->Begin(); // TODO: Depending on how the bump-pointer space move is implemented. If we // switch between two virtual memories each time, then we will have to // initialize live_words_bitmap_ accordingly. live_words_bitmap_.reset(LiveWordsBitmap<kAlignment>::Create( - reinterpret_cast<uintptr_t>(bump_pointer_space_->Begin()), - reinterpret_cast<uintptr_t>(bump_pointer_space_->Limit()))); + reinterpret_cast<uintptr_t>(moving_space_begin), + reinterpret_cast<uintptr_t>(bump_pointer_space_->Limit()))); std::string err_msg; size_t moving_space_size = bump_pointer_space_->Capacity(); @@ -487,7 +528,7 @@ MarkCompact::MarkCompact(Heap* heap) size_t moving_space_alignment = Heap::BestPageTableAlignment(moving_space_size); // The moving space is created at a fixed address, which is expected to be // PMD-size aligned. - if (!IsAlignedParam(bump_pointer_space_->Begin(), moving_space_alignment)) { + if (!IsAlignedParam(moving_space_begin, moving_space_alignment)) { LOG(WARNING) << "Bump pointer space is not aligned to " << PrettySize(moving_space_alignment) << ". This can lead to longer stop-the-world pauses for compaction"; } @@ -545,6 +586,15 @@ MarkCompact::MarkCompact(Heap* heap) // In most of the cases, we don't expect more than one LinearAlloc space. linear_alloc_spaces_data_.reserve(1); + // Ensure that huge-pages are not used on the moving-space, which may happen + // if THP is 'always' enabled and breaks our assumption that a normal-page is + // mapped when any address is accessed. + int ret = madvise(moving_space_begin, moving_space_size, MADV_NOHUGEPAGE); + // Some devices may not have THP configured in the kernel. On such devices + // madvise will fail with EINVAL. Obviously, on such devices this madvise is + // not required in the first place. + CHECK(ret == 0 || errno == EINVAL); + // Initialize GC metrics. metrics::ArtMetrics* metrics = GetMetrics(); // The mark-compact collector supports only full-heap collections at the moment. @@ -1016,6 +1066,7 @@ class MarkCompact::ConcurrentCompactionGcTask : public SelfDeletingTask { }; void MarkCompact::PrepareForCompaction() { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); uint8_t* space_begin = bump_pointer_space_->Begin(); size_t vector_len = (black_allocations_begin_ - space_begin) / kOffsetChunkSize; DCHECK_LE(vector_len, vector_length_); @@ -1954,7 +2005,7 @@ void MarkCompact::MapProcessedPages(uint8_t* to_space_start, Atomic<PageState>* state_arr, size_t arr_idx, size_t arr_len) { - DCHECK(minor_fault_initialized_); + CHECK(minor_fault_initialized_); DCHECK_LT(arr_idx, arr_len); DCHECK_ALIGNED_PARAM(to_space_start, gPageSize); // Claim all the contiguous pages, which are ready to be mapped, and then do @@ -2047,61 +2098,128 @@ void MarkCompact::MapProcessedPages(uint8_t* to_space_start, } } -void MarkCompact::ZeropageIoctl(void* addr, - size_t length, - bool tolerate_eexist, - bool tolerate_enoent) { +template <uint32_t kYieldMax = 5, uint64_t kSleepUs = 10> +static void BackOff(uint32_t i) { + // TODO: Consider adding x86 PAUSE and/or ARM YIELD here. + if (i <= kYieldMax) { + sched_yield(); + } else { + // nanosleep is not in the async-signal-safe list, but bionic implements it + // with a pure system call, so it should be fine. + NanoSleep(kSleepUs * 1000 * (i - kYieldMax)); + } +} + +size_t MarkCompact::ZeropageIoctl(void* addr, + size_t length, + bool tolerate_eexist, + bool tolerate_enoent) { + int32_t backoff_count = -1; + int32_t max_backoff = 10; // max native priority. struct uffdio_zeropage uffd_zeropage; DCHECK(IsAlignedParam(addr, gPageSize)); uffd_zeropage.range.start = reinterpret_cast<uintptr_t>(addr); uffd_zeropage.range.len = length; - uffd_zeropage.mode = 0; - while (length > 0) { + uffd_zeropage.mode = gUffdSupportsMmapTrylock ? UFFDIO_ZEROPAGE_MODE_MMAP_TRYLOCK : 0; + while (true) { + uffd_zeropage.zeropage = 0; int ret = ioctl(uffd_, UFFDIO_ZEROPAGE, &uffd_zeropage); if (ret == 0) { DCHECK_EQ(uffd_zeropage.zeropage, static_cast<ssize_t>(length)); - break; + return length; } else if (errno == EAGAIN) { - // Ioctl aborted due to mmap_lock contention. Adjust the values and try - // again. - DCHECK_GE(uffd_zeropage.zeropage, static_cast<ssize_t>(gPageSize)); - length -= uffd_zeropage.zeropage; - uffd_zeropage.range.len = length; - uffd_zeropage.range.start += uffd_zeropage.zeropage; + if (uffd_zeropage.zeropage > 0) { + // Contention was observed after acquiring mmap_lock. But the first page + // is already done, which is what we care about. + DCHECK(IsAlignedParam(uffd_zeropage.zeropage, gPageSize)); + DCHECK_GE(uffd_zeropage.zeropage, static_cast<ssize_t>(gPageSize)); + return uffd_zeropage.zeropage; + } else if (uffd_zeropage.zeropage < 0) { + // mmap_read_trylock() failed due to contention. Back-off and retry. + DCHECK_EQ(uffd_zeropage.zeropage, -EAGAIN); + if (backoff_count == -1) { + int prio = Thread::Current()->GetNativePriority(); + DCHECK(prio > 0 && prio <= 10) << prio; + max_backoff -= prio; + backoff_count = 0; + } + if (backoff_count < max_backoff) { + // Using 3 to align 'normal' priority threads with sleep. + BackOff</*kYieldMax=*/3, /*kSleepUs=*/1000>(backoff_count++); + } else { + uffd_zeropage.mode = 0; + } + } + } else if (tolerate_eexist && errno == EEXIST) { + // Ioctl returns the number of bytes it mapped. The page on which EEXIST occurred + // wouldn't be included in it. + return uffd_zeropage.zeropage > 0 ? uffd_zeropage.zeropage + gPageSize : gPageSize; } else { - DCHECK_EQ(uffd_zeropage.zeropage, -errno); - CHECK((tolerate_enoent && errno == ENOENT) || (tolerate_eexist && errno == EEXIST)) + CHECK(tolerate_enoent && errno == ENOENT) << "ioctl_userfaultfd: zeropage failed: " << strerror(errno) << ". addr:" << addr; - break; + return 0; } } } -void MarkCompact::CopyIoctl(void* dst, void* buffer, size_t length) { +size_t MarkCompact::CopyIoctl(void* dst, void* buffer, size_t length, bool return_on_contention) { + int32_t backoff_count = -1; + int32_t max_backoff = 10; // max native priority. struct uffdio_copy uffd_copy; + uffd_copy.mode = gUffdSupportsMmapTrylock ? UFFDIO_COPY_MODE_MMAP_TRYLOCK : 0; uffd_copy.src = reinterpret_cast<uintptr_t>(buffer); uffd_copy.dst = reinterpret_cast<uintptr_t>(dst); uffd_copy.len = length; - uffd_copy.mode = 0; - while (length > 0) { + uffd_copy.copy = 0; + while (true) { int ret = ioctl(uffd_, UFFDIO_COPY, &uffd_copy); if (ret == 0) { DCHECK_EQ(uffd_copy.copy, static_cast<ssize_t>(length)); break; } else if (errno == EAGAIN) { - // Ioctl aborted due to mmap_lock contention. Adjust the values and try - // again. - DCHECK_GE(uffd_copy.copy, static_cast<ssize_t>(gPageSize)); - length -= uffd_copy.copy; - uffd_copy.len = length; - uffd_copy.src += uffd_copy.copy; - uffd_copy.dst += uffd_copy.copy; + // Contention observed. + DCHECK_NE(uffd_copy.copy, 0); + if (uffd_copy.copy > 0) { + // Contention was observed after acquiring mmap_lock. + DCHECK(IsAlignedParam(uffd_copy.copy, gPageSize)); + DCHECK_GE(uffd_copy.copy, static_cast<ssize_t>(gPageSize)); + break; + } else { + // mmap_read_trylock() failed due to contention. + DCHECK_EQ(uffd_copy.copy, -EAGAIN); + uffd_copy.copy = 0; + if (return_on_contention) { + break; + } + } + if (backoff_count == -1) { + int prio = Thread::Current()->GetNativePriority(); + DCHECK(prio > 0 && prio <= 10) << prio; + max_backoff -= prio; + backoff_count = 0; + } + if (backoff_count < max_backoff) { + // Using 3 to align 'normal' priority threads with sleep. + BackOff</*kYieldMax=*/3, /*kSleepUs=*/1000>(backoff_count++); + } else { + uffd_copy.mode = 0; + } + } else if (errno == EEXIST) { + DCHECK_NE(uffd_copy.copy, 0); + if (uffd_copy.copy < 0) { + uffd_copy.copy = 0; + } + // Ioctl returns the number of bytes it mapped. The page on which EEXIST occurred + // wouldn't be included in it. + uffd_copy.copy += gPageSize; + break; } else { DCHECK_EQ(uffd_copy.copy, -errno); LOG(FATAL) << "ioctl_userfaultfd: copy failed: " << strerror(errno) << ". src:" << buffer << " dst:" << dst; } } + return uffd_copy.copy; } template <int kMode, typename CompactionFn> @@ -2124,7 +2242,7 @@ bool MarkCompact::DoPageCompactionWithStateChange(size_t page_idx, func(); if (kMode == kCopyMode) { if (map_immediately) { - CopyIoctl(to_space_page, page, gPageSize); + CopyIoctl(to_space_page, page, gPageSize, /*return_on_contention=*/false); // Store is sufficient as no other thread could modify the status at this // point. Relaxed order is sufficient as the ioctl will act as a fence. moving_pages_status_[page_idx].store(static_cast<uint8_t>(PageState::kProcessedAndMapped), @@ -2161,18 +2279,6 @@ bool MarkCompact::DoPageCompactionWithStateChange(size_t page_idx, } } -static void BackOff(uint32_t i) { - static constexpr uint32_t kYieldMax = 5; - // TODO: Consider adding x86 PAUSE and/or ARM YIELD here. - if (i <= kYieldMax) { - sched_yield(); - } else { - // nanosleep is not in the async-signal-safe list, but bionic implements it - // with a pure system call, so it should be fine. - NanoSleep(10000ull * (i - kYieldMax)); - } -} - bool MarkCompact::FreeFromSpacePages(size_t cur_page_idx, int mode, size_t end_idx_for_mapping) { // Thanks to sliding compaction, bump-pointer allocations, and reverse // compaction (see CompactMovingSpace) the logic here is pretty simple: find @@ -2183,8 +2289,7 @@ bool MarkCompact::FreeFromSpacePages(size_t cur_page_idx, int mode, size_t end_i // Find the to-space page up to which the corresponding from-space pages can be // freed. for (; idx > cur_page_idx; idx--) { - PageState state = static_cast<PageState>( - static_cast<uint8_t>(moving_pages_status_[idx - 1].load(std::memory_order_acquire))); + PageState state = GetMovingPageState(idx - 1); if (state == PageState::kMutatorProcessing) { // Some mutator is working on the page. break; @@ -2283,43 +2388,41 @@ bool MarkCompact::FreeFromSpacePages(size_t cur_page_idx, int mode, size_t end_i // lower than the reclaim range. break; } - bool ret = mode == kFallbackMode; + bool all_mapped = mode == kFallbackMode; ssize_t size = last_reclaimed_page_ - reclaim_begin; if (size > kMinFromSpaceMadviseSize) { // Map all the pages in the range. if (mode == kCopyMode && cur_page_idx < end_idx_for_mapping) { - size_t len = MapMovingSpacePages(cur_page_idx, end_idx_for_mapping); - // The pages that were not mapped by gc-thread have to be completed - // before we madvise them. So wait for their status to change to 'mapped'. - // The wait is expected to be short as the read state indicates that - // another thread is actively working on mapping the page. - for (size_t i = cur_page_idx + DivideByPageSize(len); i < end_idx_for_mapping; i++) { - PageState state = static_cast<PageState>( - static_cast<uint8_t>(moving_pages_status_[i].load(std::memory_order_relaxed))); - uint32_t backoff_count = 0; - while (state != PageState::kProcessedAndMapped) { - BackOff(backoff_count++); - state = static_cast<PageState>( - static_cast<uint8_t>(moving_pages_status_[i].load(std::memory_order_relaxed))); - } + if (MapMovingSpacePages(cur_page_idx, + end_idx_for_mapping, + /*from_ioctl=*/false, + /*return_on_contention=*/true) == + end_idx_for_mapping - cur_page_idx) { + all_mapped = true; } - ret = true; + } else { + // This for the black-allocations pages so that madvise is not missed. + all_mapped = true; + } + // If not all pages are mapped, then take it as a hint that mmap_lock is + // contended and hence don't madvise as that also needs the same lock. + if (all_mapped) { + // Retain a few pages for subsequent compactions. + const ssize_t gBufferPages = 4 * gPageSize; + DCHECK_LT(gBufferPages, kMinFromSpaceMadviseSize); + size -= gBufferPages; + uint8_t* addr = last_reclaimed_page_ - size; + int behavior = minor_fault_initialized_ ? MADV_REMOVE : MADV_DONTNEED; + CHECK_EQ(madvise(addr + from_space_slide_diff_, size, behavior), 0) + << "madvise of from-space failed: " << strerror(errno); + last_reclaimed_page_ = addr; + cur_reclaimable_page_ = addr; } - // Retain a few pages for subsequent compactions. - const ssize_t gBufferPages = 4 * gPageSize; - DCHECK_LT(gBufferPages, kMinFromSpaceMadviseSize); - size -= gBufferPages; - uint8_t* addr = last_reclaimed_page_ - size; - int behavior = minor_fault_initialized_ ? MADV_REMOVE : MADV_DONTNEED; - CHECK_EQ(madvise(addr + from_space_slide_diff_, size, behavior), 0) - << "madvise of from-space failed: " << strerror(errno); - last_reclaimed_page_ = addr; - cur_reclaimable_page_ = addr; } CHECK_LE(reclaim_begin, last_reclaimable_page_); last_reclaimable_page_ = reclaim_begin; last_checked_reclaim_page_idx_ = idx; - return ret; + return all_mapped; } void MarkCompact::UpdateClassAfterObjMap() { @@ -2442,9 +2545,10 @@ void MarkCompact::CompactMovingSpace(uint8_t* page) { CompactPage(first_obj, pre_compact_offset_moving_space_[idx], page, kMode == kCopyMode); }); if (kMode == kCopyMode && (!success || page == reserve_page) && end_idx_for_mapping - idx > 1) { - // map the pages in the following pages as they can't be mapped with - // the subsequent pages as their src-side pages won't be contiguous. - MapMovingSpacePages(idx + 1, end_idx_for_mapping); + // map the pages in the following address as they can't be mapped with the + // pages yet-to-be-compacted as their src-side pages won't be contiguous. + MapMovingSpacePages( + idx + 1, end_idx_for_mapping, /*from_fault=*/false, /*return_on_contention=*/true); } if (FreeFromSpacePages(idx, kMode, end_idx_for_mapping)) { end_idx_for_mapping = idx; @@ -2452,49 +2556,80 @@ void MarkCompact::CompactMovingSpace(uint8_t* page) { } // map one last time to finish anything left. if (kMode == kCopyMode && end_idx_for_mapping > 0) { - MapMovingSpacePages(idx, end_idx_for_mapping); + MapMovingSpacePages( + idx, end_idx_for_mapping, /*from_fault=*/false, /*return_on_contention=*/false); } DCHECK_EQ(to_space_end, bump_pointer_space_->Begin()); } -size_t MarkCompact::MapMovingSpacePages(size_t arr_idx, size_t arr_len) { - // Claim all the contiguous pages, which are ready to be mapped, and then do - // so in a single ioctl. This helps avoid the overhead of invoking syscall - // several times and also maps the already-processed pages, avoiding - // unnecessary faults on them. - DCHECK_LT(arr_idx, arr_len); - uint32_t cur_state = moving_pages_status_[arr_idx].load(std::memory_order_relaxed); - if ((cur_state & kPageStateMask) != static_cast<uint8_t>(PageState::kProcessed)) { - return 0; - } - uint32_t from_space_offset = cur_state & ~kPageStateMask; - uint8_t* to_space_start = moving_space_begin_ + arr_idx * gPageSize; - uint8_t* from_space_start = from_space_begin_ + from_space_offset; - DCHECK_ALIGNED_PARAM(to_space_start, gPageSize); - DCHECK_ALIGNED_PARAM(from_space_start, gPageSize); - size_t length = 0; - for (size_t i = arr_idx; i < arr_len; length += gPageSize, from_space_offset += gPageSize, i++) { - uint8_t desired_state = static_cast<uint8_t>(PageState::kProcessedAndMapping); - cur_state = moving_pages_status_[i].load(std::memory_order_relaxed); - // We need to guarantee that we don't end up sucsessfully marking a later - // page 'mapping' and then fail to mark an earlier page. To guarantee that - // we use acq_rel order. - if ((cur_state & kPageStateMask) != static_cast<uint8_t>(PageState::kProcessed) || - !moving_pages_status_[i].compare_exchange_strong( - cur_state, desired_state, std::memory_order_acq_rel)) { - break; +size_t MarkCompact::MapMovingSpacePages(size_t start_idx, + size_t arr_len, + bool from_fault, + bool return_on_contention) { + DCHECK_LT(start_idx, arr_len); + size_t arr_idx = start_idx; + bool wait_for_unmapped = false; + while (arr_idx < arr_len) { + size_t map_count = 0; + uint32_t cur_state = moving_pages_status_[arr_idx].load(std::memory_order_acquire); + // Find a contiguous range that can be mapped with single ioctl. + for (size_t i = arr_idx; i < arr_len; i++, map_count++) { + uint32_t s = moving_pages_status_[i].load(std::memory_order_acquire); + if (GetPageStateFromWord(s) != PageState::kProcessed) { + break; + } + DCHECK_EQ((cur_state & ~kPageStateMask) + (i - arr_idx) * gPageSize, s & ~kPageStateMask); + } + + if (map_count == 0) { + if (from_fault) { + bool mapped = GetPageStateFromWord(cur_state) == PageState::kProcessedAndMapped; + return mapped ? 1 : 0; + } + // Skip the pages that this thread cannot map. + for (; arr_idx < arr_len; arr_idx++) { + PageState s = GetMovingPageState(arr_idx); + if (s == PageState::kProcessed) { + break; + } else if (s != PageState::kProcessedAndMapped) { + wait_for_unmapped = true; + } + } + } else { + uint32_t from_space_offset = cur_state & ~kPageStateMask; + uint8_t* to_space_start = moving_space_begin_ + arr_idx * gPageSize; + uint8_t* from_space_start = from_space_begin_ + from_space_offset; + DCHECK_ALIGNED_PARAM(to_space_start, gPageSize); + DCHECK_ALIGNED_PARAM(from_space_start, gPageSize); + size_t mapped_len = + CopyIoctl(to_space_start, from_space_start, map_count * gPageSize, return_on_contention); + for (size_t l = 0; l < mapped_len; l += gPageSize, arr_idx++) { + // Store is sufficient as anyone storing is doing it with the same value. + moving_pages_status_[arr_idx].store(static_cast<uint8_t>(PageState::kProcessedAndMapped), + std::memory_order_release); + } + if (from_fault) { + return DivideByPageSize(mapped_len); + } + // We can return from COPY ioctl with a smaller length also if a page + // was found to be already mapped. But that doesn't count as contention. + if (return_on_contention && DivideByPageSize(mapped_len) < map_count && errno != EEXIST) { + return arr_idx - start_idx; + } } - DCHECK_EQ(from_space_offset, cur_state & ~kPageStateMask); } - if (length > 0) { - CopyIoctl(to_space_start, from_space_start, length); - for (size_t i = arr_idx; length > 0; length -= gPageSize, i++) { - // Store is sufficient as there are no other threads updating status of these pages. - moving_pages_status_[i].store(static_cast<uint8_t>(PageState::kProcessedAndMapped), - std::memory_order_release); + if (wait_for_unmapped) { + for (size_t i = start_idx; i < arr_len; i++) { + PageState s = GetMovingPageState(i); + DCHECK_GT(s, PageState::kProcessed); + uint32_t backoff_count = 0; + while (s != PageState::kProcessedAndMapped) { + BackOff(backoff_count++); + s = GetMovingPageState(i); + } } } - return length; + return arr_len - start_idx; } void MarkCompact::UpdateNonMovingPage(mirror::Object* first, uint8_t* page) { @@ -3219,9 +3354,9 @@ void MarkCompact::KernelPreparation() { uint8_t* moving_space_begin = bump_pointer_space_->Begin(); size_t moving_space_size = bump_pointer_space_->Capacity(); int mode = kCopyMode; - size_t moving_space_register_sz; + size_t moving_space_register_sz = (moving_first_objs_count_ + black_page_count_) * gPageSize; + DCHECK_LE(moving_space_register_sz, moving_space_size); if (minor_fault_initialized_) { - moving_space_register_sz = (moving_first_objs_count_ + black_page_count_) * gPageSize; if (shadow_to_space_map_.IsValid()) { size_t shadow_size = shadow_to_space_map_.Size(); void* addr = shadow_to_space_map_.Begin(); @@ -3249,8 +3384,6 @@ void MarkCompact::KernelPreparation() { << "mprotect failed: " << strerror(errno); } } - } else { - moving_space_register_sz = moving_space_size; } bool map_shared = @@ -3270,8 +3403,25 @@ void MarkCompact::KernelPreparation() { shadow_addr); if (IsValidFd(uffd_)) { - // Register the moving space with userfaultfd. - RegisterUffd(moving_space_begin, moving_space_register_sz, mode); + if (moving_space_register_sz > 0) { + // mremap clears 'anon_vma' field of anonymous mappings. If we + // uffd-register only the used portion of the space, then the vma gets + // split (between used and unused portions) and as soon as pages are + // mapped to the vmas, they get different `anon_vma` assigned, which + // ensures that the two vmas cannot merge after we uffd-unregister the + // used portion. OTOH, registering the entire space avoids the split, but + // unnecessarily causes userfaults on allocations. + // By faulting-in a page we force the kernel to allocate 'anon_vma' *before* + // the vma-split in uffd-register. This ensures that when we unregister + // the used portion after compaction, the two split vmas merge. This is + // necessary for the mremap of the next GC cycle to not fail due to having + // more than one vma in the source range. + if (moving_space_register_sz < moving_space_size) { + *const_cast<volatile uint8_t*>(moving_space_begin + moving_space_register_sz) = 0; + } + // Register the moving space with userfaultfd. + RegisterUffd(moving_space_begin, moving_space_register_sz, mode); + } // Prepare linear-alloc for concurrent compaction. for (auto& data : linear_alloc_spaces_data_) { bool mmap_again = map_shared && !data.already_shared_; @@ -3484,19 +3634,15 @@ void MarkCompact::ConcurrentlyProcessMovingPage(uint8_t* fault_page, size_t end_idx = page_idx + DivideByPageSize(end - fault_page); size_t length = 0; for (size_t idx = page_idx; idx < end_idx; idx++, length += gPageSize) { - // We should never have a case where two workers are trying to install a - // zeropage in this range as we synchronize using moving_pages_status_[page_idx]. - uint32_t expected_state = static_cast<uint8_t>(PageState::kUnprocessed); - if (!moving_pages_status_[idx].compare_exchange_strong( - expected_state, - static_cast<uint8_t>(PageState::kProcessedAndMapping), - std::memory_order_acq_rel)) { - DCHECK_GE(expected_state, static_cast<uint8_t>(PageState::kProcessedAndMapping)); + uint32_t cur_state = moving_pages_status_[idx].load(std::memory_order_acquire); + if (cur_state != static_cast<uint8_t>(PageState::kUnprocessed)) { + DCHECK_EQ(cur_state, static_cast<uint8_t>(PageState::kProcessedAndMapped)); break; } } if (length > 0) { - ZeropageIoctl(fault_page, length, /*tolerate_eexist=*/false, /*tolerate_enoent=*/true); + length = + ZeropageIoctl(fault_page, length, /*tolerate_eexist=*/true, /*tolerate_enoent=*/true); for (size_t len = 0, idx = page_idx; len < length; idx++, len += gPageSize) { moving_pages_status_[idx].store(static_cast<uint8_t>(PageState::kProcessedAndMapped), std::memory_order_release); @@ -3510,7 +3656,7 @@ void MarkCompact::ConcurrentlyProcessMovingPage(uint8_t* fault_page, uint32_t backoff_count = 0; PageState state; while (true) { - state = static_cast<PageState>(static_cast<uint8_t>(raw_state)); + state = GetPageStateFromWord(raw_state); if (state == PageState::kProcessing || state == PageState::kMutatorProcessing || state == PageState::kProcessingAndMapping || state == PageState::kProcessedAndMapping) { if (!use_uffd_sigbus_) { @@ -3582,7 +3728,7 @@ void MarkCompact::ConcurrentlyProcessMovingPage(uint8_t* fault_page, moving_pages_status_[page_idx].store(static_cast<uint8_t>(PageState::kProcessedAndMapping), std::memory_order_release); if (kMode == kCopyMode) { - CopyIoctl(fault_page, buf, gPageSize); + CopyIoctl(fault_page, buf, gPageSize, /*return_on_contention=*/false); // Store is sufficient as no other thread modifies the status at this stage. moving_pages_status_[page_idx].store(static_cast<uint8_t>(PageState::kProcessedAndMapped), std::memory_order_release); @@ -3592,13 +3738,16 @@ void MarkCompact::ConcurrentlyProcessMovingPage(uint8_t* fault_page, UNREACHABLE(); } } - state = static_cast<PageState>(static_cast<uint8_t>(raw_state)); + state = GetPageStateFromWord(raw_state); if (state == PageState::kProcessed) { size_t arr_len = moving_first_objs_count_ + black_page_count_; // The page is processed but not mapped. We should map it. The release // order used in MapMovingSpacePages will ensure that the increment to // moving_compaction_in_progress is done first. - if (MapMovingSpacePages(page_idx, arr_len) >= gPageSize) { + if (MapMovingSpacePages(page_idx, + arr_len, + /*from_fault=*/true, + /*return_on_contention=*/false) > 0) { break; } raw_state = moving_pages_status_[page_idx].load(std::memory_order_acquire); @@ -3622,19 +3771,16 @@ bool MarkCompact::MapUpdatedLinearAllocPages(uint8_t* start_page, uint8_t* end_page = start_page + length; while (start_page < end_page) { size_t map_len = 0; - // Claim a contiguous range of pages that we can map. - for (Atomic<PageState>* cur_state = state; map_len < length; + // Find a contiguous range of pages that we can map in single ioctl. + for (Atomic<PageState>* cur_state = state; + map_len < length && cur_state->load(std::memory_order_acquire) == PageState::kProcessed; map_len += gPageSize, cur_state++) { - PageState expected_state = PageState::kProcessed; - if (!cur_state->compare_exchange_strong( - expected_state, PageState::kProcessedAndMapping, std::memory_order_acq_rel)) { - break; - } + // No body. } + if (map_len == 0) { if (single_ioctl) { - // Didn't map anything. - return false; + return state->load(std::memory_order_relaxed) == PageState::kProcessedAndMapped; } // Skip all the pages that this thread can't map. while (length > 0) { @@ -3652,12 +3798,18 @@ bool MarkCompact::MapUpdatedLinearAllocPages(uint8_t* start_page, start_page += gPageSize; } } else { - CopyIoctl(start_page, start_shadow_page, map_len); + map_len = CopyIoctl(start_page, + start_shadow_page, + map_len, + /*return_on_contention=*/false); + DCHECK_NE(map_len, 0u); if (use_uffd_sigbus_) { // Declare that the pages are ready to be accessed. Store is sufficient - // as no other thread can modify the status of this page at this point. + // as any thread will be storing the same value. for (size_t l = 0; l < map_len; l += gPageSize, state++) { - DCHECK_EQ(state->load(std::memory_order_relaxed), PageState::kProcessedAndMapping); + PageState s = state->load(std::memory_order_relaxed); + DCHECK(s == PageState::kProcessed || s == PageState::kProcessedAndMapped) + << "state:" << s; state->store(PageState::kProcessedAndMapped, std::memory_order_release); } } else { @@ -4027,34 +4179,51 @@ void MarkCompact::CompactionPhase() { RecordFree(ObjectBytePair(freed_objects_, freed_bytes)); } - size_t moving_space_size = bump_pointer_space_->Capacity(); - size_t used_size = (moving_first_objs_count_ + black_page_count_) * gPageSize; if (CanCompactMovingSpaceWithMinorFault()) { CompactMovingSpace<kMinorFaultMode>(/*page=*/nullptr); } else { - if (used_size < moving_space_size) { - // mremap clears 'anon_vma' field of anonymous mappings. If we - // uffd-register only the used portion of the space, then the vma gets - // split (between used and unused portions) and as soon as pages are - // mapped to the vmas, they get different `anon_vma` assigned, which - // ensures that the two vmas cannot merged after we uffd-unregister the - // used portion. OTOH, registering the entire space avoids the split, but - // unnecessarily causes userfaults on allocations. - // By mapping a zero-page (below) we let the kernel assign an 'anon_vma' - // *before* the vma-split caused by uffd-unregister of the unused portion - // This ensures that when we unregister the used portion after compaction, - // the two split vmas merge. This is necessary for the mremap of the - // next GC cycle to not fail due to having more than one vmas in the source - // range. - uint8_t* unused_first_page = bump_pointer_space_->Begin() + used_size; - // It's ok if somebody else already mapped the page. - ZeropageIoctl( - unused_first_page, gPageSize, /*tolerate_eexist*/ true, /*tolerate_enoent*/ false); - UnregisterUffd(unused_first_page, moving_space_size - used_size); - } CompactMovingSpace<kCopyMode>(compaction_buffers_map_.Begin()); } + ProcessLinearAlloc(); + + if (use_uffd_sigbus_) { + // Set compaction-done bit so that no new mutator threads start compaction + // process in the SIGBUS handler. + SigbusCounterType count = sigbus_in_progress_count_.fetch_or(kSigbusCounterCompactionDoneMask, + std::memory_order_acq_rel); + // Wait for SIGBUS handlers already in play. + for (uint32_t i = 0; count > 0; i++) { + BackOff(i); + count = sigbus_in_progress_count_.load(std::memory_order_acquire); + count &= ~kSigbusCounterCompactionDoneMask; + } + } else { + DCHECK(IsAlignedParam(conc_compaction_termination_page_, gPageSize)); + // We will only iterate once if gKernelHasFaultRetry is true. + do { + // madvise the page so that we can get userfaults on it. + ZeroAndReleaseMemory(conc_compaction_termination_page_, gPageSize); + // The following load triggers 'special' userfaults. When received by the + // thread-pool workers, they will exit out of the compaction task. This fault + // happens because we madvised the page. + ForceRead(conc_compaction_termination_page_); + } while (thread_pool_counter_ > 0); + } + // Unregister linear-alloc spaces + for (auto& data : linear_alloc_spaces_data_) { + DCHECK_EQ(data.end_ - data.begin_, static_cast<ssize_t>(data.shadow_.Size())); + UnregisterUffd(data.begin_, data.shadow_.Size()); + // madvise linear-allocs's page-status array. Note that we don't need to + // madvise the shado-map as the pages from it were reclaimed in + // ProcessLinearAlloc() after arenas were mapped. + data.page_status_map_.MadviseDontNeedAndZero(); + if (minor_fault_initialized_) { + DCHECK_EQ(mprotect(data.shadow_.Begin(), data.shadow_.Size(), PROT_NONE), 0) + << "mprotect failed: " << strerror(errno); + } + } + // Make sure no mutator is reading from the from-space before unregistering // userfaultfd from moving-space and then zapping from-space. The mutator // and GC may race to set a page state to processing or further along. The two @@ -4065,6 +4234,8 @@ void MarkCompact::CompactionPhase() { for (uint32_t i = 0; compaction_in_progress_count_.load(std::memory_order_acquire) > 0; i++) { BackOff(i); } + size_t moving_space_size = bump_pointer_space_->Capacity(); + size_t used_size = (moving_first_objs_count_ + black_page_count_) * gPageSize; if (used_size > 0) { UnregisterUffd(bump_pointer_space_->Begin(), used_size); } @@ -4106,48 +4277,6 @@ void MarkCompact::CompactionPhase() { << "mprotect(PROT_NONE) for from-space failed: " << strerror(errno); } - ProcessLinearAlloc(); - - if (use_uffd_sigbus_) { - // Set compaction-done bit so that no new mutator threads start compaction - // process in the SIGBUS handler. - SigbusCounterType count = sigbus_in_progress_count_.fetch_or(kSigbusCounterCompactionDoneMask, - std::memory_order_acq_rel); - // Wait for SIGBUS handlers already in play. - for (uint32_t i = 0; count > 0; i++) { - BackOff(i); - count = sigbus_in_progress_count_.load(std::memory_order_acquire); - count &= ~kSigbusCounterCompactionDoneMask; - } - } else { - DCHECK(IsAlignedParam(conc_compaction_termination_page_, gPageSize)); - // We will only iterate once if gKernelHasFaultRetry is true. - do { - // madvise the page so that we can get userfaults on it. - ZeroAndReleaseMemory(conc_compaction_termination_page_, gPageSize); - // The following load triggers 'special' userfaults. When received by the - // thread-pool workers, they will exit out of the compaction task. This fault - // happens because we madvised the page. - ForceRead(conc_compaction_termination_page_); - } while (thread_pool_counter_ > 0); - } - // Unregister linear-alloc spaces - for (auto& data : linear_alloc_spaces_data_) { - DCHECK_EQ(data.end_ - data.begin_, static_cast<ssize_t>(data.shadow_.Size())); - UnregisterUffd(data.begin_, data.shadow_.Size()); - // madvise linear-allocs's page-status array - data.page_status_map_.MadviseDontNeedAndZero(); - // Madvise the entire linear-alloc space's shadow. In copy-mode it gets rid - // of the pages which are still mapped. In minor-fault mode this unmaps all - // pages, which is good in reducing the mremap (done in STW pause) time in - // next GC cycle. - data.shadow_.MadviseDontNeedAndZero(); - if (minor_fault_initialized_) { - DCHECK_EQ(mprotect(data.shadow_.Begin(), data.shadow_.Size(), PROT_NONE), 0) - << "mprotect failed: " << strerror(errno); - } - } - if (!use_uffd_sigbus_) { heap_->GetThreadPool()->StopWorkers(thread_running_gc_); } diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h index 47b2e81e65..dbadfd05d7 100644 --- a/runtime/gc/collector/mark_compact.h +++ b/runtime/gc/collector/mark_compact.h @@ -541,14 +541,27 @@ class MarkCompact final : public GarbageCollector { size_t arr_idx, size_t arr_len) REQUIRES_SHARED(Locks::mutator_lock_); - // Maps moving space pages in [arr_idx, arr_len) range. It fetches the page + // Maps moving space pages in [start_idx, arr_len) range. It fetches the page // address containing the compacted content from moving_pages_status_ array. - // Returns number of bytes (should be multiple of page-size) that are mapped - // by the thread. - size_t MapMovingSpacePages(size_t arr_idx, size_t arr_len) REQUIRES_SHARED(Locks::mutator_lock_); + // 'from_fault' is true when called from userfault (sigbus handler). + // 'return_on_contention' is set to true by gc-thread while it is compacting + // pages. In the end it calls the function with `return_on_contention=false` + // to ensure all pages are mapped. Returns number of pages that are mapped. + size_t MapMovingSpacePages(size_t start_idx, + size_t arr_len, + bool from_fault, + bool return_on_contention) REQUIRES_SHARED(Locks::mutator_lock_); bool IsValidFd(int fd) const { return fd >= 0; } + PageState GetPageStateFromWord(uint32_t page_word) { + return static_cast<PageState>(static_cast<uint8_t>(page_word)); + } + + PageState GetMovingPageState(size_t idx) { + return GetPageStateFromWord(moving_pages_status_[idx].load(std::memory_order_acquire)); + } + // Add/update <class, obj> pair if class > obj and obj is the lowest address // object of class. ALWAYS_INLINE void UpdateClassAfterObjectMap(mirror::Object* obj) @@ -563,13 +576,24 @@ class MarkCompact final : public GarbageCollector { void MarkZygoteLargeObjects() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_); - void ZeropageIoctl(void* addr, size_t length, bool tolerate_eexist, bool tolerate_enoent); - void CopyIoctl(void* dst, void* buffer, size_t length); + // Map zero-pages in the given range. 'tolerate_eexist' and 'tolerate_enoent' + // help us decide if we should expect EEXIST or ENOENT back from the ioctl + // respectively. It may return after mapping fewer pages than requested. + // found to be contended, then we delay the operations based on thread's + // Returns number of bytes (multiple of page-size) now known to be mapped. + size_t ZeropageIoctl(void* addr, size_t length, bool tolerate_eexist, bool tolerate_enoent); + // Map 'buffer' to 'dst', both being 'length' bytes using at most one ioctl + // call. 'return_on_contention' indicates that the function should return + // as soon as mmap_lock contention is detected. Like ZeropageIoctl(), this + // function also uses thread's priority to decide how long we delay before + // forcing the ioctl operation. If ioctl returns EEXIST, then also function + // returns. Returns number of bytes (multiple of page-size) mapped. + size_t CopyIoctl(void* dst, void* buffer, size_t length, bool return_on_contention); // Called after updating linear-alloc page(s) to map the page. It first // updates the state of the pages to kProcessedAndMapping and after ioctl to - // kProcessedAndMapped. Returns true if at least one ioctl invocation was - // done. If 'free_pages' is true then also frees shadow pages. If 'single_ioctl' + // kProcessedAndMapped. Returns true if at least the first page is now mapped. + // If 'free_pages' is true then also frees shadow pages. If 'single_ioctl' // is true, then stops after first ioctl. bool MapUpdatedLinearAllocPages(uint8_t* start_page, uint8_t* start_shadow_page, diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 71771979d4..a8c6886e1f 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -2780,7 +2780,7 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, // We should not ever become runnable and re-suspend while executing a GC. // This would likely cause a deadlock if we acted on a suspension request. // TODO: We really want to assert that we don't transition to kRunnable. - ScopedAssertNoThreadSuspension("Performing GC"); + ScopedAssertNoThreadSuspension scoped_assert("Performing GC"); if (self->IsHandlingStackOverflow()) { // If we are throwing a stack overflow error we probably don't have enough remaining stack // space to run the GC. diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 9f24b310c3..f8f1ac0165 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -43,7 +43,6 @@ #include "base/memfd.h" #include "base/os.h" #include "base/pointer_size.h" -#include "base/scoped_flock.h" #include "base/stl_util.h" #include "base/string_view_cpp20.h" #include "base/systrace.h" @@ -2812,8 +2811,6 @@ class ImageSpace::BootImageLoader { /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) { if (art_fd.get() != -1) { - // No need to lock memfd for which we hold the only file descriptor - // (see locking with ScopedFlock for normal files below). VLOG(startup) << "Using image file " << image_filename.c_str() << " for image location " << image_location << " for compiled extension"; @@ -2831,15 +2828,6 @@ class ImageSpace::BootImageLoader { return result; } - // Note that we must not use the file descriptor associated with - // ScopedFlock::GetFile to Init the image file. We want the file - // descriptor (and the associated exclusive lock) to be released when - // we leave Create. - ScopedFlock image = LockedFile::Open(image_filename.c_str(), - /*flags=*/ O_RDONLY, - /*block=*/ true, - error_msg); - VLOG(startup) << "Using image file " << image_filename.c_str() << " for image location " << image_location; diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index c2884d65fc..cb1c60d93f 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -251,17 +251,18 @@ enum AccessContextFlags { }; MemberSignature::MemberSignature(ArtField* field) { + // Note: `ArtField::GetDeclaringClassDescriptor()` does not support proxy classes. class_name_ = field->GetDeclaringClass()->GetDescriptor(&tmp_); - member_name_ = field->GetName(); - type_signature_ = field->GetTypeDescriptor(); + member_name_ = field->GetNameView(); + type_signature_ = field->GetTypeDescriptorView(); type_ = kField; } MemberSignature::MemberSignature(ArtMethod* method) { DCHECK(method == method->GetInterfaceMethodIfProxy(kRuntimePointerSize)) << "Caller should have replaced proxy method with interface method"; - class_name_ = method->GetDeclaringClass()->GetDescriptor(&tmp_); - member_name_ = method->GetName(); + class_name_ = method->GetDeclaringClassDescriptorView(); + member_name_ = method->GetNameView(); type_signature_ = method->GetSignature().ToString(); type_ = kMethod; } diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc index 7dc3d901df..96a5822973 100644 --- a/runtime/hidden_api_test.cc +++ b/runtime/hidden_api_test.cc @@ -42,20 +42,26 @@ static constexpr uint64_t kHideMaxtargetsdkQHiddenApis = 149994052; static constexpr uint64_t kAllowTestApiAccess = 166236554; -static bool Copy(const std::string& src, const std::string& dst, /*out*/ std::string* error_msg) { - std::ifstream src_stream(src, std::ios::binary); - std::ofstream dst_stream(dst, std::ios::binary); - dst_stream << src_stream.rdbuf(); - src_stream.close(); - dst_stream.close(); - if (src_stream.good() && dst_stream.good()) { - return true; - } else { - *error_msg = "Copy " + src + " => " + dst + " (src_good=" - + (src_stream.good() ? "true" : "false") + ", dst_good=" - + (dst_stream.good() ? "true" : "false") + ")"; +static bool Copy(const char* src_filename, File* dst, /*out*/ std::string* error_msg) { + std::unique_ptr<File> src(OS::OpenFileForReading(src_filename)); + if (src == nullptr) { + *error_msg = StringPrintf("Failed to open for reading: %s", src_filename); + return false; + } + int64_t length = src->GetLength(); + if (length < 0) { + *error_msg = "Failed to get file length."; + return false; + } + if (!dst->Copy(src.get(), /*offset=*/ 0, length)) { + *error_msg = "Failed to copy file contents."; + return false; + } + if (dst->Flush() != 0) { + *error_msg = "Failed to flush."; return false; } + return true; } static bool LoadDexFiles(const std::string& path, @@ -91,11 +97,11 @@ static bool LoadDexFiles(const std::string& path, return true; } -static bool Remove(const std::string& path, /*out*/ std::string* error_msg) { - if (TEMP_FAILURE_RETRY(remove(path.c_str())) == 0) { +static bool Remove(const char* path, /*out*/ std::string* error_msg) { + if (TEMP_FAILURE_RETRY(remove(path)) == 0) { return true; } - *error_msg = StringPrintf("Unable to remove(\"%s\"): %s", path.c_str(), strerror(errno)); + *error_msg = StringPrintf("Unable to remove(\"%s\"): %s", path, strerror(errno)); return false; } @@ -191,13 +197,43 @@ class HiddenApiTest : public CommonRuntimeTest { } void TestLocation(const std::string& location, hiddenapi::Domain expected_domain) { + // Create a temp file with a unique name based on `location` to isolate tests + // that may run in parallel. b/238730923 + const std::string_view suffix = ".jar"; + const std::string_view placeholder = "XXXXXX"; // See `mkstemps()`. + ASSERT_TRUE(EndsWith(location, suffix)); + std::unique_ptr<char[]> unique_location(new char[location.length() + placeholder.length() + 1]); + ASSERT_TRUE(unique_location != nullptr); + size_t stem_length = location.length() - suffix.length(); + memcpy(unique_location.get(), location.data(), stem_length); + memcpy(unique_location.get() + stem_length, placeholder.data(), placeholder.length()); + memcpy(unique_location.get() + stem_length + placeholder.length(), + location.data() + stem_length, + suffix.length()); + unique_location[location.length() + placeholder.length()] = 0; + int fd = mkstemps(unique_location.get(), suffix.length()); + ASSERT_TRUE(fd != -1) << strerror(errno); + + // Copy "Main" to the temp file. + std::string error_msg; + { + File file(fd, /*check_usage=*/ true); + bool copied = Copy(GetTestDexFileName("Main").c_str(), &file, &error_msg); + int flush_close_result = file.FlushCloseOrErase(); + if (!copied && flush_close_result == 0) { + // Silently remove the temp file before reporting the `Copy()` failure. + std::string ignored_error_msg; + Remove(unique_location.get(), &ignored_error_msg); + } + ASSERT_TRUE(copied) << error_msg; + ASSERT_EQ(flush_close_result, 0); + } + ScopedObjectAccess soa(Thread::Current()); std::vector<std::unique_ptr<const DexFile>> dex_files; - std::string error_msg; ObjPtr<mirror::ClassLoader> class_loader; - - ASSERT_TRUE(Copy(GetTestDexFileName("Main"), location, &error_msg)) << error_msg; - ASSERT_TRUE(LoadDexFiles(location, soa.Self(), &dex_files, &class_loader, &error_msg)) + ASSERT_TRUE( + LoadDexFiles(unique_location.get(), soa.Self(), &dex_files, &class_loader, &error_msg)) << error_msg; ASSERT_GE(dex_files.size(), 1u); ASSERT_TRUE(CheckAllDexFilesInDomain(class_loader, @@ -206,7 +242,7 @@ class HiddenApiTest : public CommonRuntimeTest { &error_msg)) << error_msg; dex_files.clear(); - ASSERT_TRUE(Remove(location, &error_msg)) << error_msg; + ASSERT_TRUE(Remove(unique_location.get(), &error_msg)) << error_msg; } protected: diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index 670fd589a2..1986b90a12 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -18,6 +18,7 @@ #include <memory> +#include "class_linker.h" #include "dex/utf.h" #include "gc/collector/garbage_collector.h" #include "gc/space/image_space.h" @@ -149,7 +150,7 @@ void InternTable::AddNewTable() { ObjPtr<mirror::String> InternTable::InsertStrong(ObjPtr<mirror::String> s, uint32_t hash) { Runtime* runtime = Runtime::Current(); if (runtime->IsActiveTransaction()) { - runtime->RecordStrongStringInsertion(s); + runtime->GetClassLinker()->RecordStrongStringInsertion(s); } if (log_new_roots_) { new_strong_intern_roots_.push_back(GcRoot<mirror::String>(s)); @@ -161,7 +162,7 @@ ObjPtr<mirror::String> InternTable::InsertStrong(ObjPtr<mirror::String> s, uint3 ObjPtr<mirror::String> InternTable::InsertWeak(ObjPtr<mirror::String> s, uint32_t hash) { Runtime* runtime = Runtime::Current(); if (runtime->IsActiveTransaction()) { - runtime->RecordWeakStringInsertion(s); + runtime->GetClassLinker()->RecordWeakStringInsertion(s); } weak_interns_.Insert(s, hash); return s; @@ -174,7 +175,7 @@ void InternTable::RemoveStrong(ObjPtr<mirror::String> s, uint32_t hash) { void InternTable::RemoveWeak(ObjPtr<mirror::String> s, uint32_t hash) { Runtime* runtime = Runtime::Current(); if (runtime->IsActiveTransaction()) { - runtime->RecordWeakStringRemoval(s); + runtime->GetClassLinker()->RecordWeakStringRemoval(s); } weak_interns_.Remove(s, hash); } diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 6097077fe2..fb6f36d5e7 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -309,7 +309,6 @@ static inline JValue Execute( shadow_frame, ret, instrumentation, - accessor.InsSize(), /* unlock_monitors= */ false); return ret; } @@ -324,7 +323,6 @@ static inline JValue Execute( shadow_frame, ret, instrumentation, - accessor.InsSize(), /* unlock_monitors= */ false); } return ret; diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 737e80b8ca..4683cd50ee 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -20,6 +20,7 @@ #include "base/casts.h" #include "base/pointer_size.h" +#include "class_linker.h" #include "class_root-inl.h" #include "debugger.h" #include "dex/dex_file_types.h" @@ -46,7 +47,6 @@ #include "shadow_frame-inl.h" #include "stack.h" #include "thread-inl.h" -#include "transaction.h" #include "var_handles.h" #include "well_known_classes.h" @@ -210,22 +210,6 @@ void UnexpectedOpcode(const Instruction* inst, const ShadowFrame& shadow_frame) UNREACHABLE(); } -void AbortTransactionF(Thread* self, const char* fmt, ...) { - va_list args; - va_start(args, fmt); - AbortTransactionV(self, fmt, args); - va_end(args); -} - -void AbortTransactionV(Thread* self, const char* fmt, va_list args) { - CHECK(Runtime::Current()->IsActiveTransaction()); - // Constructs abort message. - std::string abort_msg; - android::base::StringAppendV(&abort_msg, fmt, args); - // Throws an exception so we can abort the transaction and rollback every change. - Runtime::Current()->AbortTransactionAndThrowAbortError(self, abort_msg); -} - // START DECLARATIONS : // // These additional declarations are required because clang complains @@ -1524,58 +1508,11 @@ bool DoFilledNewArray(const Instruction* inst, return true; } -// TODO: Use ObjPtr here. -template<typename T> -static void RecordArrayElementsInTransactionImpl(ObjPtr<mirror::PrimitiveArray<T>> array, - int32_t count) - REQUIRES_SHARED(Locks::mutator_lock_) { - Runtime* runtime = Runtime::Current(); - for (int32_t i = 0; i < count; ++i) { - runtime->RecordWriteArray(array.Ptr(), i, array->GetWithoutChecks(i)); - } -} - -void RecordArrayElementsInTransaction(ObjPtr<mirror::Array> array, int32_t count) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(Runtime::Current()->IsActiveTransaction()); - DCHECK(array != nullptr); - DCHECK_LE(count, array->GetLength()); - Primitive::Type primitive_component_type = array->GetClass()->GetComponentType()->GetPrimitiveType(); - switch (primitive_component_type) { - case Primitive::kPrimBoolean: - RecordArrayElementsInTransactionImpl(array->AsBooleanArray(), count); - break; - case Primitive::kPrimByte: - RecordArrayElementsInTransactionImpl(array->AsByteArray(), count); - break; - case Primitive::kPrimChar: - RecordArrayElementsInTransactionImpl(array->AsCharArray(), count); - break; - case Primitive::kPrimShort: - RecordArrayElementsInTransactionImpl(array->AsShortArray(), count); - break; - case Primitive::kPrimInt: - RecordArrayElementsInTransactionImpl(array->AsIntArray(), count); - break; - case Primitive::kPrimFloat: - RecordArrayElementsInTransactionImpl(array->AsFloatArray(), count); - break; - case Primitive::kPrimLong: - RecordArrayElementsInTransactionImpl(array->AsLongArray(), count); - break; - case Primitive::kPrimDouble: - RecordArrayElementsInTransactionImpl(array->AsDoubleArray(), count); - break; - default: - LOG(FATAL) << "Unsupported primitive type " << primitive_component_type - << " in fill-array-data"; - UNREACHABLE(); - } -} - void UnlockHeldMonitors(Thread* self, ShadowFrame* shadow_frame) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(shadow_frame->GetForcePopFrame() || Runtime::Current()->IsTransactionAborted()); + DCHECK(shadow_frame->GetForcePopFrame() || + (Runtime::Current()->IsActiveTransaction() && + Runtime::Current()->GetClassLinker()->IsTransactionAborted())); // Unlock all monitors. if (shadow_frame->GetMethod()->MustCountLocks()) { DCHECK(!shadow_frame->GetMethod()->SkipAccessChecks()); @@ -1605,6 +1542,26 @@ void UnlockHeldMonitors(Thread* self, ShadowFrame* shadow_frame) } } +void PerformNonStandardReturn(Thread* self, + ShadowFrame& frame, + JValue& result, + const instrumentation::Instrumentation* instrumentation, + bool unlock_monitors) { + if (UNLIKELY(self->IsExceptionPending())) { + LOG(WARNING) << "Suppressing exception for non-standard method exit: " + << self->GetException()->Dump(); + self->ClearException(); + } + if (unlock_monitors) { + UnlockHeldMonitors(self, &frame); + DoMonitorCheckOnExit(self, &frame); + } + result = JValue(); + if (UNLIKELY(NeedsMethodExitEvent(instrumentation))) { + SendMethodExitEvents(self, instrumentation, frame, frame.GetMethod(), result); + } +} + // Explicit DoCall template function declarations. #define EXPLICIT_DO_CALL_TEMPLATE_DECL(_is_range) \ template REQUIRES_SHARED(Locks::mutator_lock_) \ diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index e6548ef82b..2001e89b9d 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -20,7 +20,6 @@ #include "android-base/macros.h" #include "instrumentation.h" #include "interpreter.h" -#include "transaction.h" #include <math.h> @@ -111,16 +110,6 @@ static inline bool DoMonitorCheckOnExit(Thread* self, ShadowFrame* frame) return true; } -void AbortTransactionF(Thread* self, const char* fmt, ...) - __attribute__((__format__(__printf__, 2, 3))) - REQUIRES_SHARED(Locks::mutator_lock_); - -void AbortTransactionV(Thread* self, const char* fmt, va_list args) - REQUIRES_SHARED(Locks::mutator_lock_); - -void RecordArrayElementsInTransaction(ObjPtr<mirror::Array> array, int32_t count) - REQUIRES_SHARED(Locks::mutator_lock_); - // Invokes the given method. This is part of the invocation support and is used by DoInvoke, // DoFastInvoke and DoInvokeVirtualQuick functions. // Returns true on success, otherwise throws an exception and returns false. @@ -160,29 +149,11 @@ NeedsMethodExitEvent(const instrumentation::Instrumentation* ins) COLD_ATTR void UnlockHeldMonitors(Thread* self, ShadowFrame* shadow_frame) REQUIRES_SHARED(Locks::mutator_lock_); -static inline ALWAYS_INLINE void PerformNonStandardReturn( - Thread* self, - ShadowFrame& frame, - JValue& result, - const instrumentation::Instrumentation* instrumentation, - uint16_t num_dex_inst, - bool unlock_monitors = true) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr<mirror::Object> thiz(frame.GetThisObject(num_dex_inst)); - StackHandleScope<1u> hs(self); - if (UNLIKELY(self->IsExceptionPending())) { - LOG(WARNING) << "Suppressing exception for non-standard method exit: " - << self->GetException()->Dump(); - self->ClearException(); - } - if (unlock_monitors) { - UnlockHeldMonitors(self, &frame); - DoMonitorCheckOnExit(self, &frame); - } - result = JValue(); - if (UNLIKELY(NeedsMethodExitEvent(instrumentation))) { - SendMethodExitEvents(self, instrumentation, frame, frame.GetMethod(), result); - } -} +void PerformNonStandardReturn(Thread* self, + ShadowFrame& frame, + JValue& result, + const instrumentation::Instrumentation* instrumentation, + bool unlock_monitors = true) REQUIRES_SHARED(Locks::mutator_lock_); // Handles all invoke-XXX/range instructions except for invoke-polymorphic[/range]. // Returns true on success, otherwise throws an exception and returns false. @@ -349,230 +320,6 @@ static inline void GetFieldInfo(Thread* self, } } -// Handles iget-XXX and sget-XXX instructions. -// Returns true on success, otherwise throws an exception and returns false. -template<FindFieldType find_type, - Primitive::Type field_type, - bool transaction_active = false> -ALWAYS_INLINE bool DoFieldGet(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data) REQUIRES_SHARED(Locks::mutator_lock_) { - const bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead); - bool should_report = Runtime::Current()->GetInstrumentation()->HasFieldReadListeners(); - ArtField* field = nullptr; - MemberOffset offset(0u); - bool is_volatile; - GetFieldInfo(self, - shadow_frame.GetMethod(), - reinterpret_cast<const uint16_t*>(inst), - is_static, - /*resolve_field_type=*/ false, - &field, - &is_volatile, - &offset); - if (self->IsExceptionPending()) { - return false; - } - - ObjPtr<mirror::Object> obj; - if (is_static) { - obj = field->GetDeclaringClass(); - if (transaction_active) { - if (Runtime::Current()->GetTransaction()->ReadConstraint(obj)) { - Runtime::Current()->AbortTransactionAndThrowAbortError(self, "Can't read static fields of " - + obj->PrettyTypeOf() + " since it does not belong to clinit's class."); - return false; - } - } - } else { - obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); - if (should_report || obj == nullptr) { - field = ResolveFieldWithAccessChecks(self, - Runtime::Current()->GetClassLinker(), - inst->VRegC_22c(), - shadow_frame.GetMethod(), - /* is_static= */ false, - /* is_put= */ false, - /* resolve_field_type= */ false); - if (obj == nullptr) { - ThrowNullPointerExceptionForFieldAccess( - field, shadow_frame.GetMethod(), /* is_read= */ true); - return false; - } - // Reload in case suspension happened during field resolution. - obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); - } - } - - uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); - JValue result; - if (should_report) { - DCHECK(field != nullptr); - if (UNLIKELY(!DoFieldGetCommon<field_type>(self, shadow_frame, obj, field, &result))) { - // Instrumentation threw an error! - CHECK(self->IsExceptionPending()); - return false; - } - } - -#define FIELD_GET(prim, type, jtype, vreg) \ - case Primitive::kPrim ##prim: \ - shadow_frame.SetVReg ##vreg(vregA, \ - should_report ? result.Get ##jtype() \ - : is_volatile ? obj->GetField ## type ## Volatile(offset) \ - : obj->GetField ##type(offset)); \ - break; - - switch (field_type) { - FIELD_GET(Boolean, Boolean, Z, ) - FIELD_GET(Byte, Byte, B, ) - FIELD_GET(Char, Char, C, ) - FIELD_GET(Short, Short, S, ) - FIELD_GET(Int, 32, I, ) - FIELD_GET(Long, 64, J, Long) -#undef FIELD_GET - case Primitive::kPrimNot: - shadow_frame.SetVRegReference( - vregA, - should_report ? result.GetL() - : is_volatile ? obj->GetFieldObjectVolatile<mirror::Object>(offset) - : obj->GetFieldObject<mirror::Object>(offset)); - break; - default: - LOG(FATAL) << "Unreachable: " << field_type; - UNREACHABLE(); - } - return true; -} - -static inline bool CheckWriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) - REQUIRES_SHARED(Locks::mutator_lock_) { - Runtime* runtime = Runtime::Current(); - if (runtime->GetTransaction()->WriteConstraint(obj)) { - DCHECK(runtime->GetHeap()->ObjectIsInBootImageSpace(obj) || obj->IsClass()); - const char* base_msg = runtime->GetHeap()->ObjectIsInBootImageSpace(obj) - ? "Can't set fields of boot image " - : "Can't set fields of "; - runtime->AbortTransactionAndThrowAbortError(self, base_msg + obj->PrettyTypeOf()); - return false; - } - return true; -} - -static inline bool CheckWriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value) - REQUIRES_SHARED(Locks::mutator_lock_) { - Runtime* runtime = Runtime::Current(); - if (runtime->GetTransaction()->WriteValueConstraint(value)) { - DCHECK(value != nullptr); - std::string msg = value->IsClass() - ? "Can't store reference to class " + value->AsClass()->PrettyDescriptor() - : "Can't store reference to instance of " + value->GetClass()->PrettyDescriptor(); - runtime->AbortTransactionAndThrowAbortError(self, msg); - return false; - } - return true; -} - -// Handles iput-XXX and sput-XXX instructions. -// Returns true on success, otherwise throws an exception and returns false. -template<FindFieldType find_type, Primitive::Type field_type, bool transaction_active> -ALWAYS_INLINE bool DoFieldPut(Thread* self, - const ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data) - REQUIRES_SHARED(Locks::mutator_lock_) { - bool should_report = Runtime::Current()->GetInstrumentation()->HasFieldWriteListeners(); - bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite); - uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); - bool resolve_field_type = (shadow_frame.GetVRegReference(vregA) != nullptr); - ArtField* field = nullptr; - MemberOffset offset(0u); - bool is_volatile; - GetFieldInfo(self, - shadow_frame.GetMethod(), - reinterpret_cast<const uint16_t*>(inst), - is_static, - resolve_field_type, - &field, - &is_volatile, - &offset); - if (self->IsExceptionPending()) { - return false; - } - - ObjPtr<mirror::Object> obj; - if (is_static) { - obj = field->GetDeclaringClass(); - } else { - obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); - if (should_report || obj == nullptr) { - field = ResolveFieldWithAccessChecks(self, - Runtime::Current()->GetClassLinker(), - inst->VRegC_22c(), - shadow_frame.GetMethod(), - /* is_static= */ false, - /* is_put= */ true, - resolve_field_type); - if (UNLIKELY(obj == nullptr)) { - ThrowNullPointerExceptionForFieldAccess( - field, shadow_frame.GetMethod(), /* is_read= */ false); - return false; - } - // Reload in case suspension happened during field resolution. - obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); - } - } - if (transaction_active && !CheckWriteConstraint(self, obj)) { - return false; - } - - JValue value = GetFieldValue<field_type>(shadow_frame, vregA); - - if (transaction_active && - field_type == Primitive::kPrimNot && - !CheckWriteValueConstraint(self, value.GetL())) { - return false; - } - if (should_report) { - return DoFieldPutCommon<field_type, transaction_active>(self, - shadow_frame, - obj, - field, - value); - } -#define FIELD_SET(prim, type, jtype) \ - case Primitive::kPrim ## prim: \ - if (is_volatile) { \ - obj->SetField ## type ## Volatile<transaction_active>(offset, value.Get ## jtype()); \ - } else { \ - obj->SetField ## type<transaction_active>(offset, value.Get ## jtype()); \ - } \ - break; - - switch (field_type) { - FIELD_SET(Boolean, Boolean, Z) - FIELD_SET(Byte, Byte, B) - FIELD_SET(Char, Char, C) - FIELD_SET(Short, Short, S) - FIELD_SET(Int, 32, I) - FIELD_SET(Long, 64, J) - FIELD_SET(Not, Object, L) - case Primitive::kPrimVoid: { - LOG(FATAL) << "Unreachable " << field_type; - break; - } - } -#undef FIELD_SET - - if (transaction_active) { - if (UNLIKELY(self->IsExceptionPending())) { - return false; - } - } - return true; -} - // Handles string resolution for const-string and const-string-jumbo instructions. Also ensures the // java.lang.String class is initialized. static inline ObjPtr<mirror::String> ResolveString(Thread* self, diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h index 8b23715ba6..222f3775bc 100644 --- a/runtime/interpreter/interpreter_switch_impl-inl.h +++ b/runtime/interpreter/interpreter_switch_impl-inl.h @@ -23,6 +23,7 @@ #include "base/memory_tool.h" #include "base/pointer_size.h" #include "base/quasi_atomic.h" +#include "common_throws.h" #include "dex/dex_file_types.h" #include "dex/dex_instruction_list.h" #include "experimental_flags.h" @@ -43,6 +44,218 @@ namespace art HIDDEN { namespace interpreter { +// We declare the helpers classes for transaction checks here but they shall be defined +// only when compiling the transactional and non-transactional interpreter. +class ActiveTransactionChecker; // For transactional interpreter. +class InactiveTransactionChecker; // For non-transactional interpreter. + +// We declare the helpers classes for instrumentation handling here but they shall be defined +// only when compiling the transactional and non-transactional interpreter. +class ActiveInstrumentationHandler; // For non-transactional interpreter. +class InactiveInstrumentationHandler; // For transactional interpreter. + +// Handles iget-XXX and sget-XXX instructions. +// Returns true on success, otherwise throws an exception and returns false. +template<FindFieldType find_type, + Primitive::Type field_type, + bool transaction_active = false> +ALWAYS_INLINE bool DoFieldGet(Thread* self, + ShadowFrame& shadow_frame, + const Instruction* inst, + uint16_t inst_data, + const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + using InstrumentationHandler = typename std::conditional_t< + transaction_active, InactiveInstrumentationHandler, ActiveInstrumentationHandler>; + bool should_report = InstrumentationHandler::HasFieldReadListeners(instrumentation); + const bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead); + ArtField* field = nullptr; + MemberOffset offset(0u); + bool is_volatile; + GetFieldInfo(self, + shadow_frame.GetMethod(), + reinterpret_cast<const uint16_t*>(inst), + is_static, + /*resolve_field_type=*/ false, + &field, + &is_volatile, + &offset); + if (self->IsExceptionPending()) { + return false; + } + + ObjPtr<mirror::Object> obj; + if (is_static) { + obj = field->GetDeclaringClass(); + using TransactionChecker = typename std::conditional_t< + transaction_active, ActiveTransactionChecker, InactiveTransactionChecker>; + if (TransactionChecker::ReadConstraint(self, obj)) { + return false; + } + } else { + obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); + if (should_report || obj == nullptr) { + field = ResolveFieldWithAccessChecks(self, + Runtime::Current()->GetClassLinker(), + inst->VRegC_22c(), + shadow_frame.GetMethod(), + /* is_static= */ false, + /* is_put= */ false, + /* resolve_field_type= */ false); + if (obj == nullptr) { + ThrowNullPointerExceptionForFieldAccess( + field, shadow_frame.GetMethod(), /* is_read= */ true); + return false; + } + // Reload in case suspension happened during field resolution. + obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); + } + } + + uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); + JValue result; + if (should_report) { + DCHECK(field != nullptr); + if (UNLIKELY(!DoFieldGetCommon<field_type>(self, shadow_frame, obj, field, &result))) { + // Instrumentation threw an error! + CHECK(self->IsExceptionPending()); + return false; + } + } + +#define FIELD_GET(prim, type, jtype, vreg) \ + case Primitive::kPrim ##prim: \ + shadow_frame.SetVReg ##vreg(vregA, \ + should_report ? result.Get ##jtype() \ + : is_volatile ? obj->GetField ## type ## Volatile(offset) \ + : obj->GetField ##type(offset)); \ + break; + + switch (field_type) { + FIELD_GET(Boolean, Boolean, Z, ) + FIELD_GET(Byte, Byte, B, ) + FIELD_GET(Char, Char, C, ) + FIELD_GET(Short, Short, S, ) + FIELD_GET(Int, 32, I, ) + FIELD_GET(Long, 64, J, Long) +#undef FIELD_GET + case Primitive::kPrimNot: + shadow_frame.SetVRegReference( + vregA, + should_report ? result.GetL() + : is_volatile ? obj->GetFieldObjectVolatile<mirror::Object>(offset) + : obj->GetFieldObject<mirror::Object>(offset)); + break; + default: + LOG(FATAL) << "Unreachable: " << field_type; + UNREACHABLE(); + } + return true; +} + +// Handles iput-XXX and sput-XXX instructions. +// Returns true on success, otherwise throws an exception and returns false. +template<FindFieldType find_type, Primitive::Type field_type, bool transaction_active> +ALWAYS_INLINE bool DoFieldPut(Thread* self, + const ShadowFrame& shadow_frame, + const Instruction* inst, + uint16_t inst_data, + const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + using InstrumentationHandler = typename std::conditional_t< + transaction_active, InactiveInstrumentationHandler, ActiveInstrumentationHandler>; + bool should_report = InstrumentationHandler::HasFieldWriteListeners(instrumentation); + bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite); + uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); + bool resolve_field_type = (shadow_frame.GetVRegReference(vregA) != nullptr); + ArtField* field = nullptr; + MemberOffset offset(0u); + bool is_volatile; + GetFieldInfo(self, + shadow_frame.GetMethod(), + reinterpret_cast<const uint16_t*>(inst), + is_static, + resolve_field_type, + &field, + &is_volatile, + &offset); + if (self->IsExceptionPending()) { + return false; + } + + ObjPtr<mirror::Object> obj; + if (is_static) { + obj = field->GetDeclaringClass(); + } else { + obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); + if (should_report || obj == nullptr) { + field = ResolveFieldWithAccessChecks(self, + Runtime::Current()->GetClassLinker(), + inst->VRegC_22c(), + shadow_frame.GetMethod(), + /* is_static= */ false, + /* is_put= */ true, + resolve_field_type); + if (UNLIKELY(obj == nullptr)) { + ThrowNullPointerExceptionForFieldAccess( + field, shadow_frame.GetMethod(), /* is_read= */ false); + return false; + } + // Reload in case suspension happened during field resolution. + obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); + } + } + using TransactionChecker = typename std::conditional_t< + transaction_active, ActiveTransactionChecker, InactiveTransactionChecker>; + if (TransactionChecker::WriteConstraint(self, obj)) { + return false; + } + + JValue value = GetFieldValue<field_type>(shadow_frame, vregA); + + if (field_type == Primitive::kPrimNot && + TransactionChecker::WriteValueConstraint(self, value.GetL())) { + return false; + } + if (should_report) { + return DoFieldPutCommon<field_type, transaction_active>(self, + shadow_frame, + obj, + field, + value); + } +#define FIELD_SET(prim, type, jtype) \ + case Primitive::kPrim ## prim: \ + if (is_volatile) { \ + obj->SetField ## type ## Volatile<transaction_active>(offset, value.Get ## jtype()); \ + } else { \ + obj->SetField ## type<transaction_active>(offset, value.Get ## jtype()); \ + } \ + break; + + switch (field_type) { + FIELD_SET(Boolean, Boolean, Z) + FIELD_SET(Byte, Byte, B) + FIELD_SET(Char, Char, C) + FIELD_SET(Short, Short, S) + FIELD_SET(Int, 32, I) + FIELD_SET(Long, 64, J) + FIELD_SET(Not, Object, L) + case Primitive::kPrimVoid: { + LOG(FATAL) << "Unreachable " << field_type; + break; + } + } +#undef FIELD_SET + + if (transaction_active) { + if (UNLIKELY(self->IsExceptionPending())) { + return false; + } + } + return true; +} + // Short-lived helper class which executes single DEX bytecode. It is inlined by compiler. // Any relevant execution information is stored in the fields - it should be kept to minimum. // All instance functions must be inlined so that the fields can be stored in registers. @@ -53,19 +266,23 @@ namespace interpreter { template<bool transaction_active, Instruction::Format kFormat> class InstructionHandler { public: + using InstrumentationHandler = typename std::conditional_t< + transaction_active, InactiveInstrumentationHandler, ActiveInstrumentationHandler>; + using TransactionChecker = typename std::conditional_t< + transaction_active, ActiveTransactionChecker, InactiveTransactionChecker>; + #define HANDLER_ATTRIBUTES ALWAYS_INLINE FLATTEN WARN_UNUSED REQUIRES_SHARED(Locks::mutator_lock_) HANDLER_ATTRIBUTES bool CheckTransactionAbort() { - if (transaction_active && Runtime::Current()->IsTransactionAborted()) { + if (TransactionChecker::IsTransactionAborted()) { // Transaction abort cannot be caught by catch handlers. // Preserve the abort exception while doing non-standard return. StackHandleScope<1u> hs(Self()); Handle<mirror::Throwable> abort_exception = hs.NewHandle(Self()->GetException()); DCHECK(abort_exception != nullptr); - DCHECK(abort_exception->GetClass()->DescriptorEquals(Transaction::kAbortExceptionDescriptor)); + DCHECK(abort_exception->GetClass()->DescriptorEquals(kTransactionAbortErrorDescriptor)); Self()->ClearException(); - PerformNonStandardReturn( - Self(), shadow_frame_, ctx_->result, Instrumentation(), Accessor().InsSize()); + PerformNonStandardReturn(Self(), shadow_frame_, ctx_->result, Instrumentation()); Self()->SetException(abort_exception.Get()); ExitInterpreterLoop(); return false; @@ -74,10 +291,9 @@ class InstructionHandler { } HANDLER_ATTRIBUTES bool CheckForceReturn() { - if (shadow_frame_.GetForcePopFrame()) { + if (InstrumentationHandler::GetForcePopFrame(shadow_frame_)) { DCHECK(Runtime::Current()->AreNonStandardExitsEnabled()); - PerformNonStandardReturn( - Self(), shadow_frame_, ctx_->result, Instrumentation(), Accessor().InsSize()); + PerformNonStandardReturn(Self(), shadow_frame_, ctx_->result, Instrumentation()); ExitInterpreterLoop(); return false; } @@ -144,16 +360,16 @@ class InstructionHandler { if (!CheckForceReturn()) { return false; } - if (UNLIKELY(shadow_frame_.GetNotifyDexPcMoveEvents())) { + if (UNLIKELY(InstrumentationHandler::NeedsDexPcEvents(shadow_frame_))) { uint8_t opcode = inst_->Opcode(inst_data_); bool is_move_result_object = (opcode == Instruction::MOVE_RESULT_OBJECT); JValue* save_ref = is_move_result_object ? &ctx_->result_register : nullptr; - if (UNLIKELY(!DoDexPcMoveEvent(Self(), - Accessor(), - shadow_frame_, - DexPC(), - Instrumentation(), - save_ref))) { + if (UNLIKELY(!InstrumentationHandler::DoDexPcMoveEvent(Self(), + Accessor(), + shadow_frame_, + DexPC(), + Instrumentation(), + save_ref))) { DCHECK(Self()->IsExceptionPending()); // Do not raise exception event if it is caused by other instrumentation event. shadow_frame_.SetSkipNextExceptionEvent(true); @@ -166,53 +382,17 @@ class InstructionHandler { return true; } - // Unlike most other events the DexPcMovedEvent can be sent when there is a pending exception (if - // the next instruction is MOVE_EXCEPTION). This means it needs to be handled carefully to be able - // to detect exceptions thrown by the DexPcMovedEvent itself. These exceptions could be thrown by - // jvmti-agents while handling breakpoint or single step events. We had to move this into its own - // function because it was making ExecuteSwitchImpl have too large a stack. - NO_INLINE static bool DoDexPcMoveEvent(Thread* self, - const CodeItemDataAccessor& accessor, - const ShadowFrame& shadow_frame, - uint32_t dex_pc_, - const instrumentation::Instrumentation* instrumentation, - JValue* save_ref) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(instrumentation->HasDexPcListeners()); - StackHandleScope<2> hs(self); - Handle<mirror::Throwable> thr(hs.NewHandle(self->GetException())); - mirror::Object* null_obj = nullptr; - HandleWrapper<mirror::Object> h( - hs.NewHandleWrapper(LIKELY(save_ref == nullptr) ? &null_obj : save_ref->GetGCRoot())); - self->ClearException(); - instrumentation->DexPcMovedEvent(self, - shadow_frame.GetThisObject(accessor.InsSize()), - shadow_frame.GetMethod(), - dex_pc_); - if (UNLIKELY(self->IsExceptionPending())) { - // We got a new exception in the dex-pc-moved event. - // We just let this exception replace the old one. - // TODO It would be good to add the old exception to the - // suppressed exceptions of the new one if possible. - return false; // Pending exception. - } - if (UNLIKELY(!thr.IsNull())) { - self->SetException(thr.Get()); - } - return true; - } - HANDLER_ATTRIBUTES bool HandleReturn(JValue result) { Self()->AllowThreadSuspension(); if (!DoMonitorCheckOnExit(Self(), &shadow_frame_)) { return false; } - if (UNLIKELY(NeedsMethodExitEvent(Instrumentation()) && - !SendMethodExitEvents(Self(), - Instrumentation(), - shadow_frame_, - shadow_frame_.GetMethod(), - result))) { + if (UNLIKELY(InstrumentationHandler::NeedsMethodExitEvent(Instrumentation()) && + !InstrumentationHandler::SendMethodExitEvents(Self(), + Instrumentation(), + shadow_frame_, + shadow_frame_.GetMethod(), + result))) { DCHECK(Self()->IsExceptionPending()); // Do not raise exception event if it is caused by other instrumentation event. shadow_frame_.SetSkipNextExceptionEvent(true); @@ -227,8 +407,9 @@ class InstructionHandler { if (UNLIKELY(Self()->ObserveAsyncException())) { return false; // Pending exception. } - if (UNLIKELY(Instrumentation()->HasBranchListeners())) { - Instrumentation()->Branch(Self(), shadow_frame_.GetMethod(), DexPC(), offset); + if (UNLIKELY(InstrumentationHandler::HasBranchListeners(Instrumentation()))) { + InstrumentationHandler::Branch( + Self(), shadow_frame_.GetMethod(), DexPC(), offset, Instrumentation()); } if (!transaction_active) { // TODO: Do OSR only on back-edges and check if OSR code is ready here. @@ -332,7 +513,7 @@ class InstructionHandler { if (UNLIKELY(!array->CheckIsValidIndex(index))) { return false; // Pending exception. } - if (transaction_active && !CheckWriteConstraint(Self(), array)) { + if (TransactionChecker::WriteConstraint(Self(), array)) { return false; } array->template SetWithoutChecks<transaction_active>(index, value); @@ -342,13 +523,13 @@ class InstructionHandler { template<FindFieldType find_type, Primitive::Type field_type> HANDLER_ATTRIBUTES bool HandleGet() { return DoFieldGet<find_type, field_type, transaction_active>( - Self(), shadow_frame_, inst_, inst_data_); + Self(), shadow_frame_, inst_, inst_data_, Instrumentation()); } template<FindFieldType find_type, Primitive::Type field_type> HANDLER_ATTRIBUTES bool HandlePut() { return DoFieldPut<find_type, field_type, transaction_active>( - Self(), shadow_frame_, inst_, inst_data_); + Self(), shadow_frame_, inst_, inst_data_, Instrumentation()); } template<InvokeType type, bool is_range> @@ -482,14 +663,14 @@ class InstructionHandler { } } result.SetL(obj_result); - if (UNLIKELY(NeedsMethodExitEvent(Instrumentation()))) { + if (UNLIKELY(InstrumentationHandler::NeedsMethodExitEvent(Instrumentation()))) { StackHandleScope<1> hs(Self()); MutableHandle<mirror::Object> h_result(hs.NewHandle(obj_result)); - if (!SendMethodExitEvents(Self(), - Instrumentation(), - shadow_frame_, - shadow_frame_.GetMethod(), - h_result)) { + if (!InstrumentationHandler::SendMethodExitEvents(Self(), + Instrumentation(), + shadow_frame_, + shadow_frame_.GetMethod(), + h_result)) { DCHECK(Self()->IsExceptionPending()); // Do not raise exception event if it is caused by other instrumentation event. shadow_frame_.SetSkipNextExceptionEvent(true); @@ -669,10 +850,7 @@ class InstructionHandler { if (LIKELY(c != nullptr)) { // Don't allow finalizable objects to be allocated during a transaction since these can't // be finalized without a started runtime. - if (transaction_active && c->IsFinalizable()) { - AbortTransactionF(Self(), - "Allocating finalizable object in transaction: %s", - c->PrettyDescriptor().c_str()); + if (TransactionChecker::AllocationConstraint(Self(), c)) { return false; // Pending exception. } gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); @@ -720,12 +898,11 @@ class InstructionHandler { const Instruction::ArrayDataPayload* payload = reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr); ObjPtr<mirror::Object> obj = GetVRegReference(A()); + // If we have an active transaction, record old values before we overwrite them. + TransactionChecker::RecordArrayElementsInTransaction(obj, payload->element_count); if (!FillArrayData(obj, payload)) { return false; // Pending exception. } - if (transaction_active) { - RecordArrayElementsInTransaction(obj->AsArray(), payload->element_count); - } return true; } @@ -898,8 +1075,8 @@ class InstructionHandler { ObjPtr<mirror::Object> val = GetVRegReference(A()); ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>(); if (array->CheckIsValidIndex(index) && array->CheckAssignable(val)) { - if (transaction_active && - (!CheckWriteConstraint(Self(), array) || !CheckWriteValueConstraint(Self(), val))) { + if (TransactionChecker::WriteConstraint(Self(), array) || + TransactionChecker::WriteValueConstraint(Self(), val)) { return false; } array->SetWithoutChecks<transaction_active>(index, val); diff --git a/runtime/interpreter/interpreter_switch_impl0.cc b/runtime/interpreter/interpreter_switch_impl0.cc index 65ae2fe333..378b6895a5 100644 --- a/runtime/interpreter/interpreter_switch_impl0.cc +++ b/runtime/interpreter/interpreter_switch_impl0.cc @@ -22,6 +22,141 @@ namespace art HIDDEN { namespace interpreter { +// Define the helper class that does not do any transaction checks. +class InactiveTransactionChecker { + public: + ALWAYS_INLINE static bool WriteConstraint([[maybe_unused]] Thread* self, + [[maybe_unused]] ObjPtr<mirror::Object> obj) + REQUIRES_SHARED(Locks::mutator_lock_) { + return false; + } + + ALWAYS_INLINE static bool WriteValueConstraint([[maybe_unused]] Thread* self, + [[maybe_unused]] ObjPtr<mirror::Object> value) + REQUIRES_SHARED(Locks::mutator_lock_) { + return false; + } + + ALWAYS_INLINE static bool ReadConstraint([[maybe_unused]] Thread* self, + [[maybe_unused]] ObjPtr<mirror::Object> value) + REQUIRES_SHARED(Locks::mutator_lock_) { + return false; + } + + ALWAYS_INLINE static bool AllocationConstraint([[maybe_unused]] Thread* self, + [[maybe_unused]] ObjPtr<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + return false; + } + + ALWAYS_INLINE static bool IsTransactionAborted() { + return false; + } + + static void RecordArrayElementsInTransaction([[maybe_unused]] ObjPtr<mirror::Object> array, + [[maybe_unused]] int32_t count) + REQUIRES_SHARED(Locks::mutator_lock_) {} +}; + +class ActiveInstrumentationHandler { + public: + ALWAYS_INLINE WARN_UNUSED + static bool HasFieldReadListeners(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + return instrumentation->HasFieldReadListeners(); + } + + ALWAYS_INLINE WARN_UNUSED + static bool HasFieldWriteListeners(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + return instrumentation->HasFieldWriteListeners(); + } + + ALWAYS_INLINE WARN_UNUSED + static bool HasBranchListeners(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + return instrumentation->HasBranchListeners(); + } + + ALWAYS_INLINE WARN_UNUSED + static bool NeedsDexPcEvents(ShadowFrame& shadow_frame) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_IMPLIES(shadow_frame.GetNotifyDexPcMoveEvents(), + Runtime::Current()->GetInstrumentation()->HasDexPcListeners()); + return shadow_frame.GetNotifyDexPcMoveEvents(); + } + + ALWAYS_INLINE WARN_UNUSED + static bool NeedsMethodExitEvent(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + return interpreter::NeedsMethodExitEvent(instrumentation); + } + + ALWAYS_INLINE WARN_UNUSED + static bool GetForcePopFrame(ShadowFrame& shadow_frame) { + DCHECK_IMPLIES(shadow_frame.GetForcePopFrame(), + Runtime::Current()->AreNonStandardExitsEnabled()); + return shadow_frame.GetForcePopFrame(); + } + + ALWAYS_INLINE + static void Branch(Thread* self, + ArtMethod* method, + uint32_t dex_pc, + int32_t dex_pc_offset, + const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + instrumentation->Branch(self, method, dex_pc, dex_pc_offset); + } + + // Unlike most other events the DexPcMovedEvent can be sent when there is a pending exception (if + // the next instruction is MOVE_EXCEPTION). This means it needs to be handled carefully to be able + // to detect exceptions thrown by the DexPcMovedEvent itself. These exceptions could be thrown by + // jvmti-agents while handling breakpoint or single step events. We had to move this into its own + // function because it was making ExecuteSwitchImpl have too large a stack. + NO_INLINE static bool DoDexPcMoveEvent(Thread* self, + const CodeItemDataAccessor& accessor, + const ShadowFrame& shadow_frame, + uint32_t dex_pc, + const instrumentation::Instrumentation* instrumentation, + JValue* save_ref) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(instrumentation->HasDexPcListeners()); + StackHandleScope<2> hs(self); + Handle<mirror::Throwable> thr(hs.NewHandle(self->GetException())); + mirror::Object* null_obj = nullptr; + HandleWrapper<mirror::Object> h( + hs.NewHandleWrapper(LIKELY(save_ref == nullptr) ? &null_obj : save_ref->GetGCRoot())); + self->ClearException(); + instrumentation->DexPcMovedEvent(self, + shadow_frame.GetThisObject(accessor.InsSize()), + shadow_frame.GetMethod(), + dex_pc); + if (UNLIKELY(self->IsExceptionPending())) { + // We got a new exception in the dex-pc-moved event. + // We just let this exception replace the old one. + // TODO It would be good to add the old exception to the + // suppressed exceptions of the new one if possible. + return false; // Pending exception. + } + if (UNLIKELY(!thr.IsNull())) { + self->SetException(thr.Get()); + } + return true; + } + + template <typename T> + ALWAYS_INLINE WARN_UNUSED + static bool SendMethodExitEvents( + Thread* self, + const instrumentation::Instrumentation* instrumentation, + ShadowFrame& frame, + ArtMethod* method, + T& result) REQUIRES_SHARED(Locks::mutator_lock_) { + return interpreter::SendMethodExitEvents(self, instrumentation, frame, method, result); + } +}; + // Explicit definition of ExecuteSwitchImplCpp. template HOT_ATTR void ExecuteSwitchImplCpp<false>(SwitchImplContext* ctx); diff --git a/runtime/interpreter/interpreter_switch_impl1.cc b/runtime/interpreter/interpreter_switch_impl1.cc index b9033d926e..2f24ab244c 100644 --- a/runtime/interpreter/interpreter_switch_impl1.cc +++ b/runtime/interpreter/interpreter_switch_impl1.cc @@ -19,9 +19,182 @@ #include "interpreter_switch_impl-inl.h" +#include "oat/aot_class_linker.h" + namespace art HIDDEN { namespace interpreter { +class ActiveTransactionChecker { + public: + static inline bool WriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetClassLinker()->TransactionWriteConstraint(self, obj); + } + + static inline bool WriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetClassLinker()->TransactionWriteValueConstraint(self, value); + } + + static inline bool ReadConstraint(Thread* self, ObjPtr<mirror::Object> obj) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetClassLinker()->TransactionReadConstraint(self, obj); + } + + static inline bool AllocationConstraint(Thread* self, ObjPtr<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetClassLinker()->TransactionAllocationConstraint(self, klass); + } + + static inline bool IsTransactionAborted() { + return GetClassLinker()->IsTransactionAborted(); + } + + static void RecordArrayElementsInTransaction(ObjPtr<mirror::Object> array, int32_t count) + REQUIRES_SHARED(Locks::mutator_lock_); + + private: + static AotClassLinker* GetClassLinker() { + return down_cast<AotClassLinker*>(Runtime::Current()->GetClassLinker()); + } +}; + +// TODO: Use ObjPtr here. +template<typename T> +static void RecordArrayElementsInTransactionImpl(ObjPtr<mirror::PrimitiveArray<T>> array, + int32_t count) + REQUIRES_SHARED(Locks::mutator_lock_) { + Runtime* runtime = Runtime::Current(); + for (int32_t i = 0; i < count; ++i) { + runtime->GetClassLinker()->RecordWriteArray(array.Ptr(), i, array->GetWithoutChecks(i)); + } +} + +void ActiveTransactionChecker::RecordArrayElementsInTransaction(ObjPtr<mirror::Object> array, + int32_t count) { + DCHECK(Runtime::Current()->IsActiveTransaction()); + if (array == nullptr) { + return; // The interpreter shall throw NPE. + } + DCHECK(array->IsArrayInstance()); + DCHECK_LE(count, array->AsArray()->GetLength()); + // No read barrier is needed for reading a chain of constant references + // for reading a constant primitive value, see `ReadBarrierOption`. + Primitive::Type primitive_component_type = + array->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() + ->GetComponentType<kDefaultVerifyFlags, kWithoutReadBarrier>()->GetPrimitiveType(); + switch (primitive_component_type) { + case Primitive::kPrimBoolean: + RecordArrayElementsInTransactionImpl(array->AsBooleanArray(), count); + break; + case Primitive::kPrimByte: + RecordArrayElementsInTransactionImpl(array->AsByteArray(), count); + break; + case Primitive::kPrimChar: + RecordArrayElementsInTransactionImpl(array->AsCharArray(), count); + break; + case Primitive::kPrimShort: + RecordArrayElementsInTransactionImpl(array->AsShortArray(), count); + break; + case Primitive::kPrimInt: + RecordArrayElementsInTransactionImpl(array->AsIntArray(), count); + break; + case Primitive::kPrimFloat: + RecordArrayElementsInTransactionImpl(array->AsFloatArray(), count); + break; + case Primitive::kPrimLong: + RecordArrayElementsInTransactionImpl(array->AsLongArray(), count); + break; + case Primitive::kPrimDouble: + RecordArrayElementsInTransactionImpl(array->AsDoubleArray(), count); + break; + default: + LOG(FATAL) << "Unsupported primitive type " << primitive_component_type + << " in fill-array-data"; + UNREACHABLE(); + } +} + +class InactiveInstrumentationHandler { + public: + ALWAYS_INLINE WARN_UNUSED + static bool HasFieldReadListeners(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!instrumentation->HasFieldReadListeners()); + return false; + } + + ALWAYS_INLINE WARN_UNUSED + static bool HasFieldWriteListeners(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!instrumentation->HasFieldWriteListeners()); + return false; + } + + ALWAYS_INLINE WARN_UNUSED + static bool HasBranchListeners(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!instrumentation->HasBranchListeners()); + return false; + } + + ALWAYS_INLINE WARN_UNUSED + static bool NeedsDexPcEvents(ShadowFrame& shadow_frame) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!shadow_frame.GetNotifyDexPcMoveEvents()); + DCHECK(!Runtime::Current()->GetInstrumentation()->HasDexPcListeners()); + return false; + } + + ALWAYS_INLINE WARN_UNUSED + static bool NeedsMethodExitEvent(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!interpreter::NeedsMethodExitEvent(instrumentation)); + return false; + } + + ALWAYS_INLINE WARN_UNUSED + static bool GetForcePopFrame(ShadowFrame& shadow_frame) { + DCHECK(!shadow_frame.GetForcePopFrame()); + DCHECK(!Runtime::Current()->AreNonStandardExitsEnabled()); + return false; + } + + NO_RETURN + static void Branch([[maybe_unused]] Thread* self, + [[maybe_unused]] ArtMethod* method, + [[maybe_unused]] uint32_t dex_pc, + [[maybe_unused]] int32_t dex_pc_offset, + [[maybe_unused]] const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); + } + + static bool DoDexPcMoveEvent( + [[maybe_unused]] Thread* self, + [[maybe_unused]] const CodeItemDataAccessor& accessor, + [[maybe_unused]] const ShadowFrame& shadow_frame, + [[maybe_unused]] uint32_t dex_pc, + [[maybe_unused]] const instrumentation::Instrumentation* instrumentation, + [[maybe_unused]] JValue* save_ref) + REQUIRES_SHARED(Locks::mutator_lock_) { + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); + } + + template <typename T> + static bool SendMethodExitEvents( + [[maybe_unused]] Thread* self, + [[maybe_unused]] const instrumentation::Instrumentation* instrumentation, + [[maybe_unused]] ShadowFrame& frame, + [[maybe_unused]] ArtMethod* method, + [[maybe_unused]] T& result) REQUIRES_SHARED(Locks::mutator_lock_) { + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); + } +}; + // Explicit definition of ExecuteSwitchImplCpp. template void ExecuteSwitchImplCpp<true>(SwitchImplContext* ctx); diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 7309391b32..d83f5378fd 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -62,7 +62,6 @@ #include "nth_caller_visitor.h" #include "reflection.h" #include "thread-inl.h" -#include "transaction.h" #include "unstarted_runtime_list.h" #include "well_known_classes-inl.h" @@ -78,9 +77,10 @@ static void AbortTransactionOrFail(Thread* self, const char* fmt, ...) static void AbortTransactionOrFail(Thread* self, const char* fmt, ...) { va_list args; - if (Runtime::Current()->IsActiveTransaction()) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { va_start(args, fmt); - AbortTransactionV(self, fmt, args); + runtime->GetClassLinker()->AbortTransactionV(self, fmt, args); va_end(args); } else { va_start(args, fmt); @@ -156,6 +156,12 @@ static void UnstartedRuntimeFindClass(Thread* self, result->SetL(found); } +static inline bool PendingExceptionHasAbortDescriptor(Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(self->IsExceptionPending()); + return self->GetException()->GetClass()->DescriptorEquals(kTransactionAbortErrorDescriptor); +} + // Common helper for class-loading cutouts in an unstarted runtime. We call Runtime methods that // rely on Java code to wrap errors in the correct exception class (i.e., NoClassDefFoundError into // ClassNotFoundException), so need to do the same. The only exception is if the exception is @@ -165,22 +171,22 @@ static void CheckExceptionGenerateClassNotFound(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { if (self->IsExceptionPending()) { Runtime* runtime = Runtime::Current(); - DCHECK_EQ(runtime->IsTransactionAborted(), - self->GetException()->GetClass()->DescriptorEquals( - Transaction::kAbortExceptionDescriptor)) - << self->GetException()->GetClass()->PrettyDescriptor(); if (runtime->IsActiveTransaction()) { // The boot class path at run time may contain additional dex files with // the required class definition(s). We cannot throw a normal exception at // compile time because a class initializer could catch it and successfully // initialize a class differently than when executing at run time. // If we're not aborting the transaction yet, abort now. b/183691501 - if (!runtime->IsTransactionAborted()) { - AbortTransactionF(self, "ClassNotFoundException"); + if (!runtime->GetClassLinker()->IsTransactionAborted()) { + DCHECK(!PendingExceptionHasAbortDescriptor(self)); + runtime->GetClassLinker()->AbortTransactionF(self, "ClassNotFoundException"); + } else { + DCHECK(PendingExceptionHasAbortDescriptor(self)) + << self->GetException()->GetClass()->PrettyDescriptor(); } } else { // If not in a transaction, it cannot be the transaction abort exception. Wrap it. - DCHECK(!runtime->IsTransactionAborted()); + DCHECK(!PendingExceptionHasAbortDescriptor(self)); self->ThrowNewWrappedException("Ljava/lang/ClassNotFoundException;", "ClassNotFoundException"); } @@ -301,12 +307,11 @@ void UnstartedRuntime::UnstartedClassNewInstance( } // If we're in a transaction, class must not be finalizable (it or a superclass has a finalizer). - if (Runtime::Current()->IsActiveTransaction()) { - if (h_klass->IsFinalizable()) { - AbortTransactionF(self, "Class for newInstance is finalizable: '%s'", - h_klass->PrettyClass().c_str()); - return; - } + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction() && + runtime->GetClassLinker()->TransactionAllocationConstraint(self, h_klass.Get())) { + DCHECK(self->IsExceptionPending()); + return; } // There are two situations in which we'll abort this run. @@ -314,7 +319,7 @@ void UnstartedRuntime::UnstartedClassNewInstance( // 2) If we can't find the default constructor. We'll postpone the exception to runtime. // Note that 2) could likely be handled here, but for safety abort the transaction. bool ok = false; - auto* cl = Runtime::Current()->GetClassLinker(); + auto* cl = runtime->GetClassLinker(); if (cl->EnsureInitialized(self, h_klass, true, true)) { ArtMethod* cons = h_klass->FindConstructor("()V", cl->GetImagePointerSize()); if (cons != nullptr && ShouldDenyAccessToMember(cons, shadow_frame)) { @@ -763,19 +768,19 @@ void UnstartedRuntime::UnstartedVmClassLoaderFindLoadedClass( // This might have an error pending. But semantics are to just return null. if (self->IsExceptionPending()) { Runtime* runtime = Runtime::Current(); - DCHECK_EQ(runtime->IsTransactionAborted(), - self->GetException()->GetClass()->DescriptorEquals( - Transaction::kAbortExceptionDescriptor)) - << self->GetException()->GetClass()->PrettyDescriptor(); if (runtime->IsActiveTransaction()) { // If we're not aborting the transaction yet, abort now. b/183691501 // See CheckExceptionGenerateClassNotFound() for more detailed explanation. - if (!runtime->IsTransactionAborted()) { - AbortTransactionF(self, "ClassNotFoundException"); + if (!runtime->GetClassLinker()->IsTransactionAborted()) { + DCHECK(!PendingExceptionHasAbortDescriptor(self)); + runtime->GetClassLinker()->AbortTransactionF(self, "ClassNotFoundException"); + } else { + DCHECK(PendingExceptionHasAbortDescriptor(self)) + << self->GetException()->GetClass()->PrettyDescriptor(); } } else { // If not in a transaction, it cannot be the transaction abort exception. Clear it. - DCHECK(!runtime->IsTransactionAborted()); + DCHECK(!PendingExceptionHasAbortDescriptor(self)); self->ClearException(); } } @@ -855,7 +860,9 @@ void UnstartedRuntime::UnstartedSystemArraycopy(Thread* self, return; } - if (Runtime::Current()->IsActiveTransaction() && !CheckWriteConstraint(self, dst_obj)) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction() && + runtime->GetClassLinker()->TransactionWriteConstraint(self, dst_obj)) { DCHECK(self->IsExceptionPending()); return; } @@ -1301,7 +1308,7 @@ static void UnstartedMemoryPeekArray( int64_t address_long = shadow_frame->GetVRegLong(arg_offset); mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset + 2); if (obj == nullptr) { - Runtime::Current()->AbortTransactionAndThrowAbortError(self, "Null pointer in peekArray"); + Runtime::Current()->GetClassLinker()->AbortTransactionF(self, "Null pointer in peekArray"); return; } ObjPtr<mirror::Array> array = obj->AsArray(); @@ -1309,9 +1316,8 @@ static void UnstartedMemoryPeekArray( int offset = shadow_frame->GetVReg(arg_offset + 3); int count = shadow_frame->GetVReg(arg_offset + 4); if (offset < 0 || offset + count > array->GetLength()) { - std::string error_msg(StringPrintf("Array out of bounds in peekArray: %d/%d vs %d", - offset, count, array->GetLength())); - Runtime::Current()->AbortTransactionAndThrowAbortError(self, error_msg); + Runtime::Current()->GetClassLinker()->AbortTransactionF( + self, "Array out of bounds in peekArray: %d/%d vs %d", offset, count, array->GetLength()); return; } @@ -1589,8 +1595,9 @@ void UnstartedRuntime::UnstartedJdkUnsafeCompareAndSwapLong( int64_t newValue = shadow_frame->GetVRegLong(arg_offset + 6); bool success; // Check whether we're in a transaction, call accordingly. - if (Runtime::Current()->IsActiveTransaction()) { - if (!CheckWriteConstraint(self, obj)) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + if (runtime->GetClassLinker()->TransactionWriteConstraint(self, obj)) { DCHECK(self->IsExceptionPending()); return; } @@ -1635,8 +1642,10 @@ void UnstartedRuntime::UnstartedJdkUnsafeCompareAndSwapObject( } bool success; // Check whether we're in a transaction, call accordingly. - if (Runtime::Current()->IsActiveTransaction()) { - if (!CheckWriteConstraint(self, obj) || !CheckWriteValueConstraint(self, new_value)) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + if (runtime->GetClassLinker()->TransactionWriteConstraint(self, obj) || + runtime->GetClassLinker()->TransactionWriteValueConstraint(self, new_value)) { DCHECK(self->IsExceptionPending()); return; } @@ -1682,8 +1691,10 @@ void UnstartedRuntime::UnstartedJdkUnsafePutReferenceVolatile(Thread* self, } int64_t offset = shadow_frame->GetVRegLong(arg_offset + 2); mirror::Object* value = shadow_frame->GetVRegReference(arg_offset + 4); - if (Runtime::Current()->IsActiveTransaction()) { - if (!CheckWriteConstraint(self, obj) || !CheckWriteValueConstraint(self, value)) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + if (runtime->GetClassLinker()->TransactionWriteConstraint(self, obj) || + runtime->GetClassLinker()->TransactionWriteValueConstraint(self, value)) { DCHECK(self->IsExceptionPending()); return; } @@ -1707,8 +1718,10 @@ void UnstartedRuntime::UnstartedJdkUnsafePutOrderedObject(Thread* self, int64_t offset = shadow_frame->GetVRegLong(arg_offset + 2); mirror::Object* new_value = shadow_frame->GetVRegReference(arg_offset + 4); std::atomic_thread_fence(std::memory_order_release); - if (Runtime::Current()->IsActiveTransaction()) { - if (!CheckWriteConstraint(self, obj) || !CheckWriteValueConstraint(self, new_value)) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + if (runtime->GetClassLinker()->TransactionWriteConstraint(self, obj) || + runtime->GetClassLinker()->TransactionWriteValueConstraint(self, new_value)) { DCHECK(self->IsExceptionPending()); return; } @@ -2154,8 +2167,9 @@ void UnstartedRuntime::UnstartedJNIJdkUnsafeCompareAndSwapInt( jint expectedValue = args[3]; jint newValue = args[4]; bool success; - if (Runtime::Current()->IsActiveTransaction()) { - if (!CheckWriteConstraint(self, obj)) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + if (runtime->GetClassLinker()->TransactionWriteConstraint(self, obj)) { DCHECK(self->IsExceptionPending()); return; } @@ -2211,8 +2225,10 @@ void UnstartedRuntime::UnstartedJNIJdkUnsafePutReference(Thread* self, } jlong offset = (static_cast<uint64_t>(args[2]) << 32) | args[1]; ObjPtr<mirror::Object> new_value = reinterpret_cast32<mirror::Object*>(args[3]); - if (Runtime::Current()->IsActiveTransaction()) { - if (!CheckWriteConstraint(self, obj) || !CheckWriteValueConstraint(self, new_value)) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + if (runtime->GetClassLinker()->TransactionWriteConstraint(self, obj) || + runtime->GetClassLinker()->TransactionWriteValueConstraint(self, new_value)) { DCHECK(self->IsExceptionPending()); return; } @@ -2420,12 +2436,17 @@ void UnstartedRuntime::Jni(Thread* self, ArtMethod* method, mirror::Object* rece // Clear out the result in case it's not zeroed out. result->SetL(nullptr); (*iter->second)(self, method, receiver, args, result); - } else if (Runtime::Current()->IsActiveTransaction()) { - AbortTransactionF(self, "Attempt to invoke native method in non-started runtime: %s", - ArtMethod::PrettyMethod(method).c_str()); } else { - LOG(FATAL) << "Calling native method " << ArtMethod::PrettyMethod(method) << " in an unstarted " - "non-transactional runtime"; + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + runtime->GetClassLinker()->AbortTransactionF( + self, + "Attempt to invoke native method in non-started runtime: %s", + ArtMethod::PrettyMethod(method).c_str()); + } else { + LOG(FATAL) << "Calling native method " << ArtMethod::PrettyMethod(method) + << " in an unstarted non-transactional runtime"; + } } } diff --git a/runtime/interpreter/unstarted_runtime.h b/runtime/interpreter/unstarted_runtime.h index 60092ad958..8019c257d4 100644 --- a/runtime/interpreter/unstarted_runtime.h +++ b/runtime/interpreter/unstarted_runtime.h @@ -99,7 +99,7 @@ class UnstartedRuntime { static void InitializeInvokeHandlers(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); static void InitializeJNIHandlers(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); - friend class UnstartedRuntimeTest; + friend class UnstartedRuntimeTestBase; DISALLOW_ALLOCATION(); DISALLOW_COPY_AND_ASSIGN(UnstartedRuntime); diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index 494eb756a4..cf7782dcdc 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "unstarted_runtime.h" +#include "unstarted_runtime_test.h" #include <limits> #include <locale> @@ -25,11 +25,12 @@ #include "class_linker.h" #include "class_root-inl.h" #include "common_runtime_test.h" +#include "common_throws.h" #include "dex/descriptors_names.h" #include "dex/dex_instruction.h" #include "handle.h" #include "handle_scope-inl.h" -#include "interpreter/interpreter_common.h" +#include "interpreter_common.h" #include "mirror/array-alloc-inl.h" #include "mirror/class-alloc-inl.h" #include "mirror/class_loader.h" @@ -42,61 +43,13 @@ #include "scoped_thread_state_change-inl.h" #include "shadow_frame-inl.h" #include "thread.h" -#include "transaction.h" #include "unstarted_runtime_list.h" namespace art HIDDEN { namespace interpreter { -// Deleter to be used with ShadowFrame::CreateDeoptimizedFrame objects. -struct DeoptShadowFrameDelete { - // NOTE: Deleting a const object is valid but free() takes a non-const pointer. - void operator()(ShadowFrame* ptr) const { - if (ptr != nullptr) { - ShadowFrame::DeleteDeoptimizedFrame(ptr); - } - } -}; -// Alias for std::unique_ptr<> that uses the above deleter. -using UniqueDeoptShadowFramePtr = std::unique_ptr<ShadowFrame, DeoptShadowFrameDelete>; - -class UnstartedRuntimeTest : public CommonRuntimeTest { +class UnstartedRuntimeTest : public UnstartedRuntimeTestBase { protected: - // Re-expose all UnstartedRuntime implementations so we don't need to declare a million - // test friends. - - // Methods that intercept available libcore implementations. -#define UNSTARTED_DIRECT(Name, DescriptorIgnored, NameIgnored, SignatureIgnored) \ - static void Unstarted ## Name(Thread* self, \ - ShadowFrame* shadow_frame, \ - JValue* result, \ - size_t arg_offset) \ - REQUIRES_SHARED(Locks::mutator_lock_) { \ - interpreter::UnstartedRuntime::Unstarted ## Name(self, shadow_frame, result, arg_offset); \ - } - UNSTARTED_RUNTIME_DIRECT_LIST(UNSTARTED_DIRECT) -#undef UNSTARTED_DIRECT - - // Methods that are native. -#define UNSTARTED_JNI(Name, DescriptorIgnored, NameIgnored, SignatureIgnored) \ - static void UnstartedJNI ## Name(Thread* self, \ - ArtMethod* method, \ - mirror::Object* receiver, \ - uint32_t* args, \ - JValue* result) \ - REQUIRES_SHARED(Locks::mutator_lock_) { \ - interpreter::UnstartedRuntime::UnstartedJNI ## Name(self, method, receiver, args, result); \ - } - UNSTARTED_RUNTIME_JNI_LIST(UNSTARTED_JNI) -#undef UNSTARTED_JNI - - UniqueDeoptShadowFramePtr CreateShadowFrame(uint32_t num_vregs, - ArtMethod* method, - uint32_t dex_pc) { - return UniqueDeoptShadowFramePtr( - ShadowFrame::CreateDeoptimizedFrame(num_vregs, method, dex_pc)); - } - // Helpers for ArrayCopy. // // Note: as we have to use handles, we use StackHandleScope to transfer data. Hardcode a size @@ -210,16 +163,6 @@ class UnstartedRuntimeTest : public CommonRuntimeTest { EXPECT_EQ(expect_int64t, result_int64t) << result.GetD() << " vs " << test_pairs[i][1]; } } - - // Prepare for aborts. Aborts assume that the exception class is already resolved, as the - // loading code doesn't work under transactions. - void PrepareForAborts() REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr<mirror::Object> result = Runtime::Current()->GetClassLinker()->FindClass( - Thread::Current(), - Transaction::kAbortExceptionDescriptor, - ScopedNullHandle<mirror::ClassLoader>()); - CHECK(result != nullptr); - } }; TEST_F(UnstartedRuntimeTest, MemoryPeekByte) { @@ -396,8 +339,7 @@ TEST_F(UnstartedRuntimeTest, StringInit) { ScopedObjectAccess soa(self); ObjPtr<mirror::Class> klass = GetClassRoot<mirror::String>(); ArtMethod* method = - klass->FindConstructor("(Ljava/lang/String;)V", - Runtime::Current()->GetClassLinker()->GetImagePointerSize()); + klass->FindConstructor("(Ljava/lang/String;)V", class_linker_->GetImagePointerSize()); ASSERT_TRUE(method != nullptr); // create instruction data for invoke-direct {v0, v1} of method with fake index @@ -483,9 +425,6 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTest) { JValue result; UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0); - StackHandleScope<1> hs_object(self); - Handle<mirror::Class> object_class(hs_object.NewHandle(GetClassRoot<mirror::Object>())); - // Simple test: // [1,2,3]{1 @ 2} into [4,5,6] = [4,2,6] { @@ -507,8 +446,8 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTest) { RunArrayCopy(self, tmp.get(), false, - object_class.Get(), - object_class.Get(), + GetClassRoot<mirror::Object>(), + GetClassRoot<mirror::Object>(), hs_src, 1, hs_dst, @@ -538,7 +477,7 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTest) { RunArrayCopy(self, tmp.get(), false, - object_class.Get(), + GetClassRoot<mirror::Object>(), GetClassRoot<mirror::String>(), hs_src, 1, @@ -569,7 +508,7 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTest) { RunArrayCopy(self, tmp.get(), true, - object_class.Get(), + GetClassRoot<mirror::Object>(), GetClassRoot<mirror::String>(), hs_src, 0, @@ -773,51 +712,6 @@ TEST_F(UnstartedRuntimeTest, ToLowerUpper) { } } } - - // Check abort for other things. Can't test all. - - PrepareForAborts(); - - for (uint32_t i = 128; i < 256; ++i) { - { - JValue result; - tmp->SetVReg(0, static_cast<int32_t>(i)); - EnterTransactionMode(); - UnstartedCharacterToLowerCase(self, tmp.get(), &result, 0); - ASSERT_TRUE(IsTransactionAborted()); - ExitTransactionMode(); - ASSERT_TRUE(self->IsExceptionPending()); - } - { - JValue result; - tmp->SetVReg(0, static_cast<int32_t>(i)); - EnterTransactionMode(); - UnstartedCharacterToUpperCase(self, tmp.get(), &result, 0); - ASSERT_TRUE(IsTransactionAborted()); - ExitTransactionMode(); - ASSERT_TRUE(self->IsExceptionPending()); - } - } - for (uint64_t i = 256; i <= std::numeric_limits<uint32_t>::max(); i <<= 1) { - { - JValue result; - tmp->SetVReg(0, static_cast<int32_t>(i)); - EnterTransactionMode(); - UnstartedCharacterToLowerCase(self, tmp.get(), &result, 0); - ASSERT_TRUE(IsTransactionAborted()); - ExitTransactionMode(); - ASSERT_TRUE(self->IsExceptionPending()); - } - { - JValue result; - tmp->SetVReg(0, static_cast<int32_t>(i)); - EnterTransactionMode(); - UnstartedCharacterToUpperCase(self, tmp.get(), &result, 0); - ASSERT_TRUE(IsTransactionAborted()); - ExitTransactionMode(); - ASSERT_TRUE(self->IsExceptionPending()); - } - } } TEST_F(UnstartedRuntimeTest, Sin) { @@ -937,21 +831,20 @@ TEST_F(UnstartedRuntimeTest, ThreadLocalGet) { UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0); StackHandleScope<1> hs(self); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // Positive test. See that We get something for float conversion. { Handle<mirror::Class> floating_decimal = hs.NewHandle( - class_linker->FindClass(self, - "Ljdk/internal/math/FloatingDecimal;", - ScopedNullHandle<mirror::ClassLoader>())); + class_linker_->FindClass(self, + "Ljdk/internal/math/FloatingDecimal;", + ScopedNullHandle<mirror::ClassLoader>())); ASSERT_TRUE(floating_decimal != nullptr); - ASSERT_TRUE(class_linker->EnsureInitialized(self, floating_decimal, true, true)); + ASSERT_TRUE(class_linker_->EnsureInitialized(self, floating_decimal, true, true)); ArtMethod* caller_method = floating_decimal->FindClassMethod( "getBinaryToASCIIBuffer", "()Ljdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;", - class_linker->GetImagePointerSize()); + class_linker_->GetImagePointerSize()); // floating_decimal->DumpClass(LOG_STREAM(ERROR), mirror::Class::kDumpClassFullDetail); ASSERT_TRUE(caller_method != nullptr); ASSERT_TRUE(caller_method->IsDirect()); @@ -965,27 +858,6 @@ TEST_F(UnstartedRuntimeTest, ThreadLocalGet) { shadow_frame->ClearLink(); } - - // Negative test. - PrepareForAborts(); - - { - // Just use a method in Class. - ObjPtr<mirror::Class> class_class = GetClassRoot<mirror::Class>(); - ArtMethod* caller_method = - &*class_class->GetDeclaredMethods(class_linker->GetImagePointerSize()).begin(); - UniqueDeoptShadowFramePtr caller_frame = CreateShadowFrame(10, caller_method, 0); - shadow_frame->SetLink(caller_frame.get()); - - EnterTransactionMode(); - UnstartedThreadLocalGet(self, shadow_frame.get(), &result, 0); - ASSERT_TRUE(IsTransactionAborted()); - ExitTransactionMode(); - ASSERT_TRUE(self->IsExceptionPending()); - self->ClearException(); - - shadow_frame->ClearLink(); - } } TEST_F(UnstartedRuntimeTest, FloatConversion) { @@ -993,17 +865,16 @@ TEST_F(UnstartedRuntimeTest, FloatConversion) { ScopedObjectAccess soa(self); StackHandleScope<1> hs(self); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> double_class = hs.NewHandle( - class_linker->FindClass(self, - "Ljava/lang/Double;", - ScopedNullHandle<mirror::ClassLoader>())); + class_linker_->FindClass(self, + "Ljava/lang/Double;", + ScopedNullHandle<mirror::ClassLoader>())); ASSERT_TRUE(double_class != nullptr); - ASSERT_TRUE(class_linker->EnsureInitialized(self, double_class, true, true)); + ASSERT_TRUE(class_linker_->EnsureInitialized(self, double_class, true, true)); ArtMethod* method = double_class->FindClassMethod("toString", "(D)Ljava/lang/String;", - class_linker->GetImagePointerSize()); + class_linker_->GetImagePointerSize()); ASSERT_TRUE(method != nullptr); ASSERT_TRUE(method->IsDirect()); ASSERT_TRUE(method->GetDeclaringClass() == double_class.Get()); @@ -1029,62 +900,26 @@ TEST_F(UnstartedRuntimeTest, FloatConversion) { EXPECT_EQ("1.23", mod_utf); } -TEST_F(UnstartedRuntimeTest, ThreadCurrentThread) { - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - - JValue result; - UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0); - - StackHandleScope<1> hs(self); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Handle<mirror::Class> thread_class = hs.NewHandle( - class_linker->FindClass(self, "Ljava/lang/Thread;", ScopedNullHandle<mirror::ClassLoader>())); - ASSERT_TRUE(thread_class.Get() != nullptr); - ASSERT_TRUE(class_linker->EnsureInitialized(self, thread_class, true, true)); - - // Negative test. In general, currentThread should fail (as we should not leak a peer that will - // be recreated at runtime). - PrepareForAborts(); - - { - EnterTransactionMode(); - UnstartedThreadCurrentThread(self, shadow_frame.get(), &result, 0); - ASSERT_TRUE(IsTransactionAborted()); - ExitTransactionMode(); - ASSERT_TRUE(self->IsExceptionPending()); - self->ClearException(); - } -} - TEST_F(UnstartedRuntimeTest, LogManager) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); StackHandleScope<1> hs(self); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Handle<mirror::Class> log_manager_class = hs.NewHandle( - class_linker->FindClass(self, - "Ljava/util/logging/LogManager;", - ScopedNullHandle<mirror::ClassLoader>())); + Handle<mirror::Class> log_manager_class = hs.NewHandle(class_linker_->FindClass( + self, "Ljava/util/logging/LogManager;", ScopedNullHandle<mirror::ClassLoader>())); ASSERT_TRUE(log_manager_class.Get() != nullptr); - ASSERT_TRUE(class_linker->EnsureInitialized(self, log_manager_class, true, true)); + ASSERT_TRUE(class_linker_->EnsureInitialized(self, log_manager_class, true, true)); } class UnstartedClassForNameTest : public UnstartedRuntimeTest { public: template <typename T> - void RunTest(T& runner, bool in_transaction, bool should_succeed) { + void RunTest(T&& runner) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); // Ensure that Class is initialized. - { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - StackHandleScope<1> hs(self); - Handle<mirror::Class> h_class = hs.NewHandle(GetClassRoot<mirror::Class>()); - CHECK(class_linker->EnsureInitialized(self, h_class, true, true)); - } + CHECK(GetClassRoot<mirror::Class>()->IsInitialized()); // A selection of classes from different core classpath components. constexpr const char* kTestCases[] = { @@ -1092,99 +927,20 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest { "dalvik.system.ClassExt", // From libart. }; - if (in_transaction) { - // For transaction mode, we cannot load any classes, as the pre-fence initialization of - // classes isn't transactional. Load them ahead of time. - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - for (const char* name : kTestCases) { - class_linker->FindClass(self, - DotToDescriptor(name).c_str(), - ScopedNullHandle<mirror::ClassLoader>()); - CHECK(!self->IsExceptionPending()) << self->GetException()->Dump(); - } - } - - if (!should_succeed) { - // Negative test. In general, currentThread should fail (as we should not leak a peer that will - // be recreated at runtime). - PrepareForAborts(); - } - JValue result; UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0); for (const char* name : kTestCases) { ObjPtr<mirror::String> name_string = mirror::String::AllocFromModifiedUtf8(self, name); CHECK(name_string != nullptr); - - if (in_transaction) { - StackHandleScope<1> hs(self); - HandleWrapperObjPtr<mirror::String> h(hs.NewHandleWrapper(&name_string)); - EnterTransactionMode(); - } CHECK(!self->IsExceptionPending()); runner(self, shadow_frame.get(), name_string, &result); - if (should_succeed) { - CHECK(!self->IsExceptionPending()) << name << " " << self->GetException()->Dump(); - CHECK(result.GetL() != nullptr) << name; - } else { - CHECK(self->IsExceptionPending()) << name; - if (in_transaction) { - ASSERT_TRUE(IsTransactionAborted()); - } - self->ClearException(); - } - - if (in_transaction) { - ExitTransactionMode(); - } + CHECK(!self->IsExceptionPending()) << name << " " << self->GetException()->Dump(); + CHECK(result.GetL() != nullptr) << name; } } - - mirror::ClassLoader* GetBootClassLoader() REQUIRES_SHARED(Locks::mutator_lock_) { - Thread* self = Thread::Current(); - StackHandleScope<2> hs(self); - MutableHandle<mirror::ClassLoader> boot_cp = hs.NewHandle<mirror::ClassLoader>(nullptr); - - { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - - // Create the fake boot classloader. Any instance is fine, they are technically interchangeable. - Handle<mirror::Class> boot_cp_class = hs.NewHandle( - class_linker->FindClass(self, - "Ljava/lang/BootClassLoader;", - ScopedNullHandle<mirror::ClassLoader>())); - CHECK(boot_cp_class != nullptr); - CHECK(class_linker->EnsureInitialized(self, boot_cp_class, true, true)); - - boot_cp.Assign(boot_cp_class->AllocObject(self)->AsClassLoader()); - CHECK(boot_cp != nullptr); - - ArtMethod* boot_cp_init = boot_cp_class->FindConstructor( - "()V", class_linker->GetImagePointerSize()); - CHECK(boot_cp_init != nullptr); - - JValue result; - UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, boot_cp_init, 0); - shadow_frame->SetVRegReference(0, boot_cp.Get()); - - // create instruction data for invoke-direct {v0} of method with fake index - uint16_t inst_data[3] = { 0x1070, 0x0000, 0x0010 }; - - interpreter::DoCall<false>(boot_cp_init, - self, - *shadow_frame, - Instruction::At(inst_data), - inst_data[0], - /* string_init= */ false, - &result); - CHECK(!self->IsExceptionPending()); - } - - return boot_cp.Get(); - } }; TEST_F(UnstartedClassForNameTest, ClassForName) { @@ -1195,7 +951,7 @@ TEST_F(UnstartedClassForNameTest, ClassForName) { shadow_frame->SetVRegReference(0, name); UnstartedClassForName(self, shadow_frame, result, 0); }; - RunTest(runner, false, true); + RunTest(runner); } TEST_F(UnstartedClassForNameTest, ClassForNameLong) { @@ -1208,7 +964,7 @@ TEST_F(UnstartedClassForNameTest, ClassForNameLong) { shadow_frame->SetVRegReference(2, nullptr); UnstartedClassForNameLong(self, shadow_frame, result, 0); }; - RunTest(runner, false, true); + RunTest(runner); } TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoader) { @@ -1227,50 +983,7 @@ TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoader) { shadow_frame->SetVRegReference(2, boot_cp.Get()); UnstartedClassForNameLong(th, shadow_frame, result, 0); }; - RunTest(runner, false, true); -} - -TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoaderTransaction) { - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - - StackHandleScope<1> hs(self); - Handle<mirror::ClassLoader> boot_cp = hs.NewHandle(GetBootClassLoader()); - - auto runner = [&](Thread* th, - ShadowFrame* shadow_frame, - ObjPtr<mirror::String> name, - JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { - shadow_frame->SetVRegReference(0, name); - shadow_frame->SetVReg(1, 0); - shadow_frame->SetVRegReference(2, boot_cp.Get()); - UnstartedClassForNameLong(th, shadow_frame, result, 0); - }; - RunTest(runner, true, true); -} - -TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoaderFail) { - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - - StackHandleScope<2> hs(self); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - jobject path_jobj = class_linker->CreatePathClassLoader(self, {}); - ASSERT_TRUE(path_jobj != nullptr); - Handle<mirror::ClassLoader> path_cp = hs.NewHandle<mirror::ClassLoader>( - self->DecodeJObject(path_jobj)->AsClassLoader()); - - auto runner = [&](Thread* th, - ShadowFrame* shadow_frame, - ObjPtr<mirror::String> name, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - shadow_frame->SetVRegReference(0, name); - shadow_frame->SetVReg(1, 0); - shadow_frame->SetVRegReference(2, path_cp.Get()); - UnstartedClassForNameLong(th, shadow_frame, result, 0); - }; - RunTest(runner, true, false); + RunTest(runner); } TEST_F(UnstartedRuntimeTest, ClassGetSignatureAnnotation) { @@ -1278,13 +991,12 @@ TEST_F(UnstartedRuntimeTest, ClassGetSignatureAnnotation) { ScopedObjectAccess soa(self); StackHandleScope<1> hs(self); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> list_class = hs.NewHandle( - class_linker->FindClass(self, - "Ljava/util/List;", - ScopedNullHandle<mirror::ClassLoader>())); + class_linker_->FindClass(self, + "Ljava/util/List;", + ScopedNullHandle<mirror::ClassLoader>())); ASSERT_TRUE(list_class.Get() != nullptr); - ASSERT_TRUE(class_linker->EnsureInitialized(self, list_class, true, true)); + ASSERT_TRUE(class_linker_->EnsureInitialized(self, list_class, true, true)); JValue result; UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0); @@ -1315,17 +1027,16 @@ TEST_F(UnstartedRuntimeTest, ConstructorNewInstance0) { ScopedObjectAccess soa(self); StackHandleScope<4> hs(self); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // Get Throwable. Handle<mirror::Class> throw_class = hs.NewHandle(GetClassRoot<mirror::Throwable>()); - ASSERT_TRUE(class_linker->EnsureInitialized(self, throw_class, true, true)); + ASSERT_TRUE(class_linker_->EnsureInitialized(self, throw_class, true, true)); // Get an input object. Handle<mirror::String> input = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "abd")); // Find the constructor. - PointerSize pointer_size = class_linker->GetImagePointerSize(); + PointerSize pointer_size = class_linker_->GetImagePointerSize(); ArtMethod* throw_cons = throw_class->FindConstructor("(Ljava/lang/String;)V", pointer_size); ASSERT_TRUE(throw_cons != nullptr); Handle<mirror::Constructor> cons = hs.NewHandle((pointer_size == PointerSize::k64) diff --git a/runtime/interpreter/unstarted_runtime_test.h b/runtime/interpreter/unstarted_runtime_test.h new file mode 100644 index 0000000000..465a4268ea --- /dev/null +++ b/runtime/interpreter/unstarted_runtime_test.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_INTERPRETER_UNSTARTED_RUNTIME_TEST_H_ +#define ART_RUNTIME_INTERPRETER_UNSTARTED_RUNTIME_TEST_H_ + +#include "unstarted_runtime.h" + +#include <memory> + +#include "class_linker-inl.h" +#include "common_runtime_test.h" +#include "handle.h" +#include "handle_scope-inl.h" +#include "interpreter_common.h" +#include "mirror/object_array-alloc-inl.h" +#include "mirror/object_array-inl.h" +#include "shadow_frame-inl.h" + +namespace art HIDDEN { +namespace interpreter { + +// Deleter to be used with ShadowFrame::CreateDeoptimizedFrame objects. +struct DeoptShadowFrameDelete { + // NOTE: Deleting a const object is valid but free() takes a non-const pointer. + void operator()(ShadowFrame* ptr) const { + ShadowFrame::DeleteDeoptimizedFrame(ptr); + } +}; +// Alias for std::unique_ptr<> that uses the above deleter. +using UniqueDeoptShadowFramePtr = std::unique_ptr<ShadowFrame, DeoptShadowFrameDelete>; + +class UnstartedRuntimeTestBase : public CommonRuntimeTest { + protected: + // Re-expose all UnstartedRuntime implementations so we don't need to declare a million + // test friends. + + // Methods that intercept available libcore implementations. +#define UNSTARTED_DIRECT(Name, DescriptorIgnored, NameIgnored, SignatureIgnored) \ + static void Unstarted ## Name(Thread* self, \ + ShadowFrame* shadow_frame, \ + JValue* result, \ + size_t arg_offset) \ + REQUIRES_SHARED(Locks::mutator_lock_) { \ + interpreter::UnstartedRuntime::Unstarted ## Name(self, shadow_frame, result, arg_offset); \ + } + UNSTARTED_RUNTIME_DIRECT_LIST(UNSTARTED_DIRECT) +#undef UNSTARTED_DIRECT + + // Methods that are native. +#define UNSTARTED_JNI(Name, DescriptorIgnored, NameIgnored, SignatureIgnored) \ + static void UnstartedJNI ## Name(Thread* self, \ + ArtMethod* method, \ + mirror::Object* receiver, \ + uint32_t* args, \ + JValue* result) \ + REQUIRES_SHARED(Locks::mutator_lock_) { \ + interpreter::UnstartedRuntime::UnstartedJNI ## Name(self, method, receiver, args, result); \ + } + UNSTARTED_RUNTIME_JNI_LIST(UNSTARTED_JNI) +#undef UNSTARTED_JNI + + UniqueDeoptShadowFramePtr CreateShadowFrame(uint32_t num_vregs, + ArtMethod* method, + uint32_t dex_pc) { + return UniqueDeoptShadowFramePtr( + ShadowFrame::CreateDeoptimizedFrame(num_vregs, method, dex_pc)); + } + + mirror::ClassLoader* GetBootClassLoader() REQUIRES_SHARED(Locks::mutator_lock_) { + Thread* self = Thread::Current(); + StackHandleScope<2> hs(self); + + // Create the fake boot classloader. Any instance is fine, they are technically interchangeable. + Handle<mirror::Class> boot_cp_class = hs.NewHandle(class_linker_->FindClass( + self, "Ljava/lang/BootClassLoader;", ScopedNullHandle<mirror::ClassLoader>())); + CHECK(boot_cp_class != nullptr); + CHECK(class_linker_->EnsureInitialized( + self, boot_cp_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true)); + + Handle<mirror::ClassLoader> boot_cp = + hs.NewHandle(boot_cp_class->AllocObject(self)->AsClassLoader()); + CHECK(boot_cp != nullptr); + + ArtMethod* boot_cp_init = + boot_cp_class->FindConstructor("()V", class_linker_->GetImagePointerSize()); + CHECK(boot_cp_init != nullptr); + + JValue result; + UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, boot_cp_init, 0); + shadow_frame->SetVRegReference(0, boot_cp.Get()); + + // create instruction data for invoke-direct {v0} of method with fake index + uint16_t inst_data[3] = { 0x1070, 0x0000, 0x0010 }; + + interpreter::DoCall<false>(boot_cp_init, + self, + *shadow_frame, + Instruction::At(inst_data), + inst_data[0], + /* string_init= */ false, + &result); + CHECK(!self->IsExceptionPending()); + + return boot_cp.Get(); + } +}; + +} // namespace interpreter +} // namespace art + +#endif // ART_RUNTIME_INTERPRETER_UNSTARTED_RUNTIME_TEST_H_ diff --git a/runtime/interpreter/unstarted_runtime_transaction_test.cc b/runtime/interpreter/unstarted_runtime_transaction_test.cc new file mode 100644 index 0000000000..abbcbbb12e --- /dev/null +++ b/runtime/interpreter/unstarted_runtime_transaction_test.cc @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "unstarted_runtime_test.h" + +#include "class_root-inl.h" +#include "common_transaction_test.h" +#include "dex/descriptors_names.h" +#include "interpreter/interpreter_common.h" +#include "handle.h" +#include "handle_scope-inl.h" +#include "mirror/class-inl.h" + +namespace art HIDDEN { +namespace interpreter { + +class UnstartedRuntimeTransactionTest : public CommonTransactionTestBase<UnstartedRuntimeTestBase> { + protected: + // Prepare for aborts. Aborts assume that the exception class is already resolved, as the + // loading code doesn't work under transactions. + void PrepareForAborts() REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr<mirror::Object> result = Runtime::Current()->GetClassLinker()->FindClass( + Thread::Current(), + kTransactionAbortErrorDescriptor, + ScopedNullHandle<mirror::ClassLoader>()); + CHECK(result != nullptr); + } +}; + +TEST_F(UnstartedRuntimeTransactionTest, ToLowerUpper) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0); + + PrepareForAborts(); + + for (uint32_t i = 128; i < 256; ++i) { + { + JValue result; + tmp->SetVReg(0, static_cast<int32_t>(i)); + EnterTransactionMode(); + UnstartedCharacterToLowerCase(self, tmp.get(), &result, 0); + ASSERT_TRUE(IsTransactionAborted()); + ExitTransactionMode(); + ASSERT_TRUE(self->IsExceptionPending()); + } + { + JValue result; + tmp->SetVReg(0, static_cast<int32_t>(i)); + EnterTransactionMode(); + UnstartedCharacterToUpperCase(self, tmp.get(), &result, 0); + ASSERT_TRUE(IsTransactionAborted()); + ExitTransactionMode(); + ASSERT_TRUE(self->IsExceptionPending()); + } + } + for (uint64_t i = 256; i <= std::numeric_limits<uint32_t>::max(); i <<= 1) { + { + JValue result; + tmp->SetVReg(0, static_cast<int32_t>(i)); + EnterTransactionMode(); + UnstartedCharacterToLowerCase(self, tmp.get(), &result, 0); + ASSERT_TRUE(IsTransactionAborted()); + ExitTransactionMode(); + ASSERT_TRUE(self->IsExceptionPending()); + } + { + JValue result; + tmp->SetVReg(0, static_cast<int32_t>(i)); + EnterTransactionMode(); + UnstartedCharacterToUpperCase(self, tmp.get(), &result, 0); + ASSERT_TRUE(IsTransactionAborted()); + ExitTransactionMode(); + ASSERT_TRUE(self->IsExceptionPending()); + } + } +} + +TEST_F(UnstartedRuntimeTransactionTest, ThreadLocalGet) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0); + + // Negative test. + PrepareForAborts(); + + // Just use a method in Class. + ObjPtr<mirror::Class> class_class = GetClassRoot<mirror::Class>(); + ArtMethod* caller_method = + &*class_class->GetDeclaredMethods(class_linker_->GetImagePointerSize()).begin(); + UniqueDeoptShadowFramePtr caller_frame = CreateShadowFrame(10, caller_method, 0); + shadow_frame->SetLink(caller_frame.get()); + + JValue result; + EnterTransactionMode(); + UnstartedThreadLocalGet(self, shadow_frame.get(), &result, 0); + ASSERT_TRUE(IsTransactionAborted()); + ExitTransactionMode(); + ASSERT_TRUE(self->IsExceptionPending()); + self->ClearException(); + + shadow_frame->ClearLink(); +} + +TEST_F(UnstartedRuntimeTransactionTest, ThreadCurrentThread) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0); + + // Negative test. In general, currentThread should fail (as we should not leak a peer that will + // be recreated at runtime). + PrepareForAborts(); + + JValue result; + EnterTransactionMode(); + UnstartedThreadCurrentThread(self, shadow_frame.get(), &result, 0); + ASSERT_TRUE(IsTransactionAborted()); + ExitTransactionMode(); + ASSERT_TRUE(self->IsExceptionPending()); + self->ClearException(); +} + +class UnstartedClassForNameTransactionTest : public UnstartedRuntimeTransactionTest { + public: + template <typename T> + void RunTest(T&& runner, bool should_succeed) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + // Ensure that Class is initialized. + CHECK(GetClassRoot<mirror::Class>()->IsInitialized()); + + // A selection of classes from different core classpath components. + constexpr const char* kTestCases[] = { + "java.net.CookieManager", // From libcore. + "dalvik.system.ClassExt", // From libart. + }; + + // For transaction mode, we cannot load any classes, as the pre-fence initialization of + // classes isn't transactional. Load them ahead of time. + for (const char* name : kTestCases) { + class_linker_->FindClass(self, + DotToDescriptor(name).c_str(), + ScopedNullHandle<mirror::ClassLoader>()); + CHECK(!self->IsExceptionPending()) << self->GetException()->Dump(); + } + + if (!should_succeed) { + // Negative test. In general, currentThread should fail (as we should not leak a peer that will + // be recreated at runtime). + PrepareForAborts(); + } + + JValue result; + UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0); + + for (const char* name : kTestCases) { + EnterTransactionMode(); + + ObjPtr<mirror::String> name_string = mirror::String::AllocFromModifiedUtf8(self, name); + CHECK(name_string != nullptr); + CHECK(!self->IsExceptionPending()); + + runner(self, shadow_frame.get(), name_string, &result); + + if (should_succeed) { + CHECK(!self->IsExceptionPending()) << name << " " << self->GetException()->Dump(); + CHECK(result.GetL() != nullptr) << name; + } else { + CHECK(self->IsExceptionPending()) << name; + ASSERT_TRUE(IsTransactionAborted()); + self->ClearException(); + } + + ExitTransactionMode(); + } + } +}; + +TEST_F(UnstartedClassForNameTransactionTest, ClassForNameLongWithClassLoader) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> boot_cp = hs.NewHandle(GetBootClassLoader()); + + auto runner = [&](Thread* th, + ShadowFrame* shadow_frame, + ObjPtr<mirror::String> name, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + shadow_frame->SetVRegReference(0, name); + shadow_frame->SetVReg(1, 0); + shadow_frame->SetVRegReference(2, boot_cp.Get()); + UnstartedClassForNameLong(th, shadow_frame, result, 0); + }; + RunTest(runner, /*should_succeed=*/ true); +} + +TEST_F(UnstartedClassForNameTransactionTest, ClassForNameLongWithClassLoaderFail) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + StackHandleScope<2> hs(self); + jobject path_jobj = class_linker_->CreatePathClassLoader(self, {}); + ASSERT_TRUE(path_jobj != nullptr); + Handle<mirror::ClassLoader> path_cp = hs.NewHandle<mirror::ClassLoader>( + self->DecodeJObject(path_jobj)->AsClassLoader()); + + auto runner = [&](Thread* th, + ShadowFrame* shadow_frame, + ObjPtr<mirror::String> name, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { + shadow_frame->SetVRegReference(0, name); + shadow_frame->SetVReg(1, 0); + shadow_frame->SetVRegReference(2, path_cp.Get()); + UnstartedClassForNameLong(th, shadow_frame, result, 0); + }; + RunTest(runner, /*should_succeed=*/ false); +} + +} // namespace interpreter +} // namespace art diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index cb80b4151d..4d395c8528 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -17,6 +17,7 @@ #include "jit.h" #include <dlfcn.h> +#include <sys/resource.h> #include "art_method-inl.h" #include "base/file_utils.h" @@ -1487,6 +1488,13 @@ static void* RunPollingThread(void* arg) { /* create_peer= */ false); CHECK(thread_attached); + if (getpriority(PRIO_PROCESS, 0 /* this thread */) == 0) { + // Slightly reduce thread priority, mostly so the suspend logic notices that we're + // not a high priority thread, and can time out more slowly. May fail on host. + (void)setpriority(PRIO_PROCESS, 0 /* this thread */, 1); + } else { + PLOG(ERROR) << "Unexpected BootImagePollingThread priority: " << getpriority(PRIO_PROCESS, 0); + } { // Prevent other threads from running while we are remapping the boot image // ArtMethod's. Native threads might still be running, but they cannot diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 378e6cba97..e6db9817df 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -447,8 +447,7 @@ void JitCodeCache::SweepRootTables(IsMarkedVisitor* visitor) { } } // Walk over inline caches to clear entries containing unloaded classes. - for (auto it : profiling_infos_) { - ProfilingInfo* info = it.second; + for (const auto& [_, info] : profiling_infos_) { InlineCache* caches = info->GetInlineCaches(); for (size_t i = 0; i < info->number_of_inline_caches_; ++i) { InlineCache* cache = &caches[i]; @@ -492,7 +491,6 @@ void JitCodeCache::FreeAllMethodHeaders( } { - MutexLock mu(Thread::Current(), *Locks::jit_lock_); ScopedCodeCacheWrite scc(private_region_); for (const OatQuickMethodHeader* method_header : method_headers) { FreeCodeAndData(method_header->GetCode()); @@ -507,24 +505,21 @@ void JitCodeCache::FreeAllMethodHeaders( if (kIsDebugBuild && !Runtime::Current()->IsZygote()) { std::map<const void*, ArtMethod*> compiled_methods; std::set<const void*> debug_info; - { - MutexLock mu(Thread::Current(), *Locks::jit_lock_); - VisitAllMethods([&](const void* addr, ArtMethod* method) { - if (!IsInZygoteExecSpace(addr)) { - CHECK(addr != nullptr && method != nullptr); - compiled_methods.emplace(addr, method); - } - }); - ForEachNativeDebugSymbol([&](const void* addr, size_t, const char* name) { - addr = AlignDown(addr, GetInstructionSetInstructionAlignment(kRuntimeISA)); // Thumb-bit. - bool res = debug_info.emplace(addr).second; - CHECK(res) << "Duplicate debug info: " << addr << " " << name; - CHECK_EQ(compiled_methods.count(addr), 1u) << "Extra debug info: " << addr << " " << name; - }); - } + VisitAllMethods([&](const void* addr, ArtMethod* method) { + if (!IsInZygoteExecSpace(addr)) { + CHECK(addr != nullptr && method != nullptr); + compiled_methods.emplace(addr, method); + } + }); + ForEachNativeDebugSymbol([&](const void* addr, size_t, const char* name) { + addr = AlignDown(addr, GetInstructionSetInstructionAlignment(kRuntimeISA)); // Thumb-bit. + bool res = debug_info.emplace(addr).second; + CHECK(res) << "Duplicate debug info: " << addr << " " << name; + CHECK_EQ(compiled_methods.count(addr), 1u) << "Extra debug info: " << addr << " " << name; + }); if (!debug_info.empty()) { // If debug-info generation is enabled. - for (auto it : compiled_methods) { - CHECK_EQ(debug_info.count(it.first), 1u) << "Missing debug info"; + for (const auto& [addr, method] : compiled_methods) { + CHECK_EQ(debug_info.count(addr), 1u) << "Mising debug info"; } CHECK_EQ(compiled_methods.size(), debug_info.size()); } @@ -539,68 +534,66 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { // for entries in this set. And it's more efficient to iterate through // the CHA dependency map just once with an unordered_set. std::unordered_set<OatQuickMethodHeader*> method_headers; + MutexLock mu(self, *Locks::jit_lock_); + // We do not check if a code cache GC is in progress, as this method comes + // with the classlinker_classes_lock_ held, and suspending ourselves could + // lead to a deadlock. { - MutexLock mu(self, *Locks::jit_lock_); - // We do not check if a code cache GC is in progress, as this method comes - // with the classlinker_classes_lock_ held, and suspending ourselves could - // lead to a deadlock. - { - for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) { - it->second.RemoveMethodsIn(alloc); - if (it->second.GetMethods().empty()) { - method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->second.GetCode())); - it = jni_stubs_map_.erase(it); - } else { - it->first.UpdateShorty(it->second.GetMethods().front()); - ++it; - } - } - for (auto it = zombie_jni_code_.begin(); it != zombie_jni_code_.end();) { - if (alloc.ContainsUnsafe(*it)) { - it = zombie_jni_code_.erase(it); - } else { - ++it; - } - } - for (auto it = processed_zombie_jni_code_.begin(); it != processed_zombie_jni_code_.end();) { - if (alloc.ContainsUnsafe(*it)) { - it = processed_zombie_jni_code_.erase(it); - } else { - ++it; - } + for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) { + it->second.RemoveMethodsIn(alloc); + if (it->second.GetMethods().empty()) { + method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->second.GetCode())); + it = jni_stubs_map_.erase(it); + } else { + it->first.UpdateShorty(it->second.GetMethods().front()); + ++it; } - for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { - if (alloc.ContainsUnsafe(it->second)) { - method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first)); - VLOG(jit) << "JIT removed " << it->second->PrettyMethod() << ": " << it->first; - it = method_code_map_.erase(it); - zombie_code_.erase(it->first); - processed_zombie_code_.erase(it->first); - } else { - ++it; - } + } + for (auto it = zombie_jni_code_.begin(); it != zombie_jni_code_.end();) { + if (alloc.ContainsUnsafe(*it)) { + it = zombie_jni_code_.erase(it); + } else { + ++it; } } - for (auto it = osr_code_map_.begin(); it != osr_code_map_.end();) { - DCHECK(!ContainsElement(zombie_code_, it->second)); - if (alloc.ContainsUnsafe(it->first)) { - // Note that the code has already been pushed to method_headers in the loop - // above and is going to be removed in FreeCode() below. - it = osr_code_map_.erase(it); + for (auto it = processed_zombie_jni_code_.begin(); it != processed_zombie_jni_code_.end();) { + if (alloc.ContainsUnsafe(*it)) { + it = processed_zombie_jni_code_.erase(it); } else { ++it; } } - for (auto it = profiling_infos_.begin(); it != profiling_infos_.end();) { - ProfilingInfo* info = it->second; - if (alloc.ContainsUnsafe(info->GetMethod())) { - private_region_.FreeWritableData(reinterpret_cast<uint8_t*>(info)); - it = profiling_infos_.erase(it); + for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { + if (alloc.ContainsUnsafe(it->second)) { + method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first)); + VLOG(jit) << "JIT removed " << it->second->PrettyMethod() << ": " << it->first; + it = method_code_map_.erase(it); + zombie_code_.erase(it->first); + processed_zombie_code_.erase(it->first); } else { ++it; } } } + for (auto it = osr_code_map_.begin(); it != osr_code_map_.end();) { + DCHECK(!ContainsElement(zombie_code_, it->second)); + if (alloc.ContainsUnsafe(it->first)) { + // Note that the code has already been pushed to method_headers in the loop + // above and is going to be removed in FreeCode() below. + it = osr_code_map_.erase(it); + } else { + ++it; + } + } + for (auto it = profiling_infos_.begin(); it != profiling_infos_.end();) { + ProfilingInfo* info = it->second; + if (alloc.ContainsUnsafe(info->GetMethod())) { + private_region_.FreeWritableData(reinterpret_cast<uint8_t*>(info)); + it = profiling_infos_.erase(it); + } else { + ++it; + } + } FreeAllMethodHeaders(method_headers); } @@ -1105,7 +1098,11 @@ bool JitCodeCache::IsAtMaxCapacity() const { } void JitCodeCache::IncreaseCodeCacheCapacity(Thread* self) { + ScopedThreadSuspension sts(self, ThreadState::kSuspended); MutexLock mu(self, *Locks::jit_lock_); + // Wait for a potential collection, as the size of the bitmap used by that collection + // is of the current capacity. + WaitForPotentialCollectionToComplete(self); private_region_.IncreaseCodeCacheCapacity(); } @@ -1113,56 +1110,53 @@ void JitCodeCache::RemoveUnmarkedCode(Thread* self) { ScopedTrace trace(__FUNCTION__); std::unordered_set<OatQuickMethodHeader*> method_headers; ScopedDebugDisallowReadBarriers sddrb(self); - { - MutexLock mu(self, *Locks::jit_lock_); - // Iterate over all zombie code and remove entries that are not marked. - for (auto it = processed_zombie_code_.begin(); it != processed_zombie_code_.end();) { - const void* code_ptr = *it; - uintptr_t allocation = FromCodeToAllocation(code_ptr); - DCHECK(!IsInZygoteExecSpace(code_ptr)); - if (GetLiveBitmap()->Test(allocation)) { - ++it; - } else { - OatQuickMethodHeader* header = OatQuickMethodHeader::FromCodePointer(code_ptr); - method_headers.insert(header); - method_code_map_.erase(header->GetCode()); - VLOG(jit) << "JIT removed " << *it; - it = processed_zombie_code_.erase(it); - } + MutexLock mu(self, *Locks::jit_lock_); + // Iterate over all zombie code and remove entries that are not marked. + for (auto it = processed_zombie_code_.begin(); it != processed_zombie_code_.end();) { + const void* code_ptr = *it; + uintptr_t allocation = FromCodeToAllocation(code_ptr); + DCHECK(!IsInZygoteExecSpace(code_ptr)); + if (GetLiveBitmap()->Test(allocation)) { + ++it; + } else { + OatQuickMethodHeader* header = OatQuickMethodHeader::FromCodePointer(code_ptr); + method_headers.insert(header); + method_code_map_.erase(header->GetCode()); + VLOG(jit) << "JIT removed " << *it; + it = processed_zombie_code_.erase(it); + } + } + for (auto it = processed_zombie_jni_code_.begin(); it != processed_zombie_jni_code_.end();) { + ArtMethod* method = *it; + auto stub = jni_stubs_map_.find(JniStubKey(method)); + if (stub == jni_stubs_map_.end()) { + it = processed_zombie_jni_code_.erase(it); + continue; } - for (auto it = processed_zombie_jni_code_.begin(); it != processed_zombie_jni_code_.end();) { - ArtMethod* method = *it; - auto stub = jni_stubs_map_.find(JniStubKey(method)); - if (stub == jni_stubs_map_.end()) { - it = processed_zombie_jni_code_.erase(it); - continue; - } - JniStubData& data = stub->second; - if (!data.IsCompiled() || !ContainsElement(data.GetMethods(), method)) { - it = processed_zombie_jni_code_.erase(it); - } else if (method->GetEntryPointFromQuickCompiledCode() == - OatQuickMethodHeader::FromCodePointer(data.GetCode())->GetEntryPoint()) { - // The stub got reused for this method, remove ourselves from the zombie - // list. - it = processed_zombie_jni_code_.erase(it); - } else if (!GetLiveBitmap()->Test(FromCodeToAllocation(data.GetCode()))) { - data.RemoveMethod(method); - if (data.GetMethods().empty()) { - OatQuickMethodHeader* header = OatQuickMethodHeader::FromCodePointer(data.GetCode()); - method_headers.insert(header); - CHECK(ContainsPc(header)); - VLOG(jit) << "JIT removed native code of" << method->PrettyMethod(); - jni_stubs_map_.erase(stub); - } else { - stub->first.UpdateShorty(stub->second.GetMethods().front()); - } - it = processed_zombie_jni_code_.erase(it); + JniStubData& data = stub->second; + if (!data.IsCompiled() || !ContainsElement(data.GetMethods(), method)) { + it = processed_zombie_jni_code_.erase(it); + } else if (method->GetEntryPointFromQuickCompiledCode() == + OatQuickMethodHeader::FromCodePointer(data.GetCode())->GetEntryPoint()) { + // The stub got reused for this method, remove ourselves from the zombie + // list. + it = processed_zombie_jni_code_.erase(it); + } else if (!GetLiveBitmap()->Test(FromCodeToAllocation(data.GetCode()))) { + data.RemoveMethod(method); + if (data.GetMethods().empty()) { + OatQuickMethodHeader* header = OatQuickMethodHeader::FromCodePointer(data.GetCode()); + method_headers.insert(header); + CHECK(ContainsPc(header)); + VLOG(jit) << "JIT removed native code of" << method->PrettyMethod(); + jni_stubs_map_.erase(stub); } else { - ++it; + stub->first.UpdateShorty(stub->second.GetMethods().front()); } + it = processed_zombie_jni_code_.erase(it); + } else { + ++it; } } - FreeAllMethodHeaders(method_headers); } @@ -1200,6 +1194,7 @@ void JitCodeCache::AddZombieCodeInternal(ArtMethod* method, const void* code_ptr CHECK(jni_stubs_map_.find(JniStubKey(method)) != jni_stubs_map_.end()); zombie_jni_code_.insert(method); } else { + CHECK(!ContainsElement(zombie_code_, code_ptr)); zombie_code_.insert(code_ptr); } // Arbitrary threshold of number of zombie code before doing a GC. @@ -1805,23 +1800,21 @@ void JitCodeCache::Dump(std::ostream& os) { void JitCodeCache::DumpAllCompiledMethods(std::ostream& os) { MutexLock mu(Thread::Current(), *Locks::jit_lock_); - for (auto it : method_code_map_) { // Includes OSR methods. - ArtMethod* meth = it.second; - const void* code_ptr = it.first; + for (const auto& [code_ptr, meth] : method_code_map_) { // Includes OSR methods. OatQuickMethodHeader* header = OatQuickMethodHeader::FromCodePointer(code_ptr); os << meth->PrettyMethod() << "@" << std::hex << code_ptr << "-" << reinterpret_cast<uintptr_t>(code_ptr) + header->GetCodeSize() << '\n'; } os << "JNIStubs: \n"; - for (auto it : jni_stubs_map_) { - const void* code_ptr = it.second.GetCode(); + for (const auto& [_, data] : jni_stubs_map_) { + const void* code_ptr = data.GetCode(); if (code_ptr == nullptr) { continue; } OatQuickMethodHeader* header = OatQuickMethodHeader::FromCodePointer(code_ptr); os << std::hex << code_ptr << "-" << reinterpret_cast<uintptr_t>(code_ptr) + header->GetCodeSize() << " "; - for (ArtMethod* m : it.second.GetMethods()) { + for (ArtMethod* m : data.GetMethods()) { os << m->PrettyMethod() << ";"; } os << "\n"; @@ -1891,13 +1884,13 @@ void JitCodeCache::VisitAllMethods(const std::function<void(const void*, ArtMeth } } } - for (auto it : method_code_map_) { // Includes OSR methods. + for (const auto& it : method_code_map_) { // Includes OSR methods. cb(it.first, it.second); } - for (auto it : saved_compiled_methods_map_) { + for (const auto& it : saved_compiled_methods_map_) { cb(it.second, it.first); } - for (auto it : zygote_map_) { + for (const auto& it : zygote_map_) { if (it.code_ptr != nullptr && it.method != nullptr) { cb(it.code_ptr, it.method); } diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 5202c2c37e..7de29d4024 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -452,6 +452,7 @@ class JitCodeCache { // Remove CHA dependents and underlying allocations for entries in `method_headers`. void FreeAllMethodHeaders(const std::unordered_set<OatQuickMethodHeader*>& method_headers) + REQUIRES(Locks::jit_lock_) REQUIRES(!Locks::cha_lock_); // Removes method from the cache. The caller must ensure that all threads diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 942d2923fc..abf01f5498 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -662,7 +662,7 @@ void ProfileSaver::GetClassesAndMethodsHelper::UpdateProfile(const std::set<std: array_class_descriptor.assign(class_record.array_dimension, '['); array_class_descriptor += dex_file->GetTypeDescriptorView(class_record.type_index); dex::TypeIndex type_index = - profile_info->FindOrCreateTypeIndex(*dex_file, array_class_descriptor.c_str()); + profile_info->FindOrCreateTypeIndex(*dex_file, array_class_descriptor); if (type_index.IsValid()) { profile_info->AddClass(profile_index, type_index); } @@ -716,7 +716,7 @@ void ProfileSaver::GetClassesAndMethodsHelper::UpdateProfile(const std::set<std: array_class_descriptor.assign(dim, '['); array_class_descriptor += Primitive::Descriptor(enum_cast<Primitive::Type>(i)); dex::TypeIndex type_index = - profile_info->FindOrCreateTypeIndex(*dex_file, array_class_descriptor.c_str()); + profile_info->FindOrCreateTypeIndex(*dex_file, array_class_descriptor); if (type_index.IsValid()) { profile_info->AddClass(profile_index, type_index); } diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc index bbe3970839..4b58fd883e 100644 --- a/runtime/jni/java_vm_ext.cc +++ b/runtime/jni/java_vm_ext.cc @@ -620,7 +620,7 @@ bool JavaVMExt::ShouldTrace(ArtMethod* method) { return false; } // Perform checks based on class name. - std::string_view class_name(method->GetDeclaringClassDescriptor()); + std::string_view class_name = method->GetDeclaringClassDescriptorView(); if (!trace_.empty() && class_name.find(trace_) != std::string_view::npos) { return true; } diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index 579b1536a3..830a7e5fb6 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -24,6 +24,7 @@ #include "base/bit_utils.h" #include "base/casts.h" #include "class.h" +#include "class_linker.h" #include "obj_ptr-inl.h" #include "runtime.h" #include "thread-current-inl.h" @@ -99,7 +100,7 @@ inline void PrimitiveArray<T>::SetWithoutChecks(int32_t i, T value) { DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); } if (kTransactionActive) { - Runtime::Current()->RecordWriteArray(this, i, GetWithoutChecks(i)); + Runtime::Current()->GetClassLinker()->RecordWriteArray(this, i, GetWithoutChecks(i)); } DCHECK(CheckIsValidIndex<kVerifyFlags>(i)) << i << " " << GetLength<kVerifyFlags>(); GetData()[i] = value; diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 98b108aff3..74776c14d4 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -818,6 +818,13 @@ inline const DexFile& Class::GetDexFile() { return *GetDexCache<kDefaultVerifyFlags, kWithoutReadBarrier>()->GetDexFile(); } +inline std::string_view Class::GetDescriptorView() { + DCHECK(!IsArrayClass()); + DCHECK(!IsPrimitive()); + DCHECK(!IsProxyClass()); + return GetDexFile().GetTypeDescriptorView(GetDexTypeIndex()); +} + inline bool Class::DescriptorEquals(const char* match) { ObjPtr<mirror::Class> klass = this; while (klass->IsArrayClass()) { diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 828ab968a3..b54e10069d 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -793,7 +793,7 @@ static std::tuple<bool, ArtMethod*> FindDeclaredClassMethod(ObjPtr<mirror::Class // Do not use ArtMethod::GetNameView() to avoid reloading dex file through the same // declaring class from different methods and also avoid the runtime method check. const dex::MethodId& method_id = get_method_id(mid); - return name.compare(dex_file.GetMethodNameView(method_id)); + return DexFile::CompareMemberNames(name, dex_file.GetMethodNameView(method_id)); }; auto signature_cmp = [&](uint32_t mid) REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE { // Do not use ArtMethod::GetSignature() to avoid reloading dex file through the same @@ -1054,11 +1054,12 @@ static std::tuple<bool, ArtField*> FindFieldByNameAndType(const DexFile& dex_fil }; auto name_cmp = [&](uint32_t mid) REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE { const dex::FieldId& field_id = get_field_id(mid); - return name.compare(dex_file.GetFieldNameView(field_id)); + return DexFile::CompareMemberNames(name, dex_file.GetFieldNameView(field_id)); }; auto type_cmp = [&](uint32_t mid) REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE { const dex::FieldId& field_id = get_field_id(mid); - return type.compare(dex_file.GetTypeDescriptorView(dex_file.GetTypeId(field_id.type_idx_))); + return DexFile::CompareDescriptors( + type, dex_file.GetTypeDescriptorView(dex_file.GetTypeId(field_id.type_idx_))); }; auto get_name_idx = [&](uint32_t mid) REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE { const dex::FieldId& field_id = get_field_id(mid); @@ -1691,6 +1692,68 @@ ObjPtr<Class> Class::CopyOf(Handle<Class> h_this, return new_class->AsClass(); } +bool Class::DescriptorEquals(ObjPtr<mirror::Class> match) { + DCHECK(match != nullptr); + ObjPtr<mirror::Class> klass = this; + while (klass->IsArrayClass()) { + // No read barrier needed, we're reading a chain of constant references for comparison + // with null. Then we follow up below with reading constant references to read constant + // primitive data in both proxy and non-proxy paths. See ReadBarrierOption. + klass = klass->GetComponentType<kDefaultVerifyFlags, kWithoutReadBarrier>(); + DCHECK(klass != nullptr); + match = match->GetComponentType<kDefaultVerifyFlags, kWithoutReadBarrier>(); + if (match == nullptr){ + return false; + } + } + if (match->IsArrayClass()) { + return false; + } + + if (UNLIKELY(klass->IsPrimitive()) || UNLIKELY(match->IsPrimitive())) { + return klass->GetPrimitiveType() == match->GetPrimitiveType(); + } + + if (UNLIKELY(klass->IsProxyClass())) { + return klass->ProxyDescriptorEquals(match); + } + if (UNLIKELY(match->IsProxyClass())) { + return match->ProxyDescriptorEquals(klass); + } + + const DexFile& klass_dex_file = klass->GetDexFile(); + const DexFile& match_dex_file = match->GetDexFile(); + dex::TypeIndex klass_type_index = klass->GetDexTypeIndex(); + dex::TypeIndex match_type_index = match->GetDexTypeIndex(); + if (&klass_dex_file == &match_dex_file) { + return klass_type_index == match_type_index; + } + std::string_view klass_descriptor = klass_dex_file.GetTypeDescriptorView(klass_type_index); + std::string_view match_descriptor = match_dex_file.GetTypeDescriptorView(match_type_index); + return klass_descriptor == match_descriptor; +} + +bool Class::ProxyDescriptorEquals(ObjPtr<mirror::Class> match) { + DCHECK(IsProxyClass()); + ObjPtr<mirror::String> name = GetName<kVerifyNone, kWithoutReadBarrier>(); + DCHECK(name != nullptr); + + DCHECK(match != nullptr); + DCHECK(!match->IsArrayClass()); + DCHECK(!match->IsPrimitive()); + if (match->IsProxyClass()) { + ObjPtr<mirror::String> match_name = match->GetName<kVerifyNone, kWithoutReadBarrier>(); + DCHECK(name != nullptr); + return name->Equals(match_name); + } + + // Note: Proxy descriptor should never match a non-proxy descriptor but ART does not enforce that. + std::string descriptor = DotToDescriptor(name->ToModifiedUtf8().c_str()); + std::string_view match_descriptor = + match->GetDexFile().GetTypeDescriptorView(match->GetDexTypeIndex()); + return descriptor == match_descriptor; +} + bool Class::ProxyDescriptorEquals(const char* match) { DCHECK(IsProxyClass()); std::string storage; @@ -1754,8 +1817,15 @@ uint32_t Class::Depth() { } dex::TypeIndex Class::FindTypeIndexInOtherDexFile(const DexFile& dex_file) { - std::string temp; - const dex::TypeId* type_id = dex_file.FindTypeId(GetDescriptor(&temp)); + std::string_view descriptor; + std::optional<std::string> temp; + if (IsPrimitive() || IsArrayClass() || IsProxyClass()) { + temp.emplace(); + descriptor = GetDescriptor(&temp.value()); + } else { + descriptor = GetDescriptorView(); + } + const dex::TypeId* type_id = dex_file.FindTypeId(descriptor); return (type_id == nullptr) ? dex::TypeIndex() : dex_file.GetIndexForTypeId(*type_id); } diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 42ce9d1349..17d45b4312 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -1226,11 +1226,16 @@ class EXPORT MANAGED Class final : public Object { void SetSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); + // Get the descriptor of the class as `std::string_view`. The class must be directly + // backed by a `DexFile` - it must not be primitive, array or proxy class. + std::string_view GetDescriptorView() REQUIRES_SHARED(Locks::mutator_lock_); + // Get the descriptor of the class. In a few cases a std::string is required, rather than // always create one the storage argument is populated and its internal c_str() returned. We do // this to avoid memory allocation in the common case. const char* GetDescriptor(std::string* storage) REQUIRES_SHARED(Locks::mutator_lock_); + bool DescriptorEquals(ObjPtr<mirror::Class> match) REQUIRES_SHARED(Locks::mutator_lock_); bool DescriptorEquals(const char* match) REQUIRES_SHARED(Locks::mutator_lock_); uint32_t DescriptorHash() REQUIRES_SHARED(Locks::mutator_lock_); @@ -1418,6 +1423,7 @@ class EXPORT MANAGED Class final : public Object { // The index in the methods_ array where the first direct method is. ALWAYS_INLINE uint32_t GetDirectMethodsStartOffset() REQUIRES_SHARED(Locks::mutator_lock_); + bool ProxyDescriptorEquals(ObjPtr<mirror::Class> match) REQUIRES_SHARED(Locks::mutator_lock_); bool ProxyDescriptorEquals(const char* match) REQUIRES_SHARED(Locks::mutator_lock_); static uint32_t UpdateHashForProxyClass(uint32_t hash, ObjPtr<mirror::Class> proxy_class) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index d423a8edfb..8f9d624399 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -141,7 +141,7 @@ inline void DexCache::SetResolvedString(dex::StringIndex string_idx, ObjPtr<Stri Runtime* const runtime = Runtime::Current(); if (UNLIKELY(runtime->IsActiveTransaction())) { DCHECK(runtime->IsAotCompiler()); - runtime->RecordResolveString(this, string_idx); + runtime->GetClassLinker()->RecordResolveString(this, string_idx); } // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. WriteBarrier::ForEveryFieldWrite(this); @@ -188,7 +188,7 @@ inline void DexCache::SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodTyp Runtime* const runtime = Runtime::Current(); if (UNLIKELY(runtime->IsActiveTransaction())) { DCHECK(runtime->IsAotCompiler()); - runtime->RecordResolveMethodType(this, proto_idx); + runtime->GetClassLinker()->RecordResolveMethodType(this, proto_idx); } // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. WriteBarrier::ForEveryFieldWrite(this); diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index 8910dfcbb5..c1d71e1501 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -374,7 +374,7 @@ class MANAGED DexCache final : public Object { void SetClassLoader(ObjPtr<ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_); - ObjPtr<ClassLoader> GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_); + EXPORT ObjPtr<ClassLoader> GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_); template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, ReadBarrierOption kReadBarrierOption = kWithReadBarrier, diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 60b8a0512e..14b9ca3af0 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -393,11 +393,8 @@ template<bool kTransactionActive, inline void Object::SetFieldBoolean(MemberOffset field_offset, uint8_t new_value) { VerifyTransaction<kTransactionActive, kCheckTransaction>(); if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldBoolean( - this, - field_offset, - GetFieldBoolean<kVerifyFlags, kIsVolatile>(field_offset), - kIsVolatile); + Runtime::Current()->GetClassLinker()->RecordWriteFieldBoolean( + this, field_offset, GetFieldBoolean<kVerifyFlags, kIsVolatile>(field_offset), kIsVolatile); } Verify<kVerifyFlags>(); SetFieldPrimitive<uint8_t, kIsVolatile>(field_offset, new_value); @@ -410,10 +407,8 @@ template<bool kTransactionActive, inline void Object::SetFieldByte(MemberOffset field_offset, int8_t new_value) { VerifyTransaction<kTransactionActive, kCheckTransaction>(); if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldByte(this, - field_offset, - GetFieldByte<kVerifyFlags, kIsVolatile>(field_offset), - kIsVolatile); + Runtime::Current()->GetClassLinker()->RecordWriteFieldByte( + this, field_offset, GetFieldByte<kVerifyFlags, kIsVolatile>(field_offset), kIsVolatile); } Verify<kVerifyFlags>(); SetFieldPrimitive<int8_t, kIsVolatile>(field_offset, new_value); @@ -460,10 +455,8 @@ template<bool kTransactionActive, inline void Object::SetFieldChar(MemberOffset field_offset, uint16_t new_value) { VerifyTransaction<kTransactionActive, kCheckTransaction>(); if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldChar(this, - field_offset, - GetFieldChar<kVerifyFlags, kIsVolatile>(field_offset), - kIsVolatile); + Runtime::Current()->GetClassLinker()->RecordWriteFieldChar( + this, field_offset, GetFieldChar<kVerifyFlags, kIsVolatile>(field_offset), kIsVolatile); } Verify<kVerifyFlags>(); SetFieldPrimitive<uint16_t, kIsVolatile>(field_offset, new_value); @@ -476,10 +469,8 @@ template<bool kTransactionActive, inline void Object::SetFieldShort(MemberOffset field_offset, int16_t new_value) { VerifyTransaction<kTransactionActive, kCheckTransaction>(); if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldChar(this, - field_offset, - GetFieldShort<kVerifyFlags, kIsVolatile>(field_offset), - kIsVolatile); + Runtime::Current()->GetClassLinker()->RecordWriteFieldChar( + this, field_offset, GetFieldShort<kVerifyFlags, kIsVolatile>(field_offset), kIsVolatile); } Verify<kVerifyFlags>(); SetFieldPrimitive<int16_t, kIsVolatile>(field_offset, new_value); @@ -504,10 +495,8 @@ template<bool kTransactionActive, inline void Object::SetField32(MemberOffset field_offset, int32_t new_value) { VerifyTransaction<kTransactionActive, kCheckTransaction>(); if (kTransactionActive) { - Runtime::Current()->RecordWriteField32(this, - field_offset, - GetField32<kVerifyFlags, kIsVolatile>(field_offset), - kIsVolatile); + Runtime::Current()->GetClassLinker()->RecordWriteField32( + this, field_offset, GetField32<kVerifyFlags, kIsVolatile>(field_offset), kIsVolatile); } Verify<kVerifyFlags>(); SetFieldPrimitive<int32_t, kIsVolatile>(field_offset, new_value); @@ -534,10 +523,8 @@ template<bool kTransactionActive, inline void Object::SetField64(MemberOffset field_offset, int64_t new_value) { VerifyTransaction<kTransactionActive, kCheckTransaction>(); if (kTransactionActive) { - Runtime::Current()->RecordWriteField64(this, - field_offset, - GetField64<kVerifyFlags, kIsVolatile>(field_offset), - kIsVolatile); + Runtime::Current()->GetClassLinker()->RecordWriteField64( + this, field_offset, GetField64<kVerifyFlags, kIsVolatile>(field_offset), kIsVolatile); } Verify<kVerifyFlags>(); SetFieldPrimitive<int64_t, kIsVolatile>(field_offset, new_value); @@ -570,13 +557,15 @@ inline bool Object::CasFieldWeakSequentiallyConsistent64(MemberOffset field_offs int64_t old_value, int64_t new_value) { VerifyTransaction<kTransactionActive, kCheckTransaction>(); - if (kTransactionActive) { - Runtime::Current()->RecordWriteField64(this, field_offset, old_value, true); - } Verify<kVerifyFlags>(); uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); Atomic<int64_t>* atomic_addr = reinterpret_cast<Atomic<int64_t>*>(raw_addr); - return atomic_addr->CompareAndSetWeakSequentiallyConsistent(old_value, new_value); + bool success = atomic_addr->CompareAndSetWeakSequentiallyConsistent(old_value, new_value); + if (kTransactionActive && success) { + Runtime::Current()->GetClassLinker()->RecordWriteField64( + this, field_offset, old_value, /*is_volatile=*/ true); + } + return success; } template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> @@ -584,13 +573,15 @@ inline bool Object::CasFieldStrongSequentiallyConsistent64(MemberOffset field_of int64_t old_value, int64_t new_value) { VerifyTransaction<kTransactionActive, kCheckTransaction>(); - if (kTransactionActive) { - Runtime::Current()->RecordWriteField64(this, field_offset, old_value, true); - } Verify<kVerifyFlags>(); uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); Atomic<int64_t>* atomic_addr = reinterpret_cast<Atomic<int64_t>*>(raw_addr); - return atomic_addr->CompareAndSetStrongSequentiallyConsistent(old_value, new_value); + bool success = atomic_addr->CompareAndSetStrongSequentiallyConsistent(old_value, new_value); + if (kTransactionActive && success) { + Runtime::Current()->GetClassLinker()->RecordWriteField64( + this, field_offset, old_value, /*is_volatile=*/ true); + } + return success; } /* @@ -626,13 +617,10 @@ inline void Object::SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, ObjPtr<Object> new_value) { VerifyTransaction<kTransactionActive, kCheckTransaction>(); if (kTransactionActive) { - ObjPtr<Object> obj; - if (kIsVolatile) { - obj = GetFieldObjectVolatile<Object>(field_offset); - } else { - obj = GetFieldObject<Object>(field_offset); - } - Runtime::Current()->RecordWriteFieldReference(this, field_offset, obj, true); + ObjPtr<Object> old_value = + GetFieldObject<Object, kVerifyFlags, kWithReadBarrier, kIsVolatile>(field_offset); + Runtime::Current()->GetClassLinker()->RecordWriteFieldReference( + this, field_offset, old_value, kIsVolatile); } Verify<kVerifyFlags>(); VerifyWrite<kVerifyFlags>(new_value); @@ -685,14 +673,16 @@ inline bool Object::CasFieldObjectWithoutWriteBarrier(MemberOffset field_offset, std::memory_order memory_order) { VerifyTransaction<kTransactionActive, kCheckTransaction>(); VerifyCAS<kVerifyFlags>(new_value, old_value); - if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true); - } uint32_t old_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(old_value)); uint32_t new_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(new_value)); uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr); - return atomic_addr->CompareAndSet(old_ref, new_ref, mode, memory_order); + bool success = atomic_addr->CompareAndSet(old_ref, new_ref, mode, memory_order); + if (kTransactionActive && success) { + Runtime::Current()->GetClassLinker()->RecordWriteFieldReference( + this, field_offset, old_value, /*is_volatile=*/ true); + } + return success; } template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> @@ -731,7 +721,8 @@ inline ObjPtr<Object> Object::CompareAndExchangeFieldObject(MemberOffset field_o } if (success) { if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldReference(this, field_offset, witness_value, true); + Runtime::Current()->GetClassLinker()->RecordWriteFieldReference( + this, field_offset, witness_value, /*is_volatile=*/ true); } WriteBarrier::ForFieldWrite(this, field_offset, new_value); } @@ -755,7 +746,8 @@ inline ObjPtr<Object> Object::ExchangeFieldObject(MemberOffset field_offset, ReadBarrier::AssertToSpaceInvariant(old_value.Ptr()); } if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true); + Runtime::Current()->GetClassLinker()->RecordWriteFieldReference( + this, field_offset, old_value, /*is_volatile=*/ true); } WriteBarrier::ForFieldWrite(this, field_offset, new_value); VerifyRead<kVerifyFlags>(old_value); @@ -777,7 +769,8 @@ inline void Object::UpdateFieldBooleanViaAccessor(MemberOffset field_offset, if (kTransactionActive) { static const bool kIsVolatile = true; uint8_t old_value = GetFieldBoolean<kVerifyFlags, kIsVolatile>(field_offset); - Runtime::Current()->RecordWriteFieldBoolean(this, field_offset, old_value, kIsVolatile); + Runtime::Current()->GetClassLinker()->RecordWriteFieldBoolean( + this, field_offset, old_value, kIsVolatile); } Verify<kVerifyFlags>(); uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); @@ -792,7 +785,8 @@ inline void Object::UpdateFieldByteViaAccessor(MemberOffset field_offset, if (kTransactionActive) { static const bool kIsVolatile = true; int8_t old_value = GetFieldByte<kVerifyFlags, kIsVolatile>(field_offset); - Runtime::Current()->RecordWriteFieldByte(this, field_offset, old_value, kIsVolatile); + Runtime::Current()->GetClassLinker()->RecordWriteFieldByte( + this, field_offset, old_value, kIsVolatile); } Verify<kVerifyFlags>(); uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); @@ -807,7 +801,8 @@ inline void Object::UpdateFieldCharViaAccessor(MemberOffset field_offset, if (kTransactionActive) { static const bool kIsVolatile = true; uint16_t old_value = GetFieldChar<kVerifyFlags, kIsVolatile>(field_offset); - Runtime::Current()->RecordWriteFieldChar(this, field_offset, old_value, kIsVolatile); + Runtime::Current()->GetClassLinker()->RecordWriteFieldChar( + this, field_offset, old_value, kIsVolatile); } Verify<kVerifyFlags>(); uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); @@ -822,7 +817,8 @@ inline void Object::UpdateFieldShortViaAccessor(MemberOffset field_offset, if (kTransactionActive) { static const bool kIsVolatile = true; int16_t old_value = GetFieldShort<kVerifyFlags, kIsVolatile>(field_offset); - Runtime::Current()->RecordWriteFieldShort(this, field_offset, old_value, kIsVolatile); + Runtime::Current()->GetClassLinker()->RecordWriteFieldShort( + this, field_offset, old_value, kIsVolatile); } Verify<kVerifyFlags>(); uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); @@ -837,7 +833,8 @@ inline void Object::UpdateField32ViaAccessor(MemberOffset field_offset, if (kTransactionActive) { static const bool kIsVolatile = true; int32_t old_value = GetField32<kVerifyFlags, kIsVolatile>(field_offset); - Runtime::Current()->RecordWriteField32(this, field_offset, old_value, kIsVolatile); + Runtime::Current()->GetClassLinker()->RecordWriteField32( + this, field_offset, old_value, kIsVolatile); } Verify<kVerifyFlags>(); uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); @@ -852,7 +849,8 @@ inline void Object::UpdateField64ViaAccessor(MemberOffset field_offset, if (kTransactionActive) { static const bool kIsVolatile = true; int64_t old_value = GetField64<kVerifyFlags, kIsVolatile>(field_offset); - Runtime::Current()->RecordWriteField64(this, field_offset, old_value, kIsVolatile); + Runtime::Current()->GetClassLinker()->RecordWriteField64( + this, field_offset, old_value, kIsVolatile); } Verify<kVerifyFlags>(); uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); diff --git a/runtime/mirror/object-readbarrier-inl.h b/runtime/mirror/object-readbarrier-inl.h index 15179b93ff..39bc43b63a 100644 --- a/runtime/mirror/object-readbarrier-inl.h +++ b/runtime/mirror/object-readbarrier-inl.h @@ -20,6 +20,7 @@ #include "object.h" #include "base/atomic.h" +#include "class_linker.h" #include "heap_poisoning.h" #include "lock_word-inl.h" #include "object_reference-inl.h" @@ -46,16 +47,18 @@ inline bool Object::CasField32(MemberOffset field_offset, if (kCheckTransaction) { DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); } - if (kTransactionActive) { - Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true); - } if (kVerifyFlags & kVerifyThis) { VerifyObject(this); } uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); AtomicInteger* atomic_addr = reinterpret_cast<AtomicInteger*>(raw_addr); - return atomic_addr->CompareAndSet(old_value, new_value, mode, memory_order); + bool success = atomic_addr->CompareAndSet(old_value, new_value, mode, memory_order); + if (kTransactionActive && success) { + Runtime::Current()->GetClassLinker()->RecordWriteField32( + this, field_offset, old_value, /*is_volatile=*/ true); + } + return success; } inline bool Object::CasLockWord(LockWord old_val, diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc index e9b7545926..1440909032 100644 --- a/runtime/native/java_lang_reflect_Executable.cc +++ b/runtime/native/java_lang_reflect_Executable.cc @@ -255,6 +255,11 @@ static jint Executable_compareMethodParametersInternal(JNIEnv* env, this_method = this_method->GetInterfaceMethodIfProxy(kRuntimePointerSize); other_method = other_method->GetInterfaceMethodIfProxy(kRuntimePointerSize); + // Get dex files early. (`ArtMethod::GetParameterTypeList()` includes `GetDexFile()`, + // so the compiler should deduplicate these subexpressions after inlining.) + const DexFile* this_dex_file = this_method->GetDexFile(); + const DexFile* other_dex_file = other_method->GetDexFile(); + const dex::TypeList* this_list = this_method->GetParameterTypeList(); const dex::TypeList* other_list = other_method->GetParameterTypeList(); @@ -278,15 +283,9 @@ static jint Executable_compareMethodParametersInternal(JNIEnv* env, } for (int32_t i = 0; i < this_size; ++i) { - const std::string_view lhs_data = this_method->GetDexFile()->GetTypeDescriptorView( - this_list->GetTypeItem(i).type_idx_); - const std::string_view rhs_data = other_method->GetDexFile()->GetTypeDescriptorView( - other_list->GetTypeItem(i).type_idx_); - - // Note: `std::string_view::compare()` uses lexicographical comparison and treats the `char` - // as unsigned; for Modified-UTF-8 without embedded nulls this is consistent with the - // `CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues()` ordering. - int cmp = lhs_data.compare(rhs_data); + int cmp = DexFile::CompareDescriptors( + this_dex_file->GetTypeDescriptorView(this_list->GetTypeItem(i).type_idx_), + other_dex_file->GetTypeDescriptorView(other_list->GetTypeItem(i).type_idx_)); if (cmp != 0) { return (cmp < 0) ? -1 : 1; } diff --git a/runtime/native_gc_triggering.md b/runtime/native_gc_triggering.md new file mode 100644 index 0000000000..3e8fb08f01 --- /dev/null +++ b/runtime/native_gc_triggering.md @@ -0,0 +1,87 @@ +Triggering the GC to reclaim non-Java memory +-------------------------------------------- + +Android applications and libraries commonly allocate "native" (i.e. C++) objects that are +effectively owned by a Java object, and reclaimed when the garbage collector determines that the +owning Java object is no longer reachable. Various mechanisms are used to accomplish this. These +include traditional Java finalizers, more modern Java `Cleaner`s, Android's `SystemCleaner` and, +for platform code, `NativeAllocationRegistry`. Internally, these all rely on the garbage +collector's processing of `java.lang.ref.Reference`s. + +Historically, we have encountered issues when large volumes of such "native" objects are owned by +Java objects of significantly smaller size. The Java garbage collector normally decides when to +collect based on occupancy of the Java heap. It would not collect if there are few bytes of newly +allocated Java objects in the Java heap, even if they "own" large amounts of C++ memory, which +cannot be reclaimed until the Java objects are reclaimed. + +This led to the development of the `VMRuntime.registerNativeAllocation` API, and eventually the +`NativeAllocationRegistry` API. Both of these allow the programmer to inform the ART GC that a +certain amount of C++ memory had been allocated, and could only be reclaimed as the result of a +Java GC. + +This had the advantage that the GC would theoretically know exactly how much native memory was +owned by Java objects. However, its major problem was that registering the exact size of such +native objects frequently turned out to be impractical. Often a Java object does not just own a +single native object, but instead owns a complex native data structure composed of many objects in +the C++ heap. Their sizes commonly depended on prior computations by C++ code. Some of these might +be shared between Java objects, and could only be reclaimed when all of those Java objects became +unreachable. Often at least some of the native objects were allocated by third-party libraries +that did not make the sizes of its internal objects available to clients. + +In extreme cases, underestimation of native object sizes could cause native memory use to be so +excessive that the device would become unstable. At one time, a particularly nasty arithmetic +expression would cause the Google Calculator app to allocate sufficiently large native arrays of +digits backing Java `BigInteger`s to force a restart of system processes. (This has since been +addressed in other ways as well.) + +Thus we switched to a scheme in which sizes of native objects are commonly no longer directly +provided by clients. The GC instead occasionally calls `mallinfo()` to determine how much native +memory has been allocated, and assumes that any of this may need the collector's help to reclaim. +C++ memory allocated by means other than `malloc`/`new` and owned by Java objects should still be +explicitly registered. This loses information about Java ownership, but results in much more +accurate information about the total size of native objects, something that was previously very +difficult to approximate, even to within an order of magnitude. + +The triggering heuristic +------------------------ + +We normally check for the need to trigger a GC due to native memory pressure only as a result of +calls to the `NativeAllocationRegistry` or `VMRuntime.registerNativeAllocation` APIs. Thus an +application not using these APIs, e.g. because it is running almost entirely native code, may +never do so. This is generally a feature, rather than a bug. + +The actual computation for triggering a native-allocation-GC is performed by +`Heap::NativeMemoryOverTarget()`. This computes and compares two quantities: + +1. An adjusted heap size for GC triggering. This consists of the Java heap size at which we would + normally trigger a GC plus an allowance for native heap size. This allowance currently consists + of one half (background processes) or three halves (foreground processes) of + `NativeAllocationGcWatermark()`. The latter is HeapMaxFree (typically 32MB) plus 1/8 of the + currently targeted heap size. For a foreground process, this allowance would typically be in + the 50-100 MB range for something other than a low-end device. + +2. An adjusted count of current bytes allocated. This is basically the number of bytes currently + allocated in the Java heap, plus half the net number of native bytes allocated since the last + GC. The latter is computed as the change since the last GC in the total-bytes-allocated + obtained from `mallinfo()` plus `native_bytes_registered_`. The `native_bytes_registered_` + field tracks the bytes explicitly registered, and not yet unregistered via + `registerNativeAllocation` APIs. It excludes bytes registered as malloc-allocated via + `NativeAllocationRegistry`. (The computation also considers the total amount of native memory + currently allocated by this metric, as opposed to the change since the last GC. But that is + currently de-weighted to the point of insignificance.) + +A background GC is triggered if the second quantity exceeds the first. A stop-the-world-GC is +triggered if the second quantity is at least 4 times larger than the first, and the native heap +currently occupies a large fraction of device memory, suggesting that the GC is falling behind and +endangering device usability. + +The fact that we consider both Java and native memory use at once means that we are more likely to +trigger a native GC when we are closer to the normal Java GC threshold. + +The actual use of `mallinfo()` to compute native heap occupancy reflects experiments with +different `libc` implementations. These have different performance characteristics, and sometimes +disagree on the interpretation of `struct mallinfo` fields. We believe our current implementation +is solid with scudo and jemalloc on Android, and minimally usable for testing elsewhere. + +(Some of this assumes typical current (May 2024) configuration constants, and may need to be +updated.) diff --git a/runtime/oat/aot_class_linker.cc b/runtime/oat/aot_class_linker.cc index 6640214dc0..0a85fa094b 100644 --- a/runtime/oat/aot_class_linker.cc +++ b/runtime/oat/aot_class_linker.cc @@ -16,6 +16,7 @@ #include "aot_class_linker.h" +#include "base/stl_util.h" #include "class_status.h" #include "compiler_callbacks.h" #include "dex/class_reference.h" @@ -23,20 +24,22 @@ #include "handle_scope-inl.h" #include "mirror/class-inl.h" #include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "transaction.h" #include "verifier/verifier_enums.h" namespace art HIDDEN { AotClassLinker::AotClassLinker(InternTable* intern_table) - : ClassLinker(intern_table, /*fast_class_not_found_exceptions=*/ false) {} + : ClassLinker(intern_table, /*fast_class_not_found_exceptions=*/ false), + preinitialization_transactions_() {} AotClassLinker::~AotClassLinker() {} bool AotClassLinker::CanAllocClass() { // AllocClass doesn't work under transaction, so we abort. - if (Runtime::Current()->IsActiveTransaction()) { - Runtime::Current()->AbortTransactionAndThrowAbortError( - Thread::Current(), "Can't resolve type within transaction."); + if (IsActiveTransaction()) { + AbortTransactionF(Thread::Current(), "Can't resolve type within transaction."); return false; } return ClassLinker::CanAllocClass(); @@ -47,8 +50,7 @@ bool AotClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, bool can_init_statics, bool can_init_parents) { - Runtime* const runtime = Runtime::Current(); - bool strict_mode = runtime->IsActiveStrictTransactionMode(); + bool strict_mode = IsActiveStrictTransactionMode(); DCHECK(klass != nullptr); if (klass->IsInitialized() || klass->IsInitializing()) { @@ -59,10 +61,12 @@ bool AotClassLinker::InitializeClass(Thread* self, // in a dex file belonging to the boot image we're compiling against. // However, we must allow the initialization of TransactionAbortError, // VerifyError, etc. outside of a transaction. - if (!strict_mode && runtime->GetHeap()->ObjectIsInBootImageSpace(klass->GetDexCache())) { - if (runtime->IsActiveTransaction()) { - runtime->AbortTransactionAndThrowAbortError(self, "Can't initialize " + klass->PrettyTypeOf() - + " because it is defined in a boot image dex file."); + if (!strict_mode && + Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass->GetDexCache())) { + if (IsActiveTransaction()) { + AbortTransactionF(self, + "Can't initialize %s because it is defined in a boot image dex file.", + klass->PrettyTypeOf().c_str()); return false; } CHECK(klass->IsThrowableClass()) << klass->PrettyDescriptor(); @@ -70,8 +74,9 @@ bool AotClassLinker::InitializeClass(Thread* self, // When in strict_mode, don't initialize a class if it belongs to boot but not initialized. if (strict_mode && klass->IsBootStrapClassLoaded()) { - runtime->AbortTransactionAndThrowAbortError(self, "Can't resolve " - + klass->PrettyTypeOf() + " because it is an uninitialized boot class."); + AbortTransactionF(self, + "Can't resolve %s because it is an uninitialized boot class.", + klass->PrettyTypeOf().c_str()); return false; } @@ -79,21 +84,22 @@ bool AotClassLinker::InitializeClass(Thread* self, // the transaction and rolled back after klass's change is commited. if (strict_mode && !klass->IsInterface() && klass->HasSuperClass()) { if (klass->GetSuperClass()->GetStatus() == ClassStatus::kInitializing) { - runtime->AbortTransactionAndThrowAbortError(self, "Can't resolve " - + klass->PrettyTypeOf() + " because it's superclass is not initialized."); + AbortTransactionF(self, + "Can't resolve %s because it's superclass is not initialized.", + klass->PrettyTypeOf().c_str()); return false; } } if (strict_mode) { - runtime->EnterTransactionMode(/*strict=*/ true, klass.Get()); + EnterTransactionMode(/*strict=*/ true, klass.Get()); } bool success = ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents); if (strict_mode) { if (success) { // Exit Transaction if success. - runtime->ExitTransactionMode(); + ExitTransactionMode(); } else { // If not successfully initialized, don't rollback immediately, leave the cleanup to compiler // driver which needs abort message and exception. @@ -130,6 +136,12 @@ verifier::FailureKind AotClassLinker::PerformClassVerification( return ClassLinker::PerformClassVerification(self, verifier_deps, klass, log_level, error_msg); } +static const std::vector<const DexFile*>* gAppImageDexFiles = nullptr; + +void AotClassLinker::SetAppImageDexFiles(const std::vector<const DexFile*>* app_image_dex_files) { + gAppImageDexFiles = app_image_dex_files; +} + bool AotClassLinker::CanReferenceInBootImageExtensionOrAppImage( ObjPtr<mirror::Class> klass, gc::Heap* heap) { // Do not allow referencing a class or instance of a class defined in a dex file @@ -159,8 +171,25 @@ bool AotClassLinker::CanReferenceInBootImageExtensionOrAppImage( } } + auto can_reference_dex_cache = [&](ObjPtr<mirror::DexCache> dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_) { + // We cannot reference a boot image dex cache for classes + // that were not themselves in the boot image. + if (heap->ObjectIsInBootImageSpace(dex_cache)) { + return false; + } + // App image compilation can pull in dex files from parent or library class loaders. + // Classes from such dex files cannot be included or referenced in the current app image + // to avoid conflicts with classes in the parent or library class loader's app image. + if (gAppImageDexFiles != nullptr && + !ContainsElement(*gAppImageDexFiles, dex_cache->GetDexFile())) { + return false; + } + return true; + }; + // Check the class itself. - if (heap->ObjectIsInBootImageSpace(klass->GetDexCache())) { + if (!can_reference_dex_cache(klass->GetDexCache())) { return false; } @@ -168,7 +197,7 @@ bool AotClassLinker::CanReferenceInBootImageExtensionOrAppImage( ObjPtr<mirror::Class> superclass = klass->GetSuperClass(); while (!heap->ObjectIsInBootImageSpace(superclass)) { DCHECK(superclass != nullptr); // Cannot skip Object which is in the primary boot image. - if (heap->ObjectIsInBootImageSpace(superclass->GetDexCache())) { + if (!can_reference_dex_cache(superclass->GetDexCache())) { return false; } superclass = superclass->GetSuperClass(); @@ -180,7 +209,7 @@ bool AotClassLinker::CanReferenceInBootImageExtensionOrAppImage( ObjPtr<mirror::Class> interface = if_table->GetInterface(i); DCHECK(interface != nullptr); if (!heap->ObjectIsInBootImageSpace(interface) && - heap->ObjectIsInBootImageSpace(interface->GetDexCache())) { + !can_reference_dex_cache(interface->GetDexCache())) { return false; } } @@ -193,7 +222,7 @@ bool AotClassLinker::CanReferenceInBootImageExtensionOrAppImage( for (auto& m : k->GetVirtualMethods(pointer_size)) { ObjPtr<mirror::Class> declaring_class = m.GetDeclaringClass(); CHECK(heap->ObjectIsInBootImageSpace(declaring_class) || - !heap->ObjectIsInBootImageSpace(declaring_class->GetDexCache())); + can_reference_dex_cache(declaring_class->GetDexCache())); } k = k->GetSuperClass(); } @@ -210,15 +239,13 @@ const SdkChecker* AotClassLinker::GetSdkChecker() const { return sdk_checker_.get(); } -bool AotClassLinker::DenyAccessBasedOnPublicSdk(ArtMethod* art_method) const - REQUIRES_SHARED(Locks::mutator_lock_) { +bool AotClassLinker::DenyAccessBasedOnPublicSdk(ArtMethod* art_method) const { return sdk_checker_ != nullptr && sdk_checker_->ShouldDenyAccess(art_method); } -bool AotClassLinker::DenyAccessBasedOnPublicSdk(ArtField* art_field) const - REQUIRES_SHARED(Locks::mutator_lock_) { +bool AotClassLinker::DenyAccessBasedOnPublicSdk(ArtField* art_field) const { return sdk_checker_ != nullptr && sdk_checker_->ShouldDenyAccess(art_field); } -bool AotClassLinker::DenyAccessBasedOnPublicSdk(const char* type_descriptor) const { +bool AotClassLinker::DenyAccessBasedOnPublicSdk(std::string_view type_descriptor) const { return sdk_checker_ != nullptr && sdk_checker_->ShouldDenyAccess(type_descriptor); } @@ -228,4 +255,260 @@ void AotClassLinker::SetEnablePublicSdkChecks(bool enabled) { } } +// Transaction support. + +bool AotClassLinker::IsActiveTransaction() const { + bool result = Runtime::Current()->IsActiveTransaction(); + DCHECK_EQ(result, !preinitialization_transactions_.empty() && !GetTransaction()->IsRollingBack()); + return result; +} + +void AotClassLinker::EnterTransactionMode(bool strict, mirror::Class* root) { + Runtime* runtime = Runtime::Current(); + ArenaPool* arena_pool = nullptr; + ArenaStack* arena_stack = nullptr; + if (preinitialization_transactions_.empty()) { // Top-level transaction? + // Make initialized classes visibly initialized now. If that happened during the transaction + // and then the transaction was aborted, we would roll back the status update but not the + // ClassLinker's bookkeeping structures, so these classes would never be visibly initialized. + { + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + HandleWrapper<mirror::Class> h(hs.NewHandleWrapper(&root)); + ScopedThreadSuspension sts(self, ThreadState::kNative); + MakeInitializedClassesVisiblyInitialized(Thread::Current(), /*wait=*/ true); + } + // Pass the runtime `ArenaPool` to the transaction. + arena_pool = runtime->GetArenaPool(); + } else { + // Pass the `ArenaStack` from previous transaction to the new one. + arena_stack = preinitialization_transactions_.front().GetArenaStack(); + } + preinitialization_transactions_.emplace_front(strict, root, arena_stack, arena_pool); + runtime->SetActiveTransaction(); +} + +void AotClassLinker::ExitTransactionMode() { + DCHECK(IsActiveTransaction()); + preinitialization_transactions_.pop_front(); + if (preinitialization_transactions_.empty()) { + Runtime::Current()->ClearActiveTransaction(); + } else { + DCHECK(IsActiveTransaction()); // Trigger the DCHECK() in `IsActiveTransaction()`. + } +} + +void AotClassLinker::RollbackAllTransactions() { + // If transaction is aborted, all transactions will be kept in the list. + // Rollback and exit all of them. + while (IsActiveTransaction()) { + RollbackAndExitTransactionMode(); + } +} + +void AotClassLinker::RollbackAndExitTransactionMode() { + DCHECK(IsActiveTransaction()); + Runtime::Current()->ClearActiveTransaction(); + preinitialization_transactions_.front().Rollback(); + preinitialization_transactions_.pop_front(); + if (!preinitialization_transactions_.empty()) { + Runtime::Current()->SetActiveTransaction(); + } +} + +const Transaction* AotClassLinker::GetTransaction() const { + DCHECK(!preinitialization_transactions_.empty()); + return &preinitialization_transactions_.front(); +} + +Transaction* AotClassLinker::GetTransaction() { + DCHECK(!preinitialization_transactions_.empty()); + return &preinitialization_transactions_.front(); +} + +bool AotClassLinker::IsActiveStrictTransactionMode() const { + return IsActiveTransaction() && GetTransaction()->IsStrict(); +} + +bool AotClassLinker::TransactionWriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) { + DCHECK(IsActiveTransaction()); + if (GetTransaction()->WriteConstraint(obj)) { + Runtime* runtime = Runtime::Current(); + DCHECK(runtime->GetHeap()->ObjectIsInBootImageSpace(obj) || obj->IsClass()); + const char* extra = runtime->GetHeap()->ObjectIsInBootImageSpace(obj) ? "boot image " : ""; + AbortTransactionF( + self, "Can't set fields of %s%s", extra, obj->PrettyTypeOf().c_str()); + return true; + } + return false; +} + +bool AotClassLinker::TransactionWriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value) { + DCHECK(IsActiveTransaction()); + if (GetTransaction()->WriteValueConstraint(value)) { + DCHECK(value != nullptr); + const char* description = value->IsClass() ? "class" : "instance of"; + ObjPtr<mirror::Class> klass = value->IsClass() ? value->AsClass() : value->GetClass(); + AbortTransactionF( + self, "Can't store reference to %s %s", description, klass->PrettyDescriptor().c_str()); + return true; + } + return false; +} + +bool AotClassLinker::TransactionReadConstraint(Thread* self, ObjPtr<mirror::Object> obj) { + DCHECK(obj->IsClass()); + if (GetTransaction()->ReadConstraint(obj)) { + AbortTransactionF(self, + "Can't read static fields of %s since it does not belong to clinit's class.", + obj->PrettyTypeOf().c_str()); + return true; + } + return false; +} + +bool AotClassLinker::TransactionAllocationConstraint(Thread* self, ObjPtr<mirror::Class> klass) { + DCHECK(IsActiveTransaction()); + if (klass->IsFinalizable()) { + AbortTransactionF(self, + "Allocating finalizable object in transaction: %s", + klass->PrettyDescriptor().c_str()); + return true; + } + return false; +} + +void AotClassLinker::RecordWriteFieldBoolean(mirror::Object* obj, + MemberOffset field_offset, + uint8_t value, + bool is_volatile) { + DCHECK(IsActiveTransaction()); + GetTransaction()->RecordWriteFieldBoolean(obj, field_offset, value, is_volatile); +} + +void AotClassLinker::RecordWriteFieldByte(mirror::Object* obj, + MemberOffset field_offset, + int8_t value, + bool is_volatile) { + DCHECK(IsActiveTransaction()); + GetTransaction()->RecordWriteFieldByte(obj, field_offset, value, is_volatile); +} + +void AotClassLinker::RecordWriteFieldChar(mirror::Object* obj, + MemberOffset field_offset, + uint16_t value, + bool is_volatile) { + DCHECK(IsActiveTransaction()); + GetTransaction()->RecordWriteFieldChar(obj, field_offset, value, is_volatile); +} + +void AotClassLinker::RecordWriteFieldShort(mirror::Object* obj, + MemberOffset field_offset, + int16_t value, + bool is_volatile) { + DCHECK(IsActiveTransaction()); + GetTransaction()->RecordWriteFieldShort(obj, field_offset, value, is_volatile); +} + +void AotClassLinker::RecordWriteField32(mirror::Object* obj, + MemberOffset field_offset, + uint32_t value, + bool is_volatile) { + DCHECK(IsActiveTransaction()); + GetTransaction()->RecordWriteField32(obj, field_offset, value, is_volatile); +} + +void AotClassLinker::RecordWriteField64(mirror::Object* obj, + MemberOffset field_offset, + uint64_t value, + bool is_volatile) { + DCHECK(IsActiveTransaction()); + GetTransaction()->RecordWriteField64(obj, field_offset, value, is_volatile); +} + +void AotClassLinker::RecordWriteFieldReference(mirror::Object* obj, + MemberOffset field_offset, + ObjPtr<mirror::Object> value, + bool is_volatile) { + DCHECK(IsActiveTransaction()); + GetTransaction()->RecordWriteFieldReference(obj, field_offset, value.Ptr(), is_volatile); +} + +void AotClassLinker::RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) { + DCHECK(IsActiveTransaction()); + GetTransaction()->RecordWriteArray(array, index, value); +} + +void AotClassLinker::RecordStrongStringInsertion(ObjPtr<mirror::String> s) { + DCHECK(IsActiveTransaction()); + GetTransaction()->RecordStrongStringInsertion(s); +} + +void AotClassLinker::RecordWeakStringInsertion(ObjPtr<mirror::String> s) { + DCHECK(IsActiveTransaction()); + GetTransaction()->RecordWeakStringInsertion(s); +} + +void AotClassLinker::RecordStrongStringRemoval(ObjPtr<mirror::String> s) { + DCHECK(IsActiveTransaction()); + GetTransaction()->RecordStrongStringRemoval(s); +} + +void AotClassLinker::RecordWeakStringRemoval(ObjPtr<mirror::String> s) { + DCHECK(IsActiveTransaction()); + GetTransaction()->RecordWeakStringRemoval(s); +} + +void AotClassLinker::RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, + dex::StringIndex string_idx) { + DCHECK(IsActiveTransaction()); + GetTransaction()->RecordResolveString(dex_cache, string_idx); +} + +void AotClassLinker::RecordResolveMethodType(ObjPtr<mirror::DexCache> dex_cache, + dex::ProtoIndex proto_idx) { + DCHECK(IsActiveTransaction()); + GetTransaction()->RecordResolveMethodType(dex_cache, proto_idx); +} + +void AotClassLinker::ThrowTransactionAbortError(Thread* self) { + DCHECK(IsActiveTransaction()); + // Passing nullptr means we rethrow an exception with the earlier transaction abort message. + GetTransaction()->ThrowAbortError(self, nullptr); +} + +void AotClassLinker::AbortTransactionF(Thread* self, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + AbortTransactionV(self, fmt, args); + va_end(args); +} + +void AotClassLinker::AbortTransactionV(Thread* self, const char* fmt, va_list args) { + CHECK(IsActiveTransaction()); + // Constructs abort message. + std::string abort_message; + android::base::StringAppendV(&abort_message, fmt, args); + // Throws an exception so we can abort the transaction and rollback every change. + // + // Throwing an exception may cause its class initialization. If we mark the transaction + // aborted before that, we may warn with a false alarm. Throwing the exception before + // marking the transaction aborted avoids that. + // But now the transaction can be nested, and abort the transaction will relax the constraints + // for constructing stack trace. + GetTransaction()->Abort(abort_message); + GetTransaction()->ThrowAbortError(self, &abort_message); +} + +bool AotClassLinker::IsTransactionAborted() const { + DCHECK(IsActiveTransaction()); + return GetTransaction()->IsAborted(); +} + +void AotClassLinker::VisitTransactionRoots(RootVisitor* visitor) { + for (Transaction& transaction : preinitialization_transactions_) { + transaction.VisitRoots(visitor); + } +} + } // namespace art diff --git a/runtime/oat/aot_class_linker.h b/runtime/oat/aot_class_linker.h index 492674ea10..0ee2fe1b42 100644 --- a/runtime/oat/aot_class_linker.h +++ b/runtime/oat/aot_class_linker.h @@ -17,12 +17,16 @@ #ifndef ART_RUNTIME_OAT_AOT_CLASS_LINKER_H_ #define ART_RUNTIME_OAT_AOT_CLASS_LINKER_H_ +#include <forward_list> + #include "base/macros.h" #include "sdk_checker.h" #include "class_linker.h" namespace art HIDDEN { +class Transaction; + namespace gc { class Heap; } // namespace gc @@ -35,19 +39,108 @@ class AotClassLinker : public ClassLinker { explicit AotClassLinker(InternTable *intern_table); ~AotClassLinker(); + EXPORT static void SetAppImageDexFiles(const std::vector<const DexFile*>* app_image_dex_files); + EXPORT static bool CanReferenceInBootImageExtensionOrAppImage( ObjPtr<mirror::Class> klass, gc::Heap* heap) REQUIRES_SHARED(Locks::mutator_lock_); EXPORT void SetSdkChecker(std::unique_ptr<SdkChecker>&& sdk_checker_); const SdkChecker* GetSdkChecker() const; - bool DenyAccessBasedOnPublicSdk([[maybe_unused]] ArtMethod* art_method) const override + // Verifies if the method is accessible according to the SdkChecker (if installed). + bool DenyAccessBasedOnPublicSdk(ArtMethod* art_method) const override REQUIRES_SHARED(Locks::mutator_lock_); - bool DenyAccessBasedOnPublicSdk([[maybe_unused]] ArtField* art_field) const override + // Verifies if the field is accessible according to the SdkChecker (if installed). + bool DenyAccessBasedOnPublicSdk(ArtField* art_field) const override REQUIRES_SHARED(Locks::mutator_lock_); - bool DenyAccessBasedOnPublicSdk([[maybe_unused]] const char* type_descriptor) const override; + // Verifies if the descriptor is accessible according to the SdkChecker (if installed). + bool DenyAccessBasedOnPublicSdk(std::string_view type_descriptor) const override; + // Enable or disable public sdk checks. void SetEnablePublicSdkChecks(bool enabled) override; + // Transaction support. + EXPORT bool IsActiveTransaction() const; + // EnterTransactionMode may suspend. + EXPORT void EnterTransactionMode(bool strict, mirror::Class* root) + REQUIRES_SHARED(Locks::mutator_lock_); + EXPORT void ExitTransactionMode(); + EXPORT void RollbackAllTransactions() REQUIRES_SHARED(Locks::mutator_lock_); + // Transaction rollback and exit transaction are always done together, it's convenience to + // do them in one function. + void RollbackAndExitTransactionMode() REQUIRES_SHARED(Locks::mutator_lock_); + const Transaction* GetTransaction() const; + Transaction* GetTransaction(); + bool IsActiveStrictTransactionMode() const; + + // Transaction constraint checks for AOT compilation. + bool TransactionWriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) override + REQUIRES_SHARED(Locks::mutator_lock_); + bool TransactionWriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value) override + REQUIRES_SHARED(Locks::mutator_lock_); + // Note: The read constraint check is non-virtual because it's not needed by `UnstartedRuntime`. + bool TransactionReadConstraint(Thread* self, ObjPtr<mirror::Object> obj) + REQUIRES_SHARED(Locks::mutator_lock_); + bool TransactionAllocationConstraint(Thread* self, ObjPtr<mirror::Class> klass) override + REQUIRES_SHARED(Locks::mutator_lock_); + + // Transaction bookkeeping for AOT compilation. + void RecordWriteFieldBoolean(mirror::Object* obj, + MemberOffset field_offset, + uint8_t value, + bool is_volatile) override; + void RecordWriteFieldByte(mirror::Object* obj, + MemberOffset field_offset, + int8_t value, + bool is_volatile) override; + void RecordWriteFieldChar(mirror::Object* obj, + MemberOffset field_offset, + uint16_t value, + bool is_volatile) override; + void RecordWriteFieldShort(mirror::Object* obj, + MemberOffset field_offset, + int16_t value, + bool is_volatile) override; + void RecordWriteField32(mirror::Object* obj, + MemberOffset field_offset, + uint32_t value, + bool is_volatile) override; + void RecordWriteField64(mirror::Object* obj, + MemberOffset field_offset, + uint64_t value, + bool is_volatile) override; + void RecordWriteFieldReference(mirror::Object* obj, + MemberOffset field_offset, + ObjPtr<mirror::Object> value, + bool is_volatile) override + REQUIRES_SHARED(Locks::mutator_lock_); + void RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) override + REQUIRES_SHARED(Locks::mutator_lock_); + void RecordStrongStringInsertion(ObjPtr<mirror::String> s) override + REQUIRES(Locks::intern_table_lock_); + void RecordWeakStringInsertion(ObjPtr<mirror::String> s) override + REQUIRES(Locks::intern_table_lock_); + void RecordStrongStringRemoval(ObjPtr<mirror::String> s) override + REQUIRES(Locks::intern_table_lock_); + void RecordWeakStringRemoval(ObjPtr<mirror::String> s) override + REQUIRES(Locks::intern_table_lock_); + void RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, dex::StringIndex string_idx) override + REQUIRES_SHARED(Locks::mutator_lock_); + void RecordResolveMethodType(ObjPtr<mirror::DexCache> dex_cache, dex::ProtoIndex proto_idx) + override REQUIRES_SHARED(Locks::mutator_lock_); + + // Aborting transactions for AOT compilation. + void ThrowTransactionAbortError(Thread* self) override + REQUIRES_SHARED(Locks::mutator_lock_); + void AbortTransactionF(Thread* self, const char* fmt, ...) override + __attribute__((__format__(__printf__, 3, 4))) + REQUIRES_SHARED(Locks::mutator_lock_); + void AbortTransactionV(Thread* self, const char* fmt, va_list args) override + REQUIRES_SHARED(Locks::mutator_lock_); + bool IsTransactionAborted() const override; + + void VisitTransactionRoots(RootVisitor* visitor) override + REQUIRES_SHARED(Locks::mutator_lock_); + protected: // Overridden version of PerformClassVerification allows skipping verification if the class was // previously verified but unloaded. @@ -76,6 +169,12 @@ class AotClassLinker : public ClassLinker { private: std::unique_ptr<SdkChecker> sdk_checker_; + + // Transactions used for pre-initializing classes at compilation time. + // Support nested transactions, maintain a list containing all transactions. Transactions are + // handled under a stack discipline. Because GC needs to go over all transactions, we choose list + // as substantial data structure instead of stack. + std::forward_list<Transaction> preinitialization_transactions_; }; } // namespace art diff --git a/runtime/oat/elf_file.cc b/runtime/oat/elf_file.cc index 30e0197470..91af410471 100644 --- a/runtime/oat/elf_file.cc +++ b/runtime/oat/elf_file.cc @@ -1025,6 +1025,20 @@ bool ElfFileImpl<ElfTypes>::GetLoadedSize(size_t* size, std::string* error_msg) return GetLoadedAddressRange(&vaddr_begin, size, error_msg); } +template <typename ElfTypes> +size_t ElfFileImpl<ElfTypes>::GetElfSegmentAlignmentFromFile() const { + // Return the alignment of the first loadable program segment. + for (Elf_Word i = 0; i < GetProgramHeaderNum(); i++) { + Elf_Phdr* program_header = GetProgramHeader(i); + if (program_header->p_type != PT_LOAD) { + continue; + } + return program_header->p_align; + } + LOG(ERROR) << "No loadable segment found in ELF file " << file_path_; + return 0; +} + // Base on bionic phdr_table_get_load_size template <typename ElfTypes> bool ElfFileImpl<ElfTypes>::GetLoadedAddressRange(/*out*/uint8_t** vaddr_begin, @@ -1671,6 +1685,10 @@ bool ElfFile::GetLoadedSize(size_t* size, std::string* error_msg) const { DELEGATE_TO_IMPL(GetLoadedSize, size, error_msg); } +size_t ElfFile::GetElfSegmentAlignmentFromFile() const { + DELEGATE_TO_IMPL(GetElfSegmentAlignmentFromFile); +} + bool ElfFile::Strip(File* file, std::string* error_msg) { std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, /*low_4gb=*/false, error_msg)); if (elf_file.get() == nullptr) { diff --git a/runtime/oat/elf_file.h b/runtime/oat/elf_file.h index a9218106de..125b4441f1 100644 --- a/runtime/oat/elf_file.h +++ b/runtime/oat/elf_file.h @@ -82,6 +82,8 @@ class ElfFile { bool GetLoadedSize(size_t* size, std::string* error_msg) const; + size_t GetElfSegmentAlignmentFromFile() const; + // Strip an ELF file of unneeded debugging information. // Returns true on success, false on failure. static bool Strip(File* file, std::string* error_msg); diff --git a/runtime/oat/elf_file_impl.h b/runtime/oat/elf_file_impl.h index ffc6a2c081..67c60e906c 100644 --- a/runtime/oat/elf_file_impl.h +++ b/runtime/oat/elf_file_impl.h @@ -116,6 +116,9 @@ class ElfFileImpl { // Retrieves the expected size when the file is loaded at runtime. Returns true if successful. bool GetLoadedSize(size_t* size, std::string* error_msg) const; + // Get the alignment of the first loadable program segment. Return 0 if no loadable segment found. + size_t GetElfSegmentAlignmentFromFile() const; + // Load segments into memory based on PT_LOAD program headers. // executable is true at run time, false at compile time. bool Load(File* file, diff --git a/runtime/oat/oat.h b/runtime/oat/oat.h index 84c169a365..b850fe8dd5 100644 --- a/runtime/oat/oat.h +++ b/runtime/oat/oat.h @@ -44,8 +44,8 @@ std::ostream& operator<<(std::ostream& stream, StubType stub_type); class EXPORT PACKED(4) OatHeader { public: static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } }; - // Last oat version changed reason: BSS mappings for boot image extension. - static constexpr std::array<uint8_t, 4> kOatVersion{{'2', '4', '3', '\0'}}; + // Last oat version changed reason: Implement `HLoadClass::LoadKind::kAppImageRelRo`. + static constexpr std::array<uint8_t, 4> kOatVersion{{'2', '4', '4', '\0'}}; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; static constexpr const char* kDebuggableKey = "debuggable"; diff --git a/runtime/oat/oat_file.cc b/runtime/oat/oat_file.cc index 889b78f015..8a6ab21c76 100644 --- a/runtime/oat/oat_file.cc +++ b/runtime/oat/oat_file.cc @@ -373,6 +373,11 @@ bool OatFileBase::ComputeFields(const std::string& file_path, std::string* error } // Readjust to be non-inclusive upper bound. data_img_rel_ro_end_ += sizeof(uint32_t); + data_img_rel_ro_app_image_ = + FindDynamicSymbolAddress("oatdataimgrelroappimage", &symbol_error_msg); + if (data_img_rel_ro_app_image_ == nullptr) { + data_img_rel_ro_app_image_ = data_img_rel_ro_end_; + } } bss_begin_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbss", &symbol_error_msg)); @@ -648,10 +653,15 @@ bool OatFileBase::Setup(int zip_fd, if (!IsAligned<sizeof(uint32_t)>(data_img_rel_ro_begin_) || !IsAligned<sizeof(uint32_t)>(data_img_rel_ro_end_) || - data_img_rel_ro_begin_ > data_img_rel_ro_end_) { - *error_msg = ErrorPrintf("unaligned or unordered databimgrelro symbol(s): begin = %p, end = %p", - data_img_rel_ro_begin_, - data_img_rel_ro_end_); + !IsAligned<sizeof(uint32_t)>(data_img_rel_ro_app_image_) || + data_img_rel_ro_begin_ > data_img_rel_ro_end_ || + data_img_rel_ro_begin_ > data_img_rel_ro_app_image_ || + data_img_rel_ro_app_image_ > data_img_rel_ro_end_) { + *error_msg = ErrorPrintf( + "unaligned or unordered databimgrelro symbol(s): begin = %p, end = %p, app_image = %p", + data_img_rel_ro_begin_, + data_img_rel_ro_end_, + data_img_rel_ro_app_image_); return false; } @@ -2015,6 +2025,7 @@ OatFile::OatFile(const std::string& location, bool is_executable) end_(nullptr), data_img_rel_ro_begin_(nullptr), data_img_rel_ro_end_(nullptr), + data_img_rel_ro_app_image_(nullptr), bss_begin_(nullptr), bss_end_(nullptr), bss_methods_(nullptr), @@ -2022,6 +2033,7 @@ OatFile::OatFile(const std::string& location, bool is_executable) is_executable_(is_executable), vdex_begin_(nullptr), vdex_end_(nullptr), + app_image_begin_(nullptr), secondary_lookup_lock_("OatFile secondary lookup lock", kOatFileSecondaryLookupLock) { CHECK(!location_.empty()); } @@ -2054,9 +2066,25 @@ const uint8_t* OatFile::DexEnd() const { ArrayRef<const uint32_t> OatFile::GetBootImageRelocations() const { if (data_img_rel_ro_begin_ != nullptr) { - const uint32_t* relocations = reinterpret_cast<const uint32_t*>(data_img_rel_ro_begin_); - const uint32_t* relocations_end = reinterpret_cast<const uint32_t*>(data_img_rel_ro_end_); - return ArrayRef<const uint32_t>(relocations, relocations_end - relocations); + const uint32_t* boot_image_relocations = + reinterpret_cast<const uint32_t*>(data_img_rel_ro_begin_); + const uint32_t* boot_image_relocations_end = + reinterpret_cast<const uint32_t*>(data_img_rel_ro_app_image_); + return ArrayRef<const uint32_t>( + boot_image_relocations, boot_image_relocations_end - boot_image_relocations); + } else { + return ArrayRef<const uint32_t>(); + } +} + +ArrayRef<const uint32_t> OatFile::GetAppImageRelocations() const { + if (data_img_rel_ro_begin_ != nullptr) { + const uint32_t* app_image_relocations = + reinterpret_cast<const uint32_t*>(data_img_rel_ro_app_image_); + const uint32_t* app_image_relocations_end = + reinterpret_cast<const uint32_t*>(data_img_rel_ro_end_); + return ArrayRef<const uint32_t>( + app_image_relocations, app_image_relocations_end - app_image_relocations); } else { return ArrayRef<const uint32_t>(); } @@ -2311,7 +2339,7 @@ OatFile::OatClass OatDexFile::GetOatClass(uint16_t class_def_index) const { } const dex::ClassDef* OatDexFile::FindClassDef(const DexFile& dex_file, - const char* descriptor, + std::string_view descriptor, size_t hash) { const OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); DCHECK_EQ(ComputeModifiedUtf8Hash(descriptor), hash); @@ -2476,7 +2504,7 @@ void OatFile::InitializeRelocations() const { DCHECK(IsExecutable()); // Initialize the .data.img.rel.ro section. - if (!GetBootImageRelocations().empty()) { + if (DataImgRelRoEnd() != DataImgRelRoBegin()) { uint8_t* reloc_begin = const_cast<uint8_t*>(DataImgRelRoBegin()); CheckedCall(mprotect, "un-protect boot image relocations", @@ -2487,6 +2515,13 @@ void OatFile::InitializeRelocations() const { for (const uint32_t& relocation : GetBootImageRelocations()) { const_cast<uint32_t&>(relocation) += boot_image_begin; } + if (!GetAppImageRelocations().empty()) { + CHECK(app_image_begin_ != nullptr); + uint32_t app_image_begin = reinterpret_cast32<uint32_t>(app_image_begin_); + for (const uint32_t& relocation : GetAppImageRelocations()) { + const_cast<uint32_t&>(relocation) += app_image_begin; + } + } CheckedCall(mprotect, "protect boot image relocations", reloc_begin, diff --git a/runtime/oat/oat_file.h b/runtime/oat/oat_file.h index e17f4c43ff..d37dc76c54 100644 --- a/runtime/oat/oat_file.h +++ b/runtime/oat/oat_file.h @@ -181,6 +181,12 @@ class OatFile { ClassLoaderContext* context, std::string* error_msg); + // Set the start of the app image. + // Needed for initializing app image relocations in the .data.img.rel.ro section. + void SetAppImageBegin(uint8_t* app_image_begin) const { + app_image_begin_ = app_image_begin; + } + // Return whether the `OatFile` uses a vdex-only file. bool IsBackedByVdexOnly() const; @@ -329,6 +335,10 @@ class OatFile { return DataImgRelRoEnd() - DataImgRelRoBegin(); } + size_t DataImgRelRoAppImageOffset() const { + return DataImgRelRoAppImage() - DataImgRelRoBegin(); + } + size_t BssSize() const { return BssEnd() - BssBegin(); } @@ -356,6 +366,7 @@ class OatFile { const uint8_t* DataImgRelRoBegin() const { return data_img_rel_ro_begin_; } const uint8_t* DataImgRelRoEnd() const { return data_img_rel_ro_end_; } + const uint8_t* DataImgRelRoAppImage() const { return data_img_rel_ro_app_image_; } const uint8_t* BssBegin() const { return bss_begin_; } const uint8_t* BssEnd() const { return bss_end_; } @@ -367,6 +378,7 @@ class OatFile { EXPORT const uint8_t* DexEnd() const; EXPORT ArrayRef<const uint32_t> GetBootImageRelocations() const; + EXPORT ArrayRef<const uint32_t> GetAppImageRelocations() const; EXPORT ArrayRef<ArtMethod*> GetBssMethods() const; EXPORT ArrayRef<GcRoot<mirror::Object>> GetBssGcRoots() const; @@ -429,16 +441,20 @@ class OatFile { // Pointer to the end of the .data.img.rel.ro section, if present, otherwise null. const uint8_t* data_img_rel_ro_end_; + // Pointer to the beginning of the app image relocations in the .data.img.rel.ro section, + // if present, otherwise null. + const uint8_t* data_img_rel_ro_app_image_; + // Pointer to the .bss section, if present, otherwise null. uint8_t* bss_begin_; // Pointer to the end of the .bss section, if present, otherwise null. uint8_t* bss_end_; - // Pointer to the beginning of the ArtMethod*s in .bss section, if present, otherwise null. + // Pointer to the beginning of the ArtMethod*s in the .bss section, if present, otherwise null. uint8_t* bss_methods_; - // Pointer to the beginning of the GC roots in .bss section, if present, otherwise null. + // Pointer to the beginning of the GC roots in the .bss section, if present, otherwise null. uint8_t* bss_roots_; // Was this oat_file loaded executable? @@ -450,6 +466,9 @@ class OatFile { // Pointer to the end of the .vdex section, if present, otherwise null. uint8_t* vdex_end_; + // Pointer to the beginning of the app image, if any. + mutable uint8_t* app_image_begin_; + // Owning storage for the OatDexFile objects. std::vector<const OatDexFile*> oat_dex_files_storage_; @@ -585,7 +604,7 @@ class OatDexFile final { // Looks up a class definition by its class descriptor. Hash must be // ComputeModifiedUtf8Hash(descriptor). EXPORT static const dex::ClassDef* FindClassDef(const DexFile& dex_file, - const char* descriptor, + std::string_view descriptor, size_t hash); const TypeLookupTable& GetTypeLookupTable() const { diff --git a/runtime/oat/oat_file_manager.cc b/runtime/oat/oat_file_manager.cc index 11333b0249..fea361b8ee 100644 --- a/runtime/oat/oat_file_manager.cc +++ b/runtime/oat/oat_file_manager.cc @@ -312,6 +312,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader))); // Can not load app image without class loader. if (h_loader != nullptr) { + oat_file->SetAppImageBegin(image_space->Begin()); std::string temp_error_msg; // Add image space has a race condition since other threads could be reading from the // spaces array. @@ -344,6 +345,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( } } else { LOG(INFO) << "Failed to add image file: " << temp_error_msg; + oat_file->SetAppImageBegin(nullptr); dex_files.clear(); { ScopedThreadSuspension sts(self, ThreadState::kSuspended); diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc index d950675b14..fb877d3ec5 100644 --- a/runtime/proxy_test.cc +++ b/runtime/proxy_test.cc @@ -114,6 +114,7 @@ TEST_F(ProxyTest, ProxyFieldHelper) { ArtField* field = &static_fields->At(0); EXPECT_STREQ("interfaces", field->GetName()); EXPECT_STREQ("[Ljava/lang/Class;", field->GetTypeDescriptor()); + EXPECT_EQ("[Ljava/lang/Class;", field->GetTypeDescriptorView()); EXPECT_OBJ_PTR_EQ(interfacesFieldClass.Get(), field->ResolveType()); std::string temp; EXPECT_STREQ("L$Proxy1234;", field->GetDeclaringClass()->GetDescriptor(&temp)); @@ -123,6 +124,7 @@ TEST_F(ProxyTest, ProxyFieldHelper) { field = &static_fields->At(1); EXPECT_STREQ("throws", field->GetName()); EXPECT_STREQ("[[Ljava/lang/Class;", field->GetTypeDescriptor()); + EXPECT_EQ("[[Ljava/lang/Class;", field->GetTypeDescriptorView()); EXPECT_OBJ_PTR_EQ(throwsFieldClass.Get(), field->ResolveType()); EXPECT_STREQ("L$Proxy1234;", field->GetDeclaringClass()->GetDescriptor(&temp)); EXPECT_FALSE(field->IsPrimitiveType()); diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 2ebcbb0fd0..37734f7d74 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -439,19 +439,41 @@ class DeoptimizeStackVisitor final : public StackVisitor { ArtMethod* method = GetMethod(); VLOG(deopt) << "Deoptimizing stack: depth: " << GetFrameDepth() << " at method " << ArtMethod::PrettyMethod(method); + if (method == nullptr || single_frame_done_) { FinishStackWalk(); return false; // End stack walk. - } else if (method->IsRuntimeMethod()) { + } + + // Update if method exit event needs to be reported. We should report exit event only if we + // have reported an entry event. So tell interpreter if/ an entry event was reported. + bool supports_exit_events = Runtime::Current()->GetInstrumentation()->MethodSupportsExitEvents( + method, GetCurrentOatQuickMethodHeader()); + + if (method->IsRuntimeMethod()) { // Ignore callee save method. DCHECK(method->IsCalleeSaveMethod()); return true; } else if (method->IsNative()) { // If we return from JNI with a pending exception and want to deoptimize, we need to skip // the native method. The top method is a runtime method, the native method comes next. - // We also deoptimize due to method instrumentation reasons from method entry / exit - // callbacks. In these cases native method is at the top of stack. + // We also deoptimize due to method instrumentation reasons from method exit callbacks. + // In these cases native method is at the top of stack. CHECK((GetFrameDepth() == 1U) || (GetFrameDepth() == 0U)); + // We see a native frame when: + // 1. returning from JNI with a pending exception + // 2. deopting from method exit callbacks (with or without a pending exception). + // skip_method_exit_callbacks_ is set in this case + // 3. handling async exception on suspend points for fast native methods. + // We only need to call method unwind event in the first case. + if (supports_exit_events && + !skip_method_exit_callbacks_ && + GetThread()->IsExceptionPending()) { + // An exception has occurred in a native method and we are deoptimizing past the native + // method. So report method unwind event here. + Runtime::Current()->GetInstrumentation()->MethodUnwindEvent( + GetThread(), method, dex::kDexNoIndex); + } callee_method_ = method; return true; } else if (!single_frame_deopt_ && @@ -482,11 +504,6 @@ class DeoptimizeStackVisitor final : public StackVisitor { } else { HandleOptimizingDeoptimization(method, new_frame, updated_vregs); } - // Update if method exit event needs to be reported. We should report exit event only if we - // have reported an entry event. So tell interpreter if/ an entry event was reported. - bool supports_exit_events = - Runtime::Current()->GetInstrumentation()->MethodSupportsExitEvents( - method, GetCurrentOatQuickMethodHeader()); new_frame->SetSkipMethodExitEvents(!supports_exit_events); // If we are deoptimizing after method exit callback we shouldn't call the method exit // callbacks again for the top frame. We may have to deopt after the callback if the callback diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 31dd8a4e0e..5c394b136a 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -496,7 +496,8 @@ bool InvokeMethodImpl(const ScopedObjectAccessAlreadyRunnable& soa, if (soa.Self()->IsExceptionPending()) { // To abort a transaction we use a fake exception that should never be caught by the bytecode // and therefore it makes no sense to wrap it. - if (Runtime::Current()->IsTransactionAborted()) { + if (Runtime::Current()->IsActiveTransaction() && + Runtime::Current()->GetClassLinker()->IsTransactionAborted()) { DCHECK(soa.Self()->GetException()->GetClass()->DescriptorEquals( "Ldalvik/system/TransactionAbortError;")) << soa.Self()->GetException()->GetClass()->PrettyDescriptor(); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index df66c5e175..bb60a863cd 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -32,16 +32,17 @@ #include <crt_externs.h> // for _NSGetEnviron #endif +#include <android-base/properties.h> +#include <android-base/strings.h> +#include <string.h> + #include <cstdio> #include <cstdlib> #include <limits> -#include <string.h> #include <thread> #include <unordered_set> #include <vector> -#include "android-base/strings.h" - #include "arch/arm/registers_arm.h" #include "arch/arm64/registers_arm64.h" #include "arch/context.h" @@ -75,8 +76,8 @@ #include "debugger.h" #include "dex/art_dex_file_loader.h" #include "dex/dex_file_loader.h" -#include "entrypoints/runtime_asm_entrypoints.h" #include "entrypoints/entrypoint_utils-inl.h" +#include "entrypoints/runtime_asm_entrypoints.h" #include "experimental_flags.h" #include "fault_handler.h" #include "gc/accounting/card_table-inl.h" @@ -116,8 +117,8 @@ #include "mirror/throwable.h" #include "mirror/var_handle.h" #include "monitor.h" -#include "native/dalvik_system_DexFile.h" #include "native/dalvik_system_BaseDexClassLoader.h" +#include "native/dalvik_system_DexFile.h" #include "native/dalvik_system_VMDebug.h" #include "native/dalvik_system_VMRuntime.h" #include "native/dalvik_system_VMStack.h" @@ -143,12 +144,12 @@ #include "native/java_lang_reflect_Parameter.h" #include "native/java_lang_reflect_Proxy.h" #include "native/java_util_concurrent_atomic_AtomicLong.h" +#include "native/jdk_internal_misc_Unsafe.h" #include "native/libcore_io_Memory.h" #include "native/libcore_util_CharsetUtils.h" #include "native/org_apache_harmony_dalvik_ddmc_DdmServer.h" #include "native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.h" #include "native/sun_misc_Unsafe.h" -#include "native/jdk_internal_misc_Unsafe.h" #include "native_bridge_art_interface.h" #include "native_stack_dump.h" #include "nativehelper/scoped_local_ref.h" @@ -177,7 +178,6 @@ #include "thread_list.h" #include "ti/agent.h" #include "trace.h" -#include "transaction.h" #include "vdex_file.h" #include "verifier/class_verifier.h" #include "well_known_classes-inl.h" @@ -286,7 +286,7 @@ Runtime::Runtime() system_thread_group_(nullptr), system_class_loader_(nullptr), dump_gc_performance_on_shutdown_(false), - preinitialization_transactions_(), + active_transaction_(false), verify_(verifier::VerifyMode::kNone), target_sdk_version_(static_cast<uint32_t>(SdkVersion::kUnset)), compat_framework_(), @@ -766,14 +766,14 @@ static void WaitUntilSingleThreaded() { #if defined(__linux__) // Read num_threads field from /proc/self/stat, avoiding higher-level IO libraries that may // break atomicity of the read. - static constexpr size_t kNumTries = 1000; + static constexpr size_t kNumTries = 1500; static constexpr size_t kNumThreadsIndex = 20; static constexpr size_t BUF_SIZE = 500; static constexpr size_t BUF_PRINT_SIZE = 150; // Only log this much on failure to limit length. static_assert(BUF_SIZE > BUF_PRINT_SIZE); char buf[BUF_SIZE]; size_t bytes_read = 0; - + uint64_t millis = 0; for (size_t tries = 0; tries < kNumTries; ++tries) { bytes_read = GetOsThreadStat(getpid(), buf, BUF_SIZE); CHECK_NE(bytes_read, 0u); @@ -794,6 +794,9 @@ static void WaitUntilSingleThreaded() { if (buf[pos] == '1') { return; // num_threads == 1; success. } + if (millis == 0) { + millis = MilliTime(); + } usleep(1000); } buf[std::min(BUF_PRINT_SIZE, bytes_read)] = '\0'; // Truncate buf before printing. @@ -804,7 +807,7 @@ static void WaitUntilSingleThreaded() { CHECK_NE(bytes_read, 0u); LOG(ERROR) << "After re-read: bytes_read = " << bytes_read << " stat contents = \"" << buf << "...\""; - LOG(FATAL) << "Failed to reach single-threaded state"; + LOG(FATAL) << "Failed to reach single-threaded state: wait_time = " << MilliTime() - millis; #else // Not Linux; shouldn't matter, but this has a high probability of working slowly. usleep(20'000); #endif @@ -1509,6 +1512,14 @@ static std::vector<File> FileFdsToFileObjects(std::vector<int>&& fds) { return files; } +inline static uint64_t GetThreadSuspendTimeout(const RuntimeArgumentMap* runtime_options) { + auto suspend_timeout_opt = runtime_options->GetOptional(RuntimeArgumentMap::ThreadSuspendTimeout); + return suspend_timeout_opt.has_value() ? + suspend_timeout_opt.value().GetNanoseconds() : + ThreadList::kDefaultThreadSuspendTimeout * + android::base::GetIntProperty("ro.hw_timeout_multiplier", 1); +} + bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // (b/30160149): protect subprocesses from modifications to LD_LIBRARY_PATH, etc. // Take a snapshot of the environment at the time the runtime was created, for use by Exec, etc. @@ -1654,7 +1665,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { monitor_list_ = new MonitorList; monitor_pool_ = MonitorPool::Create(); - thread_list_ = new ThreadList(runtime_options.GetOrDefault(Opt::ThreadSuspendTimeout)); + thread_list_ = new ThreadList(GetThreadSuspendTimeout(&runtime_options)); intern_table_ = new InternTable; monitor_timeout_enable_ = runtime_options.GetOrDefault(Opt::MonitorTimeoutEnable); @@ -2649,12 +2660,6 @@ void Runtime::VisitConcurrentRoots(RootVisitor* visitor, VisitRootFlags flags) { } } -void Runtime::VisitTransactionRoots(RootVisitor* visitor) { - for (Transaction& transaction : preinitialization_transactions_) { - transaction.VisitRoots(visitor); - } -} - void Runtime::VisitNonThreadRoots(RootVisitor* visitor) { java_vm_->VisitRoots(visitor); sentinel_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); @@ -2666,7 +2671,7 @@ void Runtime::VisitNonThreadRoots(RootVisitor* visitor) { .VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); pre_allocated_NoClassDefFoundError_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); VisitImageRoots(visitor); - VisitTransactionRoots(visitor); + class_linker_->VisitTransactionRoots(visitor); } void Runtime::VisitNonConcurrentRoots(RootVisitor* visitor, VisitRootFlags flags) { @@ -2919,205 +2924,6 @@ void Runtime::RegisterAppInfo(const std::string& package_name, jit_->StartProfileSaver(profile_output_filename, code_paths, ref_profile_filename); } -// Transaction support. -bool Runtime::IsActiveTransaction() const { - return !preinitialization_transactions_.empty() && !GetTransaction()->IsRollingBack(); -} - -void Runtime::EnterTransactionMode(bool strict, mirror::Class* root) { - DCHECK(IsAotCompiler()); - ArenaPool* arena_pool = nullptr; - ArenaStack* arena_stack = nullptr; - if (preinitialization_transactions_.empty()) { // Top-level transaction? - // Make initialized classes visibly initialized now. If that happened during the transaction - // and then the transaction was aborted, we would roll back the status update but not the - // ClassLinker's bookkeeping structures, so these classes would never be visibly initialized. - { - Thread* self = Thread::Current(); - StackHandleScope<1> hs(self); - HandleWrapper<mirror::Class> h(hs.NewHandleWrapper(&root)); - ScopedThreadSuspension sts(self, ThreadState::kNative); - GetClassLinker()->MakeInitializedClassesVisiblyInitialized(Thread::Current(), /*wait=*/ true); - } - // Pass the runtime `ArenaPool` to the transaction. - arena_pool = GetArenaPool(); - } else { - // Pass the `ArenaStack` from previous transaction to the new one. - arena_stack = preinitialization_transactions_.front().GetArenaStack(); - } - preinitialization_transactions_.emplace_front(strict, root, arena_stack, arena_pool); -} - -void Runtime::ExitTransactionMode() { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - preinitialization_transactions_.pop_front(); -} - -void Runtime::RollbackAndExitTransactionMode() { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - preinitialization_transactions_.front().Rollback(); - preinitialization_transactions_.pop_front(); -} - -bool Runtime::IsTransactionAborted() const { - if (!IsActiveTransaction()) { - return false; - } else { - DCHECK(IsAotCompiler()); - return GetTransaction()->IsAborted(); - } -} - -void Runtime::RollbackAllTransactions() { - // If transaction is aborted, all transactions will be kept in the list. - // Rollback and exit all of them. - while (IsActiveTransaction()) { - RollbackAndExitTransactionMode(); - } -} - -bool Runtime::IsActiveStrictTransactionMode() const { - return IsActiveTransaction() && GetTransaction()->IsStrict(); -} - -const Transaction* Runtime::GetTransaction() const { - DCHECK(!preinitialization_transactions_.empty()); - return &preinitialization_transactions_.front(); -} - -Transaction* Runtime::GetTransaction() { - DCHECK(!preinitialization_transactions_.empty()); - return &preinitialization_transactions_.front(); -} - -void Runtime::AbortTransactionAndThrowAbortError(Thread* self, const std::string& abort_message) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - // Throwing an exception may cause its class initialization. If we mark the transaction - // aborted before that, we may warn with a false alarm. Throwing the exception before - // marking the transaction aborted avoids that. - // But now the transaction can be nested, and abort the transaction will relax the constraints - // for constructing stack trace. - GetTransaction()->Abort(abort_message); - GetTransaction()->ThrowAbortError(self, &abort_message); -} - -void Runtime::ThrowTransactionAbortError(Thread* self) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - // Passing nullptr means we rethrow an exception with the earlier transaction abort message. - GetTransaction()->ThrowAbortError(self, nullptr); -} - -void Runtime::RecordWriteFieldBoolean(mirror::Object* obj, - MemberOffset field_offset, - uint8_t value, - bool is_volatile) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - GetTransaction()->RecordWriteFieldBoolean(obj, field_offset, value, is_volatile); -} - -void Runtime::RecordWriteFieldByte(mirror::Object* obj, - MemberOffset field_offset, - int8_t value, - bool is_volatile) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - GetTransaction()->RecordWriteFieldByte(obj, field_offset, value, is_volatile); -} - -void Runtime::RecordWriteFieldChar(mirror::Object* obj, - MemberOffset field_offset, - uint16_t value, - bool is_volatile) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - GetTransaction()->RecordWriteFieldChar(obj, field_offset, value, is_volatile); -} - -void Runtime::RecordWriteFieldShort(mirror::Object* obj, - MemberOffset field_offset, - int16_t value, - bool is_volatile) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - GetTransaction()->RecordWriteFieldShort(obj, field_offset, value, is_volatile); -} - -void Runtime::RecordWriteField32(mirror::Object* obj, - MemberOffset field_offset, - uint32_t value, - bool is_volatile) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - GetTransaction()->RecordWriteField32(obj, field_offset, value, is_volatile); -} - -void Runtime::RecordWriteField64(mirror::Object* obj, - MemberOffset field_offset, - uint64_t value, - bool is_volatile) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - GetTransaction()->RecordWriteField64(obj, field_offset, value, is_volatile); -} - -void Runtime::RecordWriteFieldReference(mirror::Object* obj, - MemberOffset field_offset, - ObjPtr<mirror::Object> value, - bool is_volatile) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - GetTransaction()->RecordWriteFieldReference(obj, field_offset, value.Ptr(), is_volatile); -} - -void Runtime::RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - GetTransaction()->RecordWriteArray(array, index, value); -} - -void Runtime::RecordStrongStringInsertion(ObjPtr<mirror::String> s) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - GetTransaction()->RecordStrongStringInsertion(s); -} - -void Runtime::RecordWeakStringInsertion(ObjPtr<mirror::String> s) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - GetTransaction()->RecordWeakStringInsertion(s); -} - -void Runtime::RecordStrongStringRemoval(ObjPtr<mirror::String> s) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - GetTransaction()->RecordStrongStringRemoval(s); -} - -void Runtime::RecordWeakStringRemoval(ObjPtr<mirror::String> s) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - GetTransaction()->RecordWeakStringRemoval(s); -} - -void Runtime::RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, - dex::StringIndex string_idx) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - GetTransaction()->RecordResolveString(dex_cache, string_idx); -} - -void Runtime::RecordResolveMethodType(ObjPtr<mirror::DexCache> dex_cache, - dex::ProtoIndex proto_idx) { - DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); - GetTransaction()->RecordResolveMethodType(dex_cache, proto_idx); -} - void Runtime::SetFaultMessage(const std::string& message) { std::string* new_msg = new std::string(message); std::string* cur_msg = fault_message_.exchange(new_msg); diff --git a/runtime/runtime.h b/runtime/runtime.h index 1a1c28bff5..96f76652d2 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -20,7 +20,6 @@ #include <jni.h> #include <stdio.h> -#include <forward_list> #include <iosfwd> #include <memory> #include <optional> @@ -120,7 +119,6 @@ class ThreadList; class ThreadPool; class Trace; struct TraceConfig; -class Transaction; using RuntimeOptions = std::vector<std::pair<std::string, const void*>>; @@ -482,9 +480,6 @@ class Runtime { void VisitNonThreadRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - void VisitTransactionRoots(RootVisitor* visitor) - REQUIRES_SHARED(Locks::mutator_lock_); - // Sweep system weaks, the system weak is deleted if the visitor return null. Otherwise, the // system weak is updated to be the visitor's returned value. EXPORT void SweepSystemWeaks(IsMarkedVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); @@ -626,68 +621,19 @@ class Runtime { const std::string& ref_profile_filename, int32_t code_type); - // Transaction support. - EXPORT bool IsActiveTransaction() const; - // EnterTransactionMode may suspend. - EXPORT void EnterTransactionMode(bool strict, mirror::Class* root) - REQUIRES_SHARED(Locks::mutator_lock_); - EXPORT void ExitTransactionMode(); - EXPORT void RollbackAllTransactions() REQUIRES_SHARED(Locks::mutator_lock_); - // Transaction rollback and exit transaction are always done together, it's convenience to - // do them in one function. - void RollbackAndExitTransactionMode() REQUIRES_SHARED(Locks::mutator_lock_); - bool IsTransactionAborted() const; - const Transaction* GetTransaction() const; - Transaction* GetTransaction(); - bool IsActiveStrictTransactionMode() const; - - void AbortTransactionAndThrowAbortError(Thread* self, const std::string& abort_message) - REQUIRES_SHARED(Locks::mutator_lock_); - void ThrowTransactionAbortError(Thread* self) - REQUIRES_SHARED(Locks::mutator_lock_); + void SetActiveTransaction() { + DCHECK(IsAotCompiler()); + active_transaction_ = true; + } - void RecordWriteFieldBoolean(mirror::Object* obj, - MemberOffset field_offset, - uint8_t value, - bool is_volatile); - void RecordWriteFieldByte(mirror::Object* obj, - MemberOffset field_offset, - int8_t value, - bool is_volatile); - void RecordWriteFieldChar(mirror::Object* obj, - MemberOffset field_offset, - uint16_t value, - bool is_volatile); - void RecordWriteFieldShort(mirror::Object* obj, - MemberOffset field_offset, - int16_t value, - bool is_volatile); - EXPORT void RecordWriteField32(mirror::Object* obj, - MemberOffset field_offset, - uint32_t value, - bool is_volatile); - void RecordWriteField64(mirror::Object* obj, - MemberOffset field_offset, - uint64_t value, - bool is_volatile); - EXPORT void RecordWriteFieldReference(mirror::Object* obj, - MemberOffset field_offset, - ObjPtr<mirror::Object> value, - bool is_volatile) REQUIRES_SHARED(Locks::mutator_lock_); - EXPORT void RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) - REQUIRES_SHARED(Locks::mutator_lock_); - void RecordStrongStringInsertion(ObjPtr<mirror::String> s) - REQUIRES(Locks::intern_table_lock_); - void RecordWeakStringInsertion(ObjPtr<mirror::String> s) - REQUIRES(Locks::intern_table_lock_); - void RecordStrongStringRemoval(ObjPtr<mirror::String> s) - REQUIRES(Locks::intern_table_lock_); - void RecordWeakStringRemoval(ObjPtr<mirror::String> s) - REQUIRES(Locks::intern_table_lock_); - void RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, dex::StringIndex string_idx) - REQUIRES_SHARED(Locks::mutator_lock_); - void RecordResolveMethodType(ObjPtr<mirror::DexCache> dex_cache, dex::ProtoIndex proto_idx) - REQUIRES_SHARED(Locks::mutator_lock_); + void ClearActiveTransaction() { + DCHECK(IsAotCompiler()); + active_transaction_ = false; + } + + bool IsActiveTransaction() { + return active_transaction_; + } void SetFaultMessage(const std::string& message); @@ -1394,11 +1340,12 @@ class Runtime { // If true, then we dump the GC cumulative timings on shutdown. bool dump_gc_performance_on_shutdown_; - // Transactions used for pre-initializing classes at compilation time. - // Support nested transactions, maintain a list containing all transactions. Transactions are - // handled under a stack discipline. Because GC needs to go over all transactions, we choose list - // as substantial data structure instead of stack. - std::forward_list<Transaction> preinitialization_transactions_; + // Transactions are handled by the `AotClassLinker` but we keep a simple flag + // in the `Runtime` for quick transaction checks. + // Code that's not AOT-specific but needs some transaction-specific behavior + // shall check this flag and, for an active transaction, make virtual calls + // through `ClassLinker` to `AotClassLinker` to implement that behavior. + bool active_transaction_; // If kNone, verification is disabled. kEnable by default. verifier::VerifyMode verify_; diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 8dcba19113..e82f6566b2 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -70,8 +70,7 @@ RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \ LongPauseLogThreshold, gc::Heap::kDefaultLongPauseLogThreshold) RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \ LongGCLogThreshold, gc::Heap::kDefaultLongGCLogThreshold) -RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \ - ThreadSuspendTimeout, ThreadList::kDefaultThreadSuspendTimeout) +RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, ThreadSuspendTimeout) RUNTIME_OPTIONS_KEY (bool, MonitorTimeoutEnable, false) RUNTIME_OPTIONS_KEY (int, MonitorTimeout, Monitor::kDefaultMonitorTimeoutMs) RUNTIME_OPTIONS_KEY (Unit, DumpGCPerformanceOnShutdown) diff --git a/runtime/runtime_options.h b/runtime/runtime_options.h index 3cadd09bf8..9e127925ee 100644 --- a/runtime/runtime_options.h +++ b/runtime/runtime_options.h @@ -41,8 +41,6 @@ class DexFile; struct XGcOption; struct BackgroundGcOption; -#define DECLARE_KEY(Type, Name) static const Key<Type> Name - // Define a key that is usable with a RuntimeArgumentMap. // This key will *not* work with other subtypes of VariantMap. template <typename TValue> @@ -75,7 +73,7 @@ struct EXPORT RuntimeArgumentMap : VariantMap<RuntimeArgumentMap, RuntimeArgumen #include "runtime_options.def" }; -#undef DECLARE_KEY +#undef RUNTIME_OPTIONS_KEY // using RuntimeOptions = RuntimeArgumentMap; } // namespace art diff --git a/runtime/runtime_test.cc b/runtime/runtime_test.cc index 210ad7d711..cd0c862d82 100644 --- a/runtime/runtime_test.cc +++ b/runtime/runtime_test.cc @@ -21,9 +21,14 @@ #include "android-base/logging.h" #include "base/locks.h" #include "base/mutex.h" +#include "oat/elf_file.h" #include "runtime.h" #include "thread-current-inl.h" +#ifdef ART_TARGET_ANDROID +#include "android-base/properties.h" +#endif + namespace art HIDDEN { class RuntimeTest : public CommonRuntimeTest {}; @@ -75,4 +80,33 @@ TEST_F(RuntimeTest, AbortFromUnattachedThread) { }, ::testing::KilledBySignal(SIGABRT), kDeathRegex); } +// It is possible to run tests that validate an existing deployed on-device ART APEX ('standalone' +// tests). If these tests expect to load ELF files with a particular alignment, but those ELF files +// were created with a different alignment, there will be many difficult-to-debug failures. This +// test aims to identify this mismatch, related to whether or not the runtimes were built to be +// page-size agnostic. +TEST_F(RuntimeTest, ElfAlignmentMismatch) { +#ifdef ART_TARGET_ANDROID + bool platform_pga = android::base::GetBoolProperty("ro.product.build.no_bionic_page_size_macro", + false); + if (kPageSizeAgnostic != platform_pga) { + LOG(WARNING) << "Test configured with kPageSizeAgnostic=" << kPageSizeAgnostic << ", but " + << "platform ro.product.build.no_bionic_page_size_macro=" << platform_pga << "."; + } +#endif + // Determine the alignment of the ART APEX by reading the alignment of boot.oat. + std::string core_oat_location = GetSystemImageFilename(GetCoreOatLocation().c_str(), kRuntimeISA); + std::unique_ptr<File> core_oat_file(OS::OpenFileForReading(core_oat_location.c_str())); + ASSERT_TRUE(core_oat_file.get() != nullptr) << core_oat_location; + + std::string error_msg; + std::unique_ptr<ElfFile> elf_file(ElfFile::Open(core_oat_file.get(), + /*writable=*/false, + /*program_header_only=*/true, + /*low_4gb=*/false, + &error_msg)); + ASSERT_TRUE(elf_file != nullptr) << error_msg; + EXPECT_EQ(kElfSegmentAlignment, elf_file->GetElfSegmentAlignmentFromFile()); +} + } // namespace art diff --git a/runtime/sdk_checker.cc b/runtime/sdk_checker.cc index 0ad8ce5c9e..40987e96b5 100644 --- a/runtime/sdk_checker.cc +++ b/runtime/sdk_checker.cc @@ -47,14 +47,16 @@ bool SdkChecker::ShouldDenyAccess(ArtMethod* art_method) const { return false; } + std::string_view declaring_class_descriptor = art_method->GetDeclaringClassDescriptorView(); + const char* name = art_method->GetName(); + bool found = false; for (const std::unique_ptr<const DexFile>& dex_file : sdk_dex_files_) { - const dex::TypeId* declaring_type_id = - dex_file->FindTypeId(art_method->GetDeclaringClassDescriptor()); + const dex::TypeId* declaring_type_id = dex_file->FindTypeId(declaring_class_descriptor); if (declaring_type_id == nullptr) { continue; } - const dex::StringId* name_id = dex_file->FindStringId(art_method->GetName()); + const dex::StringId* name_id = dex_file->FindStringId(name); if (name_id == nullptr) { continue; } @@ -91,20 +93,21 @@ bool SdkChecker::ShouldDenyAccess(ArtField* art_field) const { return false; } + std::string_view declaring_class_descriptor = art_field->GetDeclaringClassDescriptorView(); + const char* name = art_field->GetName(); + std::string_view type_descriptor = art_field->GetTypeDescriptorView(); + bool found = false; for (const std::unique_ptr<const DexFile>& dex_file : sdk_dex_files_) { - std::string declaring_class; - - const dex::TypeId* declaring_type_id = - dex_file->FindTypeId(art_field->GetDeclaringClass()->GetDescriptor(&declaring_class)); + const dex::TypeId* declaring_type_id = dex_file->FindTypeId(declaring_class_descriptor); if (declaring_type_id == nullptr) { continue; } - const dex::StringId* name_id = dex_file->FindStringId(art_field->GetName()); + const dex::StringId* name_id = dex_file->FindStringId(name); if (name_id == nullptr) { continue; } - const dex::TypeId* type_id = dex_file->FindTypeId(art_field->GetTypeDescriptor()); + const dex::TypeId* type_id = dex_file->FindTypeId(type_descriptor); if (type_id == nullptr) { continue; } @@ -124,7 +127,7 @@ bool SdkChecker::ShouldDenyAccess(ArtField* art_field) const { return !found; } -bool SdkChecker::ShouldDenyAccess(const char* descriptor) const { +bool SdkChecker::ShouldDenyAccess(std::string_view descriptor) const { if (!enabled_) { return false; } diff --git a/runtime/sdk_checker.h b/runtime/sdk_checker.h index 6bd0b2a15b..08e4479846 100644 --- a/runtime/sdk_checker.h +++ b/runtime/sdk_checker.h @@ -59,7 +59,7 @@ class SdkChecker { bool ShouldDenyAccess(ArtField* art_field) const REQUIRES_SHARED(Locks::mutator_lock_); // Similar to ShouldDenyAccess(ArtMethod* art_method). - bool ShouldDenyAccess(const char* type_descriptor) const; + bool ShouldDenyAccess(std::string_view type_descriptor) const; // Enabled/Disable the checks. void SetEnabled(bool enabled) { enabled_ = enabled; } diff --git a/runtime/thread.cc b/runtime/thread.cc index 2ebbe1327d..c648f0f55c 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -4846,7 +4846,7 @@ int Thread::GetNativePriority() const { return priority; } -void Thread::AbortInThis(std::string message) { +void Thread::AbortInThis(const std::string& message) { std::string thread_name; Thread::Current()->GetThreadName(thread_name); LOG(ERROR) << message; diff --git a/runtime/thread.h b/runtime/thread.h index 943a7edff3..35e8fe8716 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -1550,7 +1550,7 @@ class EXPORT Thread { // checkpoint. Useful mostly to discover why a thread isn't responding to a suspend request or // checkpoint. The caller should "suspend" (in the Java sense) 'thread' before invoking this, so // 'thread' can't get deallocated before we access it. - NO_RETURN void AbortInThis(std::string message); + NO_RETURN void AbortInThis(const std::string& message); // Returns true if StrictMode events are traced for the current thread. static bool IsSensitiveThread() { diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 8ecc89c0a1..436daa0b54 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -17,6 +17,9 @@ #include "thread_list.h" #include <dirent.h> +#include <nativehelper/scoped_local_ref.h> +#include <nativehelper/scoped_utf_chars.h> +#include <sys/resource.h> // For getpriority() #include <sys/types.h> #include <unistd.h> @@ -26,10 +29,6 @@ #include <vector> #include "android-base/stringprintf.h" -#include "nativehelper/scoped_local_ref.h" -#include "nativehelper/scoped_utf_chars.h" -#include "unwindstack/AndroidUnwinder.h" - #include "art_field-inl.h" #include "base/aborting.h" #include "base/histogram-inl.h" @@ -52,6 +51,7 @@ #include "scoped_thread_state_change-inl.h" #include "thread.h" #include "trace.h" +#include "unwindstack/AndroidUnwinder.h" #include "well_known_classes.h" #if ART_USE_FUTEXES @@ -714,6 +714,16 @@ std::optional<std::string> ThreadList::WaitForSuspendBarrier(AtomicInteger* barr #endif uint64_t timeout_ns = attempt_of_4 == 0 ? thread_suspend_timeout_ns_ : thread_suspend_timeout_ns_ / 4; + if (attempt_of_4 != 1 && getpriority(PRIO_PROCESS, 0 /* this thread */) > 0) { + // We're a low priority thread, and thus have a longer ANR timeout. Double the suspend + // timeout. To avoid the getpriority system call in the common case, we fail to double the + // first of 4 waits, but then triple the third one to compensate. + if (attempt_of_4 == 3) { + timeout_ns *= 3; + } else { + timeout_ns *= 2; + } + } bool collect_state = (t != 0 && (attempt_of_4 == 0 || attempt_of_4 == 4)); int32_t cur_val = barrier->load(std::memory_order_acquire); if (cur_val <= 0) { diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc index f0ea078cdc..a7c2cd1286 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -86,14 +86,22 @@ ThreadPoolWorker::~ThreadPoolWorker() { CHECK_PTHREAD_CALL(pthread_join, (pthread_, nullptr), "thread pool worker shutdown"); } -void ThreadPoolWorker::SetPthreadPriority(int priority) { +// Set the "nice" priority for tid (0 means self). +static void SetPriorityForTid(pid_t tid, int priority) { CHECK_GE(priority, PRIO_MIN); CHECK_LE(priority, PRIO_MAX); -#if defined(ART_TARGET_ANDROID) - int result = setpriority(PRIO_PROCESS, pthread_gettid_np(pthread_), priority); + int result = setpriority(PRIO_PROCESS, tid, priority); if (result != 0) { +#if defined(ART_TARGET_ANDROID) PLOG(ERROR) << "Failed to setpriority to :" << priority; +#endif + // Setpriority may fail on host due to ulimit issues. } +} + +void ThreadPoolWorker::SetPthreadPriority(int priority) { +#if defined(ART_TARGET_ANDROID) + SetPriorityForTid(pthread_gettid_np(pthread_), priority); #else UNUSED(priority); #endif @@ -144,6 +152,10 @@ void* ThreadPoolWorker::Callback(void* arg) { // Do work until its time to shut down. worker->Run(); runtime->DetachCurrentThread(/* should_run_callbacks= */ false); + // On zygote fork, we wait for this thread to exit completely. Set to highest Java priority + // to speed that up. + constexpr int kJavaMaxPrioNiceness = -8; + SetPriorityForTid(0 /* this thread */, kJavaMaxPrioNiceness); return nullptr; } diff --git a/runtime/transaction.cc b/runtime/transaction.cc index b796df620e..78d3f16ea8 100644 --- a/runtime/transaction.cc +++ b/runtime/transaction.cc @@ -20,6 +20,7 @@ #include "base/mutex-inl.h" #include "base/stl_util.h" +#include "common_throws.h" #include "dex/descriptors_names.h" #include "gc/accounting/card_table-inl.h" #include "gc/heap.h" @@ -104,17 +105,15 @@ void Transaction::Abort(const std::string& abort_message) { void Transaction::ThrowAbortError(Thread* self, const std::string* abort_message) { const bool rethrow = (abort_message == nullptr); if (kIsDebugBuild && rethrow) { - CHECK(IsAborted()) << "Rethrow " << DescriptorToDot(Transaction::kAbortExceptionDescriptor) + CHECK(IsAborted()) << "Rethrow " << DescriptorToDot(kTransactionAbortErrorDescriptor) << " while transaction is not aborted"; } if (rethrow) { // Rethrow an exception with the earlier abort message stored in the transaction. - self->ThrowNewWrappedException(Transaction::kAbortExceptionDescriptor, - GetAbortMessage().c_str()); + self->ThrowNewWrappedException(kTransactionAbortErrorDescriptor, GetAbortMessage().c_str()); } else { // Throw an exception with the given abort message. - self->ThrowNewWrappedException(Transaction::kAbortExceptionDescriptor, - abort_message->c_str()); + self->ThrowNewWrappedException(kTransactionAbortErrorDescriptor, abort_message->c_str()); } } @@ -141,16 +140,15 @@ bool Transaction::WriteValueConstraint(ObjPtr<mirror::Object> value) const { if (value == nullptr) { return false; // We can always store null values. } - gc::Heap* heap = Runtime::Current()->GetHeap(); if (IsStrict()) { // TODO: Should we restrict writes the same way as for boot image extension? return false; - } else if (heap->GetBootImageSpaces().empty()) { + } else if (heap_->GetBootImageSpaces().empty()) { return false; // No constraints for boot image. } else { // Boot image extension. ObjPtr<mirror::Class> klass = value->IsClass() ? value->AsClass() : value->GetClass(); - return !AotClassLinker::CanReferenceInBootImageExtensionOrAppImage(klass, heap); + return !AotClassLinker::CanReferenceInBootImageExtensionOrAppImage(klass, heap_); } } @@ -746,7 +744,8 @@ void Transaction::ArrayLog::UndoArrayWrite(mirror::Array* array, Transaction* ScopedAssertNoNewTransactionRecords::InstallAssertion(const char* reason) { Transaction* transaction = nullptr; if (kIsDebugBuild && Runtime::Current()->IsActiveTransaction()) { - transaction = Runtime::Current()->GetTransaction(); + AotClassLinker* class_linker = down_cast<AotClassLinker*>(Runtime::Current()->GetClassLinker()); + transaction = class_linker->GetTransaction(); if (transaction != nullptr) { CHECK(transaction->assert_no_new_records_reason_ == nullptr) << "old: " << transaction->assert_no_new_records_reason_ << " new: " << reason; @@ -758,7 +757,8 @@ Transaction* ScopedAssertNoNewTransactionRecords::InstallAssertion(const char* r void ScopedAssertNoNewTransactionRecords::RemoveAssertion(Transaction* transaction) { if (kIsDebugBuild) { - CHECK(Runtime::Current()->GetTransaction() == transaction); + AotClassLinker* class_linker = down_cast<AotClassLinker*>(Runtime::Current()->GetClassLinker()); + CHECK(class_linker->GetTransaction() == transaction); CHECK(transaction->assert_no_new_records_reason_ != nullptr); transaction->assert_no_new_records_reason_ = nullptr; } diff --git a/runtime/transaction.h b/runtime/transaction.h index 9f3282f8d9..847120234d 100644 --- a/runtime/transaction.h +++ b/runtime/transaction.h @@ -46,8 +46,6 @@ template<class MirrorType> class ObjPtr; class Transaction final { public: - static constexpr const char* kAbortExceptionDescriptor = "Ldalvik/system/TransactionAbortError;"; - Transaction(bool strict, mirror::Class* root, ArenaStack* arena_stack, ArenaPool* arena_pool); ~Transaction(); diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc index c4eb7e808b..17a78ad6af 100644 --- a/runtime/transaction_test.cc +++ b/runtime/transaction_test.cc @@ -19,7 +19,8 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "class_linker-inl.h" -#include "common_runtime_test.h" +#include "common_transaction_test.h" +#include "common_throws.h" #include "dex/dex_file.h" #include "mirror/array-alloc-inl.h" #include "mirror/class-alloc-inl.h" @@ -27,7 +28,7 @@ namespace art HIDDEN { -class TransactionTest : public CommonRuntimeTest { +class TransactionTest : public CommonTransactionTest { protected: TransactionTest() { this->use_boot_image_ = true; // We need the boot image for this test. @@ -52,8 +53,7 @@ class TransactionTest : public CommonRuntimeTest { class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); ASSERT_TRUE(h_klass->IsInitialized()); - h_klass.Assign(class_linker_->FindSystemClass(soa.Self(), - Transaction::kAbortExceptionDescriptor)); + h_klass.Assign(class_linker_->FindSystemClass(soa.Self(), kTransactionAbortErrorDescriptor)); ASSERT_TRUE(h_klass != nullptr); class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); ASSERT_TRUE(h_klass->IsInitialized()); @@ -357,6 +357,58 @@ TEST_F(TransactionTest, InstanceFieldsTest) { EXPECT_FLOAT_EQ(floatField->GetFloat(h_instance.Get()), static_cast<float>(0.0f)); EXPECT_DOUBLE_EQ(doubleField->GetDouble(h_instance.Get()), static_cast<double>(0.0)); EXPECT_EQ(objectField->GetObject(h_instance.Get()), nullptr); + + // Fail to modify fields with strong CAS inside transaction, then rollback changes. + EnterTransactionMode(); + bool cas_success = h_instance->CasField32</*kTransactionActive=*/ true>( + intField->GetOffset(), + /*old_value=*/ 1, + /*new_value=*/ 2, + CASMode::kStrong, + std::memory_order_seq_cst); + EXPECT_FALSE(cas_success); + cas_success = h_instance->CasFieldStrongSequentiallyConsistent64</*kTransactionActive=*/ true>( + longField->GetOffset(), /*old_value=*/ INT64_C(1), /*new_value=*/ INT64_C(2)); + EXPECT_FALSE(cas_success); + cas_success = h_instance->CasFieldObject</*kTransactionActive=*/ true>( + objectField->GetOffset(), + /*old_value=*/ h_instance.Get(), + /*new_value=*/ nullptr, + CASMode::kStrong, + std::memory_order_seq_cst); + EXPECT_FALSE(cas_success); + RollbackAndExitTransactionMode(); + + // Check values have properly been restored to their original (default) value. + EXPECT_EQ(intField->GetInt(h_instance.Get()), 0); + EXPECT_EQ(longField->GetLong(h_instance.Get()), static_cast<int64_t>(0)); + EXPECT_EQ(objectField->GetObject(h_instance.Get()), nullptr); + + // Fail to modify fields with weak CAS inside transaction, then rollback changes. + EnterTransactionMode(); + cas_success = h_instance->CasField32</*kTransactionActive=*/ true>( + intField->GetOffset(), + /*old_value=*/ 3, + /*new_value=*/ 4, + CASMode::kWeak, + std::memory_order_seq_cst); + EXPECT_FALSE(cas_success); + cas_success = h_instance->CasFieldWeakSequentiallyConsistent64</*kTransactionActive=*/ true>( + longField->GetOffset(), /*old_value=*/ INT64_C(3), /*new_value=*/ INT64_C(4)); + EXPECT_FALSE(cas_success); + cas_success = h_instance->CasFieldObject</*kTransactionActive=*/ true>( + objectField->GetOffset(), + /*old_value=*/ h_klass.Get(), + /*new_value=*/ nullptr, + CASMode::kWeak, + std::memory_order_seq_cst); + EXPECT_FALSE(cas_success); + RollbackAndExitTransactionMode(); + + // Check values have properly been restored to their original (default) value. + EXPECT_EQ(intField->GetInt(h_instance.Get()), 0); + EXPECT_EQ(longField->GetLong(h_instance.Get()), static_cast<int64_t>(0)); + EXPECT_EQ(objectField->GetObject(h_instance.Get()), nullptr); } // Tests static array fields are reset to their default value after transaction rollback. diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index aca2b477fc..a9983fa9bc 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -670,7 +670,7 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier { const dex::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_); const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_)); - declaring_class_ = ®_types_.FromDescriptor(class_loader_.Get(), descriptor, false); + declaring_class_ = ®_types_.FromDescriptor(class_loader_, descriptor); } return *declaring_class_; } @@ -2534,8 +2534,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data for array of type " << array_type; } else { - const RegType& component_type = reg_types_.GetComponentType(array_type, - class_loader_.Get()); + const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_); DCHECK(!component_type.IsConflict()); if (component_type.IsNonZeroReferenceTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with component type " @@ -2878,7 +2877,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g dex::TypeIndex return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; const char* descriptor = dex_file_->GetTypeDescriptor(return_type_idx); - return_type = ®_types_.FromDescriptor(class_loader_.Get(), descriptor, false); + return_type = ®_types_.FromDescriptor(class_loader_, descriptor); } if (!return_type->IsLowHalf()) { work_line_->SetResultRegisterType(this, *return_type); @@ -2958,9 +2957,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g work_line_->MarkRefsAsInitialized(this, this_type); } if (return_type == nullptr) { - return_type = ®_types_.FromDescriptor(class_loader_.Get(), - return_type_descriptor, - false); + return_type = ®_types_.FromDescriptor(class_loader_, return_type_descriptor); } if (!return_type->IsLowHalf()) { work_line_->SetResultRegisterType(this, *return_type); @@ -2984,9 +2981,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g } else { descriptor = called_method->GetReturnTypeDescriptor(); } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_.Get(), - descriptor, - false); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(this, return_type); } else { @@ -3041,9 +3036,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g } else { descriptor = abs_method->GetReturnTypeDescriptor(); } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_.Get(), - descriptor, - false); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(this, return_type); } else { @@ -3076,7 +3069,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g const char* return_descriptor = dex_file_->GetReturnTypeDescriptor(dex_file_->GetProtoId(proto_idx)); const RegType& return_type = - reg_types_.FromDescriptor(class_loader_.Get(), return_descriptor, false); + reg_types_.FromDescriptor(class_loader_, return_descriptor); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(this, return_type); } else { @@ -3109,7 +3102,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g // Step 3. Propagate return type information const RegType& return_type = - reg_types_.FromDescriptor(class_loader_.Get(), return_descriptor, false); + reg_types_.FromDescriptor(class_loader_, return_descriptor); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(this, return_type); } else { @@ -3618,7 +3611,7 @@ const RegType& MethodVerifier<kVerifierDebug>::ResolveClass(dex::TypeIndex class } } else { const char* descriptor = dex_file_->GetTypeDescriptor(class_idx); - result = ®_types_.FromDescriptor(class_loader_.Get(), descriptor, false); + result = ®_types_.FromDescriptor(class_loader_, descriptor); } DCHECK(result != nullptr); if (result->IsConflict()) { @@ -3941,10 +3934,8 @@ ArtMethod* MethodVerifier<kVerifierDebug>::VerifyInvocationArgsFromIterator( } else { const uint32_t method_idx = GetMethodIdxOfInvoke(inst); const dex::TypeIndex class_idx = dex_file_->GetMethodId(method_idx).class_idx_; - res_method_class = ®_types_.FromDescriptor( - class_loader_.Get(), - dex_file_->GetTypeDescriptor(class_idx), - false); + res_method_class = + ®_types_.FromDescriptor(class_loader_, dex_file_->GetTypeDescriptor(class_idx)); } if (!res_method_class->IsAssignableFrom(adjusted_type, this)) { Fail(adjusted_type.IsUnresolvedTypes() @@ -3981,9 +3972,7 @@ ArtMethod* MethodVerifier<kVerifierDebug>::VerifyInvocationArgsFromIterator( return nullptr; } - const RegType& reg_type = reg_types_.FromDescriptor(class_loader_.Get(), - param_descriptor, - false); + const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, param_descriptor); uint32_t get_reg = is_range ? inst->VRegC() + static_cast<uint32_t>(sig_registers) : arg[sig_registers]; if (reg_type.IsIntegralTypes()) { @@ -4139,10 +4128,8 @@ ArtMethod* MethodVerifier<kVerifierDebug>::VerifyInvocationArgs( // has a vtable entry for the target method. Or the target is on a interface. if (method_type == METHOD_SUPER) { dex::TypeIndex class_idx = dex_file_->GetMethodId(method_idx).class_idx_; - const RegType& reference_type = reg_types_.FromDescriptor( - class_loader_.Get(), - dex_file_->GetTypeDescriptor(class_idx), - false); + const RegType& reference_type = + reg_types_.FromDescriptor(class_loader_, dex_file_->GetTypeDescriptor(class_idx)); if (reference_type.IsUnresolvedTypes()) { // We cannot differentiate on whether this is a class change error or just // a missing method. This will be handled at runtime. @@ -4315,7 +4302,7 @@ void MethodVerifier<kVerifierDebug>::VerifyNewArray(const Instruction* inst, DCHECK(!res_type.IsUnresolvedMergedReference()); // Verify each register. If "arg_count" is bad, VerifyRegisterType() will run off the end of // the list and fail. It's legal, if silly, for arg_count to be zero. - const RegType& expected_type = reg_types_.GetComponentType(res_type, class_loader_.Get()); + const RegType& expected_type = reg_types_.GetComponentType(res_type, class_loader_); uint32_t arg_count = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c(); uint32_t arg[5]; if (!is_range) { @@ -4376,7 +4363,7 @@ void MethodVerifier<kVerifierDebug>::VerifyAGet(const Instruction* inst, } } else { /* verify the class */ - const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_.Get()); + const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_); if (!component_type.IsReferenceTypes() && !is_primitive) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type << " source for aget-object"; @@ -4498,7 +4485,7 @@ void MethodVerifier<kVerifierDebug>::VerifyAPut(const Instruction* inst, << " because of missing class"; } } else { - const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_.Get()); + const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_); const uint32_t vregA = inst->VRegA_23x(); if (is_primitive) { VerifyPrimitivePut(component_type, insn_type, vregA); @@ -4738,9 +4725,8 @@ void MethodVerifier<kVerifierDebug>::VerifyISFieldAccess(const Instruction* inst if (kAccType == FieldAccessType::kAccPut) { const dex::FieldId& field_id = dex_file_->GetFieldId(field_idx); const char* field_class_descriptor = dex_file_->GetFieldDeclaringClassDescriptor(field_id); - const RegType* field_class_type = ®_types_.FromDescriptor(class_loader_.Get(), - field_class_descriptor, - false); + const RegType* field_class_type = + ®_types_.FromDescriptor(class_loader_, field_class_descriptor); if (!field_class_type->Equals(GetDeclaringClass())) { Fail(VERIFY_ERROR_ACCESS_FIELD) << "could not check field put for final field modify of " << field_class_descriptor @@ -4754,7 +4740,7 @@ void MethodVerifier<kVerifierDebug>::VerifyISFieldAccess(const Instruction* inst if (field_type == nullptr) { const dex::FieldId& field_id = dex_file_->GetFieldId(field_idx); const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id); - field_type = ®_types_.FromDescriptor(class_loader_.Get(), descriptor, false); + field_type = ®_types_.FromDescriptor(class_loader_, descriptor); } DCHECK(field_type != nullptr); const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c(); @@ -4874,7 +4860,7 @@ const RegType& MethodVerifier<kVerifierDebug>::GetMethodReturnType() { const dex::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id); dex::TypeIndex return_type_idx = proto_id.return_type_idx_; const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx)); - return_type_ = ®_types_.FromDescriptor(class_loader_.Get(), descriptor, false); + return_type_ = ®_types_.FromDescriptor(class_loader_, descriptor); } return *return_type_; } diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h index 8db2fe669a..c5311e6e62 100644 --- a/runtime/verifier/reg_type-inl.h +++ b/runtime/verifier/reg_type-inl.h @@ -127,6 +127,10 @@ inline bool RegType::AssignableFrom(const RegType& lhs, // For unresolved types, we don't know if they are assignable, and the // verifier will continue assuming they are. We need to record that. if (verifier != nullptr) { + // Note that if `rhs` is an interface type, `lhs` may be j.l.Object + // and if the assignability check is not strict, then this should be + // OK. However we don't encode strictness in the verifier deps, and + // such a situation will force a full verification. VerifierDeps::MaybeRecordAssignability(verifier->GetVerifierDeps(), verifier->GetDexFile(), verifier->GetClassDef(), diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 14022d297f..b7aa1715e1 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -84,9 +84,8 @@ void RegTypeCache::FillPrimitiveAndSmallConstantTypes() { new (&allocator_) NullType(null_handle_, "", kNullCacheId); } -const RegType& RegTypeCache::FromDescriptor(ObjPtr<mirror::ClassLoader> loader, - const char* descriptor, - bool precise) { +const RegType& RegTypeCache::FromDescriptor(Handle<mirror::ClassLoader> loader, + const char* descriptor) { if (descriptor[1] == '\0') { switch (descriptor[0]) { case 'Z': @@ -110,7 +109,7 @@ const RegType& RegTypeCache::FromDescriptor(ObjPtr<mirror::ClassLoader> loader, return Conflict(); } } else if (descriptor[0] == 'L' || descriptor[0] == '[') { - return From(loader, descriptor, precise); + return From(loader, descriptor); } else { return Conflict(); } @@ -156,17 +155,15 @@ bool RegTypeCache::MatchDescriptor(size_t idx, const std::string_view& descripto } ObjPtr<mirror::Class> RegTypeCache::ResolveClass(const char* descriptor, - ObjPtr<mirror::ClassLoader> loader) { + Handle<mirror::ClassLoader> loader) { // Class was not found, must create new type. // Try resolving class Thread* self = Thread::Current(); - StackHandleScope<1> hs(self); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(loader)); ObjPtr<mirror::Class> klass = nullptr; if (can_load_classes_) { - klass = class_linker_->FindClass(self, descriptor, class_loader); + klass = class_linker_->FindClass(self, descriptor, loader); } else { - klass = class_linker_->LookupClass(self, descriptor, loader); + klass = class_linker_->LookupClass(self, descriptor, loader.Get()); if (klass != nullptr && !klass->IsResolved()) { // We found the class but without it being loaded its not safe for use. klass = nullptr; @@ -181,14 +178,12 @@ std::string_view RegTypeCache::AddString(const std::string_view& str) { return std::string_view(ptr, str.length()); } -const RegType& RegTypeCache::From(ObjPtr<mirror::ClassLoader> loader, - const char* descriptor, - bool precise) { +const RegType& RegTypeCache::From(Handle<mirror::ClassLoader> loader, const char* descriptor) { std::string_view sv_descriptor(descriptor); // Try looking up the class in the cache first. We use a std::string_view to avoid // repeated strlen operations on the descriptor. for (size_t i = kNumPrimitivesAndSmallConstants; i < entries_.size(); i++) { - if (MatchDescriptor(i, sv_descriptor, precise)) { + if (MatchDescriptor(i, sv_descriptor, /* precise= */ false)) { return *(entries_[i]); } } @@ -196,17 +191,14 @@ const RegType& RegTypeCache::From(ObjPtr<mirror::ClassLoader> loader, // Try resolving class. ObjPtr<mirror::Class> klass = ResolveClass(descriptor, loader); if (klass != nullptr) { - // Class resolved, first look for the class in the list of entries - // Class was not found, must create new type. - // To pass the verification, the type should be imprecise, - // instantiable or an interface with the precise type set to false. - DCHECK_IMPLIES(precise, klass->IsInstantiable()); - // Create a precise type if: - // 1- Class is final and NOT an interface. a precise interface is meaningless !! - // 2- Precise Flag passed as true. + // Create a precise type if the class cannot be assigned from other types + // (final classes, arrays of final classes and primitive arrays, see + // `Class::CannotBeAssignedFromOtherTypes()`; primitive types should not + // reach this code). + DCHECK(!klass->IsPrimitive()); RegType* entry; // Create an imprecise type if we can't tell for a fact that it is precise. - if (klass->CannotBeAssignedFromOtherTypes() || precise) { + if (klass->CannotBeAssignedFromOtherTypes()) { DCHECK_IMPLIES(klass->IsAbstract(), klass->IsArrayClass()); DCHECK(!klass->IsInterface()); entry = new (&allocator_) PreciseReferenceType(handles_.NewHandle(klass), @@ -587,13 +579,13 @@ const ConstantType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) { } const RegType& RegTypeCache::GetComponentType(const RegType& array, - ObjPtr<mirror::ClassLoader> loader) { + Handle<mirror::ClassLoader> loader) { if (!array.IsArrayTypes()) { return Conflict(); } else if (array.IsUnresolvedTypes()) { DCHECK(!array.IsUnresolvedMergedReference()); // Caller must make sure not to ask for this. const std::string descriptor(array.GetDescriptor()); - return FromDescriptor(loader, descriptor.c_str() + 1, false); + return FromDescriptor(loader, descriptor.c_str() + 1); } else { ObjPtr<mirror::Class> klass = array.GetClass()->GetComponentType(); std::string temp; @@ -602,7 +594,7 @@ const RegType& RegTypeCache::GetComponentType(const RegType& array, // Arrays may have erroneous component types, use unresolved in that case. // We assume that the primitive classes are not erroneous, so we know it is a // reference type. - return FromDescriptor(loader, descriptor, false); + return FromDescriptor(loader, descriptor); } else { return FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()); } diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h index ae8b904ecd..897cfb7809 100644 --- a/runtime/verifier/reg_type_cache.h +++ b/runtime/verifier/reg_type_cache.h @@ -72,8 +72,6 @@ class RegTypeCache { VariableSizedHandleScope& handles, bool can_suspend = true); const art::verifier::RegType& GetFromId(uint16_t id) const; - const RegType& From(ObjPtr<mirror::ClassLoader> loader, const char* descriptor, bool precise) - REQUIRES_SHARED(Locks::mutator_lock_); // Find a RegType, returns null if not found. const RegType* FindClass(ObjPtr<mirror::Class> klass, bool precise) const REQUIRES_SHARED(Locks::mutator_lock_); @@ -91,9 +89,7 @@ class RegTypeCache { REQUIRES_SHARED(Locks::mutator_lock_); const ConstantType& FromCat2ConstHi(int32_t value, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); - const RegType& FromDescriptor(ObjPtr<mirror::ClassLoader> loader, - const char* descriptor, - bool precise) + const RegType& FromDescriptor(Handle<mirror::ClassLoader> loader, const char* descriptor) REQUIRES_SHARED(Locks::mutator_lock_); const RegType& FromUnresolvedMerge(const RegType& left, const RegType& right, @@ -148,7 +144,7 @@ class RegTypeCache { const ImpreciseConstType& IntConstant() REQUIRES_SHARED(Locks::mutator_lock_); const ImpreciseConstType& PosByteConstant() REQUIRES_SHARED(Locks::mutator_lock_); const ImpreciseConstType& PosShortConstant() REQUIRES_SHARED(Locks::mutator_lock_); - const RegType& GetComponentType(const RegType& array, ObjPtr<mirror::ClassLoader> loader) + const RegType& GetComponentType(const RegType& array, Handle<mirror::ClassLoader> loader) REQUIRES_SHARED(Locks::mutator_lock_); void Dump(std::ostream& os) REQUIRES_SHARED(Locks::mutator_lock_); const RegType& RegTypeFromPrimitiveType(Primitive::Type) const; @@ -181,13 +177,16 @@ class RegTypeCache { private: void FillPrimitiveAndSmallConstantTypes() REQUIRES_SHARED(Locks::mutator_lock_); - ObjPtr<mirror::Class> ResolveClass(const char* descriptor, ObjPtr<mirror::ClassLoader> loader) + ObjPtr<mirror::Class> ResolveClass(const char* descriptor, Handle<mirror::ClassLoader> loader) REQUIRES_SHARED(Locks::mutator_lock_); bool MatchDescriptor(size_t idx, const std::string_view& descriptor, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); const ConstantType& FromCat1NonSmallConstant(int32_t value, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); + const RegType& From(Handle<mirror::ClassLoader> loader, const char* descriptor) + REQUIRES_SHARED(Locks::mutator_lock_); + // Returns the pass in RegType. template <class RegTypeType> RegTypeType& AddEntry(RegTypeType* new_entry) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc index ec4c23ee59..052eae76e2 100644 --- a/runtime/verifier/reg_type_test.cc +++ b/runtime/verifier/reg_type_test.cc @@ -36,6 +36,19 @@ class RegTypeTest : public CommonRuntimeTest { RegTypeTest() { use_boot_image_ = true; // Make the Runtime creation cheaper. } + + static const RegType& PreciseJavaLangObjectFromDescriptor(RegTypeCache* cache, + Handle<mirror::ClassLoader> loader) + REQUIRES_SHARED(Locks::mutator_lock_) { + // To create a precise `java.lang.Object` reference from a descriptor, go through + // `Uninitialized()` and `FromUninitialized()` as we would for `new Object()`. + const RegType& imprecise_obj = cache->FromDescriptor(loader, "Ljava/lang/Object;"); + CHECK(!imprecise_obj.IsPreciseReference()); + const RegType& precise_obj = + cache->FromUninitialized(cache->Uninitialized(imprecise_obj, /* allocation_pc= */ 0u)); + CHECK(precise_obj.IsPreciseReference()); + return precise_obj; + } }; TEST_F(RegTypeTest, ConstLoHi) { @@ -368,18 +381,19 @@ TEST_F(RegTypeTest, Primitives) { class RegTypeReferenceTest : public RegTypeTest {}; -TEST_F(RegTypeReferenceTest, JavalangObjectImprecise) { +TEST_F(RegTypeReferenceTest, JavaLangObjectImprecise) { // Tests matching precisions. A reference type that was created precise doesn't // match the one that is imprecise. ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); VariableSizedHandleScope handles(soa.Self()); + ScopedNullHandle<mirror::ClassLoader> loader; RegTypeCache cache( Runtime::Current()->GetClassLinker(), /* can_load_classes= */ true, allocator, handles); const RegType& imprecise_obj = cache.JavaLangObject(false); const RegType& precise_obj = cache.JavaLangObject(true); - const RegType& precise_obj_2 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true); + const RegType& precise_obj_2 = PreciseJavaLangObjectFromDescriptor(&cache, loader); EXPECT_TRUE(precise_obj.Equals(precise_obj_2)); EXPECT_FALSE(imprecise_obj.Equals(precise_obj)); @@ -394,13 +408,14 @@ TEST_F(RegTypeReferenceTest, UnresolvedType) { ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); VariableSizedHandleScope handles(soa.Self()); + ScopedNullHandle<mirror::ClassLoader> loader; RegTypeCache cache( Runtime::Current()->GetClassLinker(), /* can_load_classes= */ true, allocator, handles); - const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); + const RegType& ref_type_0 = cache.FromDescriptor(loader, "Ljava/lang/DoesNotExist;"); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); EXPECT_TRUE(ref_type_0.IsNonZeroReferenceTypes()); - const RegType& ref_type_1 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); + const RegType& ref_type_1 = cache.FromDescriptor(loader, "Ljava/lang/DoesNotExist;"); EXPECT_TRUE(ref_type_0.Equals(ref_type_1)); const RegType& unresolved_super_class = cache.FromUnresolvedSuperClass(ref_type_0); @@ -414,11 +429,12 @@ TEST_F(RegTypeReferenceTest, UnresolvedUnintializedType) { ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); VariableSizedHandleScope handles(soa.Self()); + ScopedNullHandle<mirror::ClassLoader> loader; RegTypeCache cache( Runtime::Current()->GetClassLinker(), /* can_load_classes= */ true, allocator, handles); - const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); + const RegType& ref_type_0 = cache.FromDescriptor(loader, "Ljava/lang/DoesNotExist;"); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); - const RegType& ref_type = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); + const RegType& ref_type = cache.FromDescriptor(loader, "Ljava/lang/DoesNotExist;"); EXPECT_TRUE(ref_type_0.Equals(ref_type)); // Create an uninitialized type of this unresolved type const RegType& unresolved_unintialised = cache.Uninitialized(ref_type, 1101ull); @@ -440,10 +456,12 @@ TEST_F(RegTypeReferenceTest, Dump) { ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); VariableSizedHandleScope handles(soa.Self()); + ScopedNullHandle<mirror::ClassLoader> loader; RegTypeCache cache( Runtime::Current()->GetClassLinker(), /* can_load_classes= */ true, allocator, handles); - const RegType& unresolved_ref = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); - const RegType& unresolved_ref_another = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExistEither;", true); + const RegType& unresolved_ref = cache.FromDescriptor(loader, "Ljava/lang/DoesNotExist;"); + const RegType& unresolved_ref_another = + cache.FromDescriptor(loader, "Ljava/lang/DoesNotExistEither;"); const RegType& resolved_ref = cache.JavaLangString(); const RegType& resolved_unintialiesd = cache.Uninitialized(resolved_ref, 10); const RegType& unresolved_unintialized = cache.Uninitialized(unresolved_ref, 12); @@ -470,11 +488,12 @@ TEST_F(RegTypeReferenceTest, JavalangString) { ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); VariableSizedHandleScope handles(soa.Self()); + ScopedNullHandle<mirror::ClassLoader> loader; RegTypeCache cache( Runtime::Current()->GetClassLinker(), /* can_load_classes= */ true, allocator, handles); const RegType& ref_type = cache.JavaLangString(); const RegType& ref_type_2 = cache.JavaLangString(); - const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/String;", true); + const RegType& ref_type_3 = cache.FromDescriptor(loader, "Ljava/lang/String;"); EXPECT_TRUE(ref_type.Equals(ref_type_2)); EXPECT_TRUE(ref_type_2.Equals(ref_type_3)); @@ -494,11 +513,12 @@ TEST_F(RegTypeReferenceTest, JavalangObject) { ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); VariableSizedHandleScope handles(soa.Self()); + ScopedNullHandle<mirror::ClassLoader> loader; RegTypeCache cache( Runtime::Current()->GetClassLinker(), /* can_load_classes= */ true, allocator, handles); const RegType& ref_type = cache.JavaLangObject(true); const RegType& ref_type_2 = cache.JavaLangObject(true); - const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true); + const RegType& ref_type_3 = PreciseJavaLangObjectFromDescriptor(&cache, loader); EXPECT_TRUE(ref_type.Equals(ref_type_2)); EXPECT_TRUE(ref_type_3.Equals(ref_type_2)); @@ -511,15 +531,16 @@ TEST_F(RegTypeReferenceTest, Merging) { ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); VariableSizedHandleScope handles(soa.Self()); + ScopedNullHandle<mirror::ClassLoader> loader; RegTypeCache cache_new( Runtime::Current()->GetClassLinker(), /* can_load_classes= */ true, allocator, handles); const RegType& string = cache_new.JavaLangString(); const RegType& Object = cache_new.JavaLangObject(true); EXPECT_TRUE(string.Merge(Object, &cache_new, /* verifier= */ nullptr).IsJavaLangObject()); // Merge two unresolved types. - const RegType& ref_type_0 = cache_new.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); + const RegType& ref_type_0 = cache_new.FromDescriptor(loader, "Ljava/lang/DoesNotExist;"); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); - const RegType& ref_type_1 = cache_new.FromDescriptor(nullptr, "Ljava/lang/DoesNotExistToo;", true); + const RegType& ref_type_1 = cache_new.FromDescriptor(loader, "Ljava/lang/DoesNotExistToo;"); EXPECT_FALSE(ref_type_0.Equals(ref_type_1)); const RegType& merged = ref_type_1.Merge(ref_type_0, &cache_new, /* verifier= */ nullptr); @@ -751,6 +772,7 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { ScopedDisableMovingGC no_gc(soa.Self()); VariableSizedHandleScope handles(soa.Self()); + ScopedNullHandle<mirror::ClassLoader> loader; RegTypeCache cache( Runtime::Current()->GetClassLinker(), /* can_load_classes= */ true, allocator, handles); @@ -760,12 +782,12 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { const RegType& int_type = cache.Integer(); const RegType& obj = cache.JavaLangObject(false); - const RegType& obj_arr = cache.From(nullptr, "[Ljava/lang/Object;", false); + const RegType& obj_arr = cache.FromDescriptor(loader, "[Ljava/lang/Object;"); ASSERT_FALSE(obj_arr.IsUnresolvedReference()); - const RegType& unresolved_a = cache.From(nullptr, "Ldoes/not/resolve/A;", false); + const RegType& unresolved_a = cache.FromDescriptor(loader, "Ldoes/not/resolve/A;"); ASSERT_TRUE(unresolved_a.IsUnresolvedReference()); - const RegType& unresolved_b = cache.From(nullptr, "Ldoes/not/resolve/B;", false); + const RegType& unresolved_b = cache.FromDescriptor(loader, "Ldoes/not/resolve/B;"); ASSERT_TRUE(unresolved_b.IsUnresolvedReference()); const RegType& unresolved_ab = cache.FromUnresolvedMerge(unresolved_a, unresolved_b, nullptr); ASSERT_TRUE(unresolved_ab.IsUnresolvedMergedReference()); @@ -778,25 +800,25 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { const RegType& uninit_unres_a_0 = cache.Uninitialized(unresolved_a, 0); const RegType& uninit_unres_b_0 = cache.Uninitialized(unresolved_b, 0); - const RegType& number = cache.From(nullptr, "Ljava/lang/Number;", false); + const RegType& number = cache.FromDescriptor(loader, "Ljava/lang/Number;"); ASSERT_FALSE(number.IsUnresolvedReference()); - const RegType& integer = cache.From(nullptr, "Ljava/lang/Integer;", false); + const RegType& integer = cache.FromDescriptor(loader, "Ljava/lang/Integer;"); ASSERT_FALSE(integer.IsUnresolvedReference()); const RegType& uninit_number_0 = cache.Uninitialized(number, 0u); const RegType& uninit_integer_0 = cache.Uninitialized(integer, 0u); - const RegType& number_arr = cache.From(nullptr, "[Ljava/lang/Number;", false); + const RegType& number_arr = cache.FromDescriptor(loader, "[Ljava/lang/Number;"); ASSERT_FALSE(number_arr.IsUnresolvedReference()); - const RegType& integer_arr = cache.From(nullptr, "[Ljava/lang/Integer;", false); + const RegType& integer_arr = cache.FromDescriptor(loader, "[Ljava/lang/Integer;"); ASSERT_FALSE(integer_arr.IsUnresolvedReference()); - const RegType& number_arr_arr = cache.From(nullptr, "[[Ljava/lang/Number;", false); + const RegType& number_arr_arr = cache.FromDescriptor(loader, "[[Ljava/lang/Number;"); ASSERT_FALSE(number_arr_arr.IsUnresolvedReference()); - const RegType& char_arr = cache.From(nullptr, "[C", false); + const RegType& char_arr = cache.FromDescriptor(loader, "[C"); ASSERT_FALSE(char_arr.IsUnresolvedReference()); - const RegType& byte_arr = cache.From(nullptr, "[B", false); + const RegType& byte_arr = cache.FromDescriptor(loader, "[B"); ASSERT_FALSE(byte_arr.IsUnresolvedReference()); const RegType& unresolved_a_num = cache.FromUnresolvedMerge(unresolved_a, number, nullptr); @@ -1118,11 +1140,12 @@ TEST_F(RegTypeOOMTest, ClassJoinOOM) { constexpr const char* kNumberArrayFive = "[[[[[Ljava/lang/Number;"; VariableSizedHandleScope handles(soa.Self()); + ScopedNullHandle<mirror::ClassLoader> loader; RegTypeCache cache( Runtime::Current()->GetClassLinker(), /* can_load_classes= */ true, allocator, handles); - const RegType& int_array_array = cache.From(nullptr, kIntArrayFive, false); + const RegType& int_array_array = cache.FromDescriptor(loader, kIntArrayFive); ASSERT_TRUE(int_array_array.HasClass()); - const RegType& float_array_array = cache.From(nullptr, kFloatArrayFive, false); + const RegType& float_array_array = cache.FromDescriptor(loader, kFloatArrayFive); ASSERT_TRUE(float_array_array.HasClass()); // Check assumptions: the joined classes don't exist, yet. diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index e09e007d93..f5df8ddf72 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -673,17 +673,18 @@ void VerifierDeps::Dump(VariableIndentationOutputStream* vios) const { } } -bool VerifierDeps::ValidateDependencies(Thread* self, - Handle<mirror::ClassLoader> class_loader, - const std::vector<const DexFile*>& dex_files, - /* out */ std::string* error_msg) const { +bool VerifierDeps::ValidateDependenciesAndUpdateStatus( + Thread* self, + Handle<mirror::ClassLoader> class_loader, + const std::vector<const DexFile*>& dex_files) { + bool all_validated = true; for (const auto* dex_file : dex_files) { - const DexFileDeps* my_deps = GetDexFileDeps(*dex_file); - if (!VerifyDexFile(class_loader, *dex_file, *my_deps, self, error_msg)) { - return false; + DexFileDeps* my_deps = GetDexFileDeps(*dex_file); + if (!VerifyDexFileAndUpdateStatus(class_loader, *dex_file, *my_deps, self)) { + all_validated = false; } } - return true; + return all_validated; } // TODO: share that helper with other parts of the compiler that have @@ -701,16 +702,21 @@ static ObjPtr<mirror::Class> FindClassAndClearException(ClassLinker* class_linke return result; } -bool VerifierDeps::VerifyAssignability(Handle<mirror::ClassLoader> class_loader, - const DexFile& dex_file, - const std::vector<std::set<TypeAssignability>>& assignables, - Thread* self, - /* out */ std::string* error_msg) const { +bool VerifierDeps::VerifyDexFileAndUpdateStatus( + Handle<mirror::ClassLoader> class_loader, + const DexFile& dex_file, + DexFileDeps& deps, + Thread* self) { StackHandleScope<2> hs(self); + const std::vector<std::set<TypeAssignability>>& assignables = deps.assignable_types_; ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); MutableHandle<mirror::Class> source(hs.NewHandle<mirror::Class>(nullptr)); MutableHandle<mirror::Class> destination(hs.NewHandle<mirror::Class>(nullptr)); + uint32_t class_def_index = 0u; + bool all_validated = true; + uint32_t number_of_warnings = 0; + static constexpr uint32_t kMaxWarnings = 5; for (const auto& vec : assignables) { for (const auto& entry : vec) { const std::string& destination_desc = GetStringFromId(dex_file, entry.GetDestination()); @@ -728,31 +734,20 @@ bool VerifierDeps::VerifyAssignability(Handle<mirror::ClassLoader> class_loader, DCHECK(destination->IsResolved() && source->IsResolved()); if (!destination->IsAssignableFrom(source.Get())) { - *error_msg = ART_FORMAT("Class {} not assignable from {}", destination_desc, source_desc); - return false; + deps.verified_classes_[class_def_index] = false; + all_validated = false; + if (number_of_warnings++ < kMaxWarnings) { + LOG(WARNING) << "Class " + << dex_file.PrettyType(dex_file.GetClassDef(class_def_index).class_idx_) + << " could not be fast verified because one of its methods wrongly expected " + << destination_desc << " to be assignable from " << source_desc; + } + break; } } + class_def_index++; } - return true; -} - -void VerifierDeps::ClearData(const std::vector<const DexFile*>& dex_files) { - for (const DexFile* dex_file : dex_files) { - auto it = dex_deps_.find(dex_file); - if (it == dex_deps_.end()) { - continue; - } - std::unique_ptr<DexFileDeps> deps(new DexFileDeps(dex_file->NumClassDefs())); - it->second.swap(deps); - } -} - -bool VerifierDeps::VerifyDexFile(Handle<mirror::ClassLoader> class_loader, - const DexFile& dex_file, - const DexFileDeps& deps, - Thread* self, - /* out */ std::string* error_msg) const { - return VerifyAssignability(class_loader, dex_file, deps.assignable_types_, self, error_msg); + return all_validated; } } // namespace verifier diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h index dfa13ea112..5e3fb63c2d 100644 --- a/runtime/verifier/verifier_deps.h +++ b/runtime/verifier/verifier_deps.h @@ -119,11 +119,12 @@ class VerifierDeps { EXPORT void Dump(VariableIndentationOutputStream* vios) const; - // Verify the encoded dependencies of this `VerifierDeps` are still valid. - EXPORT bool ValidateDependencies(Thread* self, - Handle<mirror::ClassLoader> class_loader, - const std::vector<const DexFile*>& dex_files, - /* out */ std::string* error_msg) const + // Verifies the encoded dependencies of this `VerifierDeps` are still valid. + // Returns whether all dependencies were validated. + EXPORT bool ValidateDependenciesAndUpdateStatus( + Thread* self, + Handle<mirror::ClassLoader> class_loader, + const std::vector<const DexFile*>& dex_files) REQUIRES_SHARED(Locks::mutator_lock_); const std::vector<bool>& GetVerifiedClasses(const DexFile& dex_file) const { @@ -142,9 +143,6 @@ class VerifierDeps { return GetDexFileDeps(dex_file) != nullptr; } - // Resets the data related to the given dex files. - EXPORT void ClearData(const std::vector<const DexFile*>& dex_files); - // Parses raw VerifierDeps data to extract bitvectors of which class def indices // were verified or not. The given `dex_files` must match the order and count of // dex files used to create the VerifierDeps. @@ -234,11 +232,12 @@ class VerifierDeps { // Verify `dex_file` according to the `deps`, that is going over each // `DexFileDeps` field, and checking that the recorded information still // holds. - bool VerifyDexFile(Handle<mirror::ClassLoader> class_loader, - const DexFile& dex_file, - const DexFileDeps& deps, - Thread* self, - /* out */ std::string* error_msg) const + // Returns whether all dependencies were validated. + bool VerifyDexFileAndUpdateStatus( + Handle<mirror::ClassLoader> class_loader, + const DexFile& dex_file, + DexFileDeps& deps, + Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); // Iterates over `dex_files` and tries to find a class def matching `descriptor`. @@ -248,13 +247,6 @@ class VerifierDeps { const std::vector<const DexFile*>& dex_files, /* out */ const DexFile** cp_dex_file) const; - bool VerifyAssignability(Handle<mirror::ClassLoader> class_loader, - const DexFile& dex_file, - const std::vector<std::set<TypeAssignability>>& assignables, - Thread* self, - /* out */ std::string* error_msg) const - REQUIRES_SHARED(Locks::mutator_lock_); - // Map from DexFiles into dependencies collected from verification of their methods. std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_; diff --git a/test/2256-checker-vector-replacement/src/Main.java b/test/2256-checker-vector-replacement/src/Main.java index fca80fbeda..2f94094577 100644 --- a/test/2256-checker-vector-replacement/src/Main.java +++ b/test/2256-checker-vector-replacement/src/Main.java @@ -20,30 +20,34 @@ public class Main { } // Before loop optimization we only had an array get. After it, we optimized to also have - // VecLoad operations. This happens consistently only for Arm64. Arm32 vectorizes consistently - // but it also removes the ArrayGet. X86/X86_64 doesn't vectorize consistently (other - // vectorization tests also ignore x86/x86_64). + // VecLoad operations. This happens consistently only for Arm64 when using traditional + // vectorization (NEON). Arm32 vectorizes consistently but it also removes the ArrayGet, as does + // Arm64 predicated vectorization (SVE) because the scalar tail loop is eliminated. X86/X86_64 + // doesn't vectorize consistently (other vectorization tests also ignore x86/x86_64). + // TODO: Create equivalent ArrayGet-replacement regression test for SVE, when SVE supports LSE. /// CHECK-START-ARM64: void Main.$noinline$testVectorAndNonVector() loop_optimization (before) - /// CHECK: ArrayGet + /// CHECK-IF: not (hasIsaFeature("sve") and os.environ.get('ART_FORCE_TRY_PREDICATED_SIMD') == 'true') + /// CHECK-DAG: ArrayGet + /// CHECK-NOT: VecLoad + /// CHECK-FI: /// CHECK-START-ARM64: void Main.$noinline$testVectorAndNonVector() loop_optimization (after) - /// CHECK: ArrayGet - - /// CHECK-START-ARM64: void Main.$noinline$testVectorAndNonVector() loop_optimization (before) - /// CHECK-NOT: VecLoad - - /// CHECK-START-ARM64: void Main.$noinline$testVectorAndNonVector() loop_optimization (after) - /// CHECK: VecLoad + /// CHECK-IF: not (hasIsaFeature("sve") and os.environ.get('ART_FORCE_TRY_PREDICATED_SIMD') == 'true') + /// CHECK-DAG: ArrayGet + /// CHECK-DAG: VecLoad + /// CHECK-FI: // In LoadStoreElimination both ArrayGet and VecLoad have the same heap location. We will try to // replace the ArrayGet with the constant 0. The crash happens when we want to do the same with // the vector operation, changing the vector operation to a scalar. /// CHECK-START-ARM64: void Main.$noinline$testVectorAndNonVector() load_store_elimination (before) - /// CHECK-DAG: VecLoad outer_loop:<<VecBlock:B\d+>> - /// CHECK-DAG: ArrayGet outer_loop:<<ScalarBlock:B\d+>> - /// CHECK-EVAL: "<<VecBlock>>" == "<<ScalarBlock>>" + /// CHECK-IF: not (hasIsaFeature("sve") and os.environ.get('ART_FORCE_TRY_PREDICATED_SIMD') == 'true') + /// CHECK-DAG: VecLoad outer_loop:<<VecBlock:B\d+>> + /// CHECK-DAG: ArrayGet outer_loop:<<ScalarBlock:B\d+>> + /// CHECK-EVAL: "<<VecBlock>>" == "<<ScalarBlock>>" + /// CHECK-FI: private static void $noinline$testVectorAndNonVector() { int[] result = new int[2]; diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java index c809278d8e..6519e5c2ad 100644 --- a/test/536-checker-intrinsic-optimization/src/Main.java +++ b/test/536-checker-intrinsic-optimization/src/Main.java @@ -19,6 +19,10 @@ public class Main { public static String mediumString = generateString(300); public static String largeString = generateString(2000); + public static String smallNonAsciiString = generateNonAsciiString(100); + public static String mediumNonAsciiString = generateNonAsciiString(300); + public static String largeNonAsciiString = generateNonAsciiString(2000); + public static String generateString(int length) { // Generate a string in the ASCII range that will // use string compression. @@ -30,6 +34,14 @@ public class Main { return sb.toString(); } + public static String generateNonAsciiString(int length) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) { + sb.append(Character.valueOf('\uFFFF')); + } + return sb.toString(); + } + public static void assertIntEquals(int expected, int result) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); @@ -102,6 +114,38 @@ public class Main { // Substring > 8 characters. assertStringEquals(smallString.substring(7, 28), stringGetCharsRange(smallString, 7, 28, 17)); + // Single character. + assertStringEquals("\uFFFF", stringGetCharsAndBack("\uFFFF")); + + // Strings < 8 characters. + assertStringEquals("\uFFFF\uFFFF\uFFFF\uFFFF\uFFFF", + stringGetCharsAndBack("\uFFFF\uFFFF\uFFFF\uFFFF\uFFFF")); + + // Strings > 8 characters of various lengths. + assertStringEquals(smallNonAsciiString, stringGetCharsAndBack(smallNonAsciiString)); + assertStringEquals(mediumNonAsciiString, stringGetCharsAndBack(mediumNonAsciiString)); + assertStringEquals(largeNonAsciiString, stringGetCharsAndBack(largeNonAsciiString)); + + // Get only a substring: + // Substring < 8 characters. + assertStringEquals(smallNonAsciiString.substring(5, 10), + stringGetCharsRange(smallNonAsciiString, 5, 10, 0)); + // Substring > 8 characters. + assertStringEquals(smallNonAsciiString.substring(7, 28), + stringGetCharsRange(smallNonAsciiString, 7, 28, 0)); + + // Get full string with offset in the char array. + assertStringEquals(smallNonAsciiString, + stringGetCharsAndBackOffset(smallNonAsciiString, 17)); + + // Get a substring with an offset in the char array. + // Substring < 8 characters. + assertStringEquals(smallNonAsciiString.substring(5, 10), + stringGetCharsRange(smallNonAsciiString, 5, 10, 17)); + // Substring > 8 characters. + assertStringEquals(smallNonAsciiString.substring(7, 28), + stringGetCharsRange(smallNonAsciiString, 7, 28, 17)); + try { $opt$noinline$stringCharAt("abc", -1); throw new Error("Should throw SIOOB."); diff --git a/test/663-checker-select-generator/src/Main.java b/test/663-checker-select-generator/src/Main.java index 0ab3aefc17..b89c4b73a7 100644 --- a/test/663-checker-select-generator/src/Main.java +++ b/test/663-checker-select-generator/src/Main.java @@ -194,6 +194,7 @@ public class Main { } // Check that we don't generate a select since we only have a single return. + // TODO: This is no longer true, since D8 does not share the branches with different lines. /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValueWithReturn(boolean) builder (after) /// CHECK: <<Const10:i\d+>> IntConstant 10 @@ -201,7 +202,7 @@ public class Main { /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValueWithReturn(boolean) builder (after) /// CHECK: Return - /// CHECK-NOT: Return + /// CHECK: Return private static int $noinline$testSimpleDiamondSameValueWithReturn(boolean bool_param) { if (bool_param) { @@ -234,6 +235,7 @@ public class Main { } // Check that we don't generate a select since we only have a single return. + // TODO: This is no longer true, since D8 does not share the branches with different lines. /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValueWithReturn(boolean) builder (after) /// CHECK: <<Const10:i\d+>> IntConstant 10 @@ -241,7 +243,7 @@ public class Main { /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValueWithReturn(boolean) builder (after) /// CHECK: Return - /// CHECK-NOT: Return + /// CHECK: Return private static int $noinline$testDoubleDiamondSameValueWithReturn(boolean bool_param_1, boolean bool_param_2) { if (bool_param_1) { return 10; @@ -261,18 +263,21 @@ public class Main { /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20 /// CHECK-DAG: Return [<<Const10>>] /// CHECK-DAG: Return [<<Const20>>] + /// CHECK-DAG: Return [<<Const20>>] - // Note that we have 2 returns instead of 3 as the two `return 20;` get merged into one before `select_generator`. + // Note that we have 3 returns as D8 only merges when the line positions are equal. /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllOuterWithReturn(boolean, boolean) select_generator (before) /// CHECK: Return /// CHECK: Return - /// CHECK-NOT: Return + /// CHECK: Return /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllOuterWithReturn(boolean, boolean) select_generator (after) /// CHECK-DAG: <<Bool1:z\d+>> ParameterValue + /// CHECK-DAG: <<Bool2:z\d+>> ParameterValue /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10 /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20 - /// CHECK-DAG: <<Select2:i\d+>> Select [<<Const20>>,<<Const10>>,<<Bool1>>] + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const20>>,<<Const20>>,<<Bool2>>] + /// CHECK-DAG: <<Select2:i\d+>> Select [<<Select>>,<<Const10>>,<<Bool1>>] /// CHECK-DAG: Return [<<Select2>>] private static int $noinline$testDoubleDiamondSameValueButNotAllOuterWithReturn(boolean bool_param_1, boolean bool_param_2) { if (bool_param_1) { diff --git a/test/667-jit-jni-stub/jit_jni_stub_test.cc b/test/667-jit-jni-stub/jit_jni_stub_test.cc index b7946f39ed..52f467e38b 100644 --- a/test/667-jit-jni-stub/jit_jni_stub_test.cc +++ b/test/667-jit-jni-stub/jit_jni_stub_test.cc @@ -39,11 +39,15 @@ extern "C" JNIEXPORT void Java_Main_jitGc(JNIEnv*, jclass) { CHECK(Runtime::Current()->GetJit() != nullptr); jit::JitCodeCache* cache = Runtime::Current()->GetJit()->GetCodeCache(); + Thread* self = Thread::Current(); { - ScopedObjectAccess soa(Thread::Current()); + ScopedObjectAccess soa(self); cache->InvalidateAllCompiledCode(); } - cache->DoCollection(Thread::Current()); + cache->DoCollection(self); + // Run a second time in case the first run was a no-op due to a concurrent JIT + // GC from the JIT thread. + cache->DoCollection(self); } extern "C" JNIEXPORT diff --git a/test/732-checker-app-image/expected-stdout.txt b/test/732-checker-app-image/expected-stdout.txt index a8deeec6de..cb6b036407 100644 --- a/test/732-checker-app-image/expected-stdout.txt +++ b/test/732-checker-app-image/expected-stdout.txt @@ -1,2 +1,5 @@ AppImageClass NonAppImageClass +SecondaryAppImageClass +SecondaryNonAppImageClass +SecondaryClassWithUnmetDependency diff --git a/test/732-checker-app-image/profile b/test/732-checker-app-image/profile index 924c319453..a35f977d7c 100644 --- a/test/732-checker-app-image/profile +++ b/test/732-checker-app-image/profile @@ -5,3 +5,8 @@ SHLMain;->$noinline$getNonAppImageClass()Ljava/lang/Class; SHLMain;->$noinline$callAppImageClassNop()V SHLMain;->$noinline$callAppImageClassWithClinitNop()V SHLMain;->$noinline$callNonAppImageClassNop()V +LSecondaryAppImageClass; +LSecondaryClassWithUnmetDependency; +SHLSecondary;->$noinline$getSecondaryAppImageClass()Ljava/lang/Class; +SHLSecondary;->$noinline$getSecondaryNonAppImageClass()Ljava/lang/Class; +SHLSecondary;->$noinline$getSecondaryClassWithUnmetDependency()Ljava/lang/Class; diff --git a/test/732-checker-app-image/run.py b/test/732-checker-app-image/run.py index 3dc165b8e8..16658852ad 100644 --- a/test/732-checker-app-image/run.py +++ b/test/732-checker-app-image/run.py @@ -16,5 +16,12 @@ def run(ctx, args): - # We need a profile to tell dex2oat to include classes in the final app image - ctx.default_run(args, profile=True) + if args.jvm: + ctx.default_run(args) + else: + ctx.default_run( + args, + # We need a profile to tell dex2oat to include classes in the final app image + profile=True, + # Append graphs for checker tests (we run dex2oat twice) with `--dump-cfg-append`. + Xcompiler_option=["--dump-cfg-append"]) diff --git a/test/732-checker-app-image/src-ex/Secondary.java b/test/732-checker-app-image/src-ex/Secondary.java new file mode 100644 index 0000000000..e544e6c162 --- /dev/null +++ b/test/732-checker-app-image/src-ex/Secondary.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Secondary { + public static void main() { + System.out.println($noinline$getSecondaryAppImageClass().getName()); + System.out.println($noinline$getSecondaryNonAppImageClass().getName()); + System.out.println($noinline$getSecondaryClassWithUnmetDependency().getName()); + } + + /// CHECK-START: java.lang.Class Secondary.$noinline$getSecondaryAppImageClass() builder (after) + /// CHECK: LoadClass load_kind:AppImageRelRo in_image:true + public static Class<?> $noinline$getSecondaryAppImageClass() { + return SecondaryAppImageClass.class; + } + + /// CHECK-START: java.lang.Class Secondary.$noinline$getSecondaryNonAppImageClass() builder (after) + /// CHECK: LoadClass load_kind:BssEntry in_image:false + public static Class<?> $noinline$getSecondaryNonAppImageClass() { + return SecondaryNonAppImageClass.class; + } + + /// CHECK-START: java.lang.Class Secondary.$noinline$getSecondaryClassWithUnmetDependency() builder (after) + /// CHECK: LoadClass load_kind:BssEntry in_image:false + public static Class<?> $noinline$getSecondaryClassWithUnmetDependency() { + return SecondaryClassWithUnmetDependency.class; + } +} + +class SecondaryAppImageClass { // Included in the profile. +} + +class SecondaryNonAppImageClass { // Not included in the profile. +} + +// Included in the profile but with interface defined in parent class loader +// and therefore unsuitable for inclusion in the app image. +class SecondaryClassWithUnmetDependency implements PrimaryInterface {} diff --git a/test/732-checker-app-image/src/Main.java b/test/732-checker-app-image/src/Main.java index 200708a20b..815902940d 100644 --- a/test/732-checker-app-image/src/Main.java +++ b/test/732-checker-app-image/src/Main.java @@ -14,18 +14,43 @@ * limitations under the License. */ +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + public class Main { - public static void main(String args[]) { + public static String TEST_NAME = "732-checker-app-image"; + + public static ClassLoader getSecondaryClassLoader() throws Exception { + String location = System.getenv("DEX_LOCATION"); + try { + Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader"); + Constructor<?> ctor = + class_loader_class.getConstructor(String.class, ClassLoader.class); + return (ClassLoader) ctor.newInstance(location + "/" + TEST_NAME + "-ex.jar", + Main.class.getClassLoader()); + } catch (ClassNotFoundException e) { + // Running on RI. Use URLClassLoader. + return new java.net.URLClassLoader( + new java.net.URL[] { new java.net.URL("file://" + location + "/classes-ex/") }); + } + } + + public static void main(String args[]) throws Exception { System.out.println($noinline$getAppImageClass().getName()); System.out.println($noinline$getNonAppImageClass().getName()); $noinline$callAppImageClassNop(); $noinline$callAppImageClassWithClinitNop(); $noinline$callNonAppImageClassNop(); + + ClassLoader secondaryLoader = getSecondaryClassLoader(); + Class<?> secondaryClass = Class.forName("Secondary", true, secondaryLoader); + Method secondaryMain = secondaryClass.getMethod("main"); + secondaryMain.invoke(null); } /// CHECK-START: java.lang.Class Main.$noinline$getAppImageClass() builder (after) - /// CHECK: LoadClass load_kind:BssEntry in_image:true + /// CHECK: LoadClass load_kind:AppImageRelRo in_image:true public static Class<?> $noinline$getAppImageClass() { return AppImageClass.class; } @@ -52,22 +77,20 @@ public class Main { } /// CHECK-START: void Main.$noinline$callAppImageClassWithClinitNop() builder (after) - /// CHECK: LoadClass load_kind:BssEntry in_image:true gen_clinit_check:false + /// CHECK: LoadClass load_kind:AppImageRelRo in_image:true gen_clinit_check:false /// CHECK: ClinitCheck /// CHECK: InvokeStaticOrDirect clinit_check:explicit /// CHECK-START: void Main.$noinline$callAppImageClassWithClinitNop() inliner (after) - /// CHECK: LoadClass load_kind:BssEntry in_image:true gen_clinit_check:false + /// CHECK: LoadClass load_kind:AppImageRelRo in_image:true gen_clinit_check:false /// CHECK: ClinitCheck /// CHECK-START: void Main.$noinline$callAppImageClassWithClinitNop() inliner (after) /// CHECK-NOT: InvokeStaticOrDirect /// CHECK-START: void Main.$noinline$callAppImageClassWithClinitNop() prepare_for_register_allocation (after) - /// CHECK: LoadClass load_kind:BssEntry in_image:true gen_clinit_check:true - - /// CHECK-START: void Main.$noinline$callAppImageClassWithClinitNop() prepare_for_register_allocation (after) - /// CHECK-NOT: ClinitCheck + /// CHECK: LoadClass load_kind:AppImageRelRo in_image:true gen_clinit_check:false + /// CHECK: ClinitCheck public static void $noinline$callAppImageClassWithClinitNop() { AppImageClassWithClinit.$inline$nop(); } diff --git a/test/732-checker-app-image/src/PrimaryInterface.java b/test/732-checker-app-image/src/PrimaryInterface.java new file mode 100644 index 0000000000..4b2e3f4a7e --- /dev/null +++ b/test/732-checker-app-image/src/PrimaryInterface.java @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface PrimaryInterface {} diff --git a/test/854-image-inlining/expected-stderr.txt b/test/854-image-inlining/expected-stderr.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/854-image-inlining/expected-stderr.txt diff --git a/test/854-image-inlining/expected-stdout.txt b/test/854-image-inlining/expected-stdout.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/854-image-inlining/expected-stdout.txt diff --git a/test/854-image-inlining/info.txt b/test/854-image-inlining/info.txt new file mode 100644 index 0000000000..92564a9893 --- /dev/null +++ b/test/854-image-inlining/info.txt @@ -0,0 +1,4 @@ +We used to incorrectly assume a class in the profile would mean it +would be in the app image. + +This got fixed with aosp/3064144. diff --git a/test/854-image-inlining/profile b/test/854-image-inlining/profile new file mode 100644 index 0000000000..ec8fb7521c --- /dev/null +++ b/test/854-image-inlining/profile @@ -0,0 +1,7 @@ +# We need callInstance to be AOT compiled. The AOT compiled code used to wrongly assume +# the CallStatic class was loaded. +HSLTest;->callInstance()I + +# We need CallStatic to be in the profile to make the compiler think it will be part of +# the image. +LCallStatic; diff --git a/test/854-image-inlining/run.py b/test/854-image-inlining/run.py new file mode 100644 index 0000000000..621611002e --- /dev/null +++ b/test/854-image-inlining/run.py @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright (C) 2024 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def run(ctx, args): + # Make sure we pass the profile when compiling. + ctx.default_run(args, profile=True) diff --git a/test/854-image-inlining/src-ex/Test.java b/test/854-image-inlining/src-ex/Test.java new file mode 100644 index 0000000000..6f56a2759d --- /dev/null +++ b/test/854-image-inlining/src-ex/Test.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class WithStatic { + public static int field = 42; +} + +// `CallStatic` being part of the profile, the compiler used to think it will be in the app image. +// However, given it implements `Itf` which is from a different dex file from a different class +// loader, we cannot encode `CallStatic` in the image. We used to find that information too late in +// the compilation stage and were therefore wrongly assuming we could inline methods from +// `CallStatic` without the need for resolving it. +// +// Note that to trigger the crash, we needed an interface from a different dex file. We were +// correctly pruning the class if the superclass was from a different dex file. +class CallStatic implements Itf { + public static int $inline$foo() { + // Access a static field to make sure we invoke the runtime, which will then walk the call + // stack. + return WithStatic.field; + } +} + +public class Test { + public static int callInstance() { + // Call a method from `CallStatic` which will be inlined. If the compiler thinks `CallStatic` + // will be in the image, it will skip the slow path of resolving it. + return CallStatic.$inline$foo(); + } +} diff --git a/test/854-image-inlining/src/Itf.java b/test/854-image-inlining/src/Itf.java new file mode 100644 index 0000000000..963a194116 --- /dev/null +++ b/test/854-image-inlining/src/Itf.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface Itf { +} diff --git a/test/854-image-inlining/src/Main.java b/test/854-image-inlining/src/Main.java new file mode 100644 index 0000000000..f983344833 --- /dev/null +++ b/test/854-image-inlining/src/Main.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +public class Main { + static final String DEX_FILE = + System.getenv("DEX_LOCATION") + "/854-image-inlining-ex.jar"; + static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path"); + + public static void main(String[] args) throws Exception { + // Create the secondary class loader. + Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); + Constructor<?> constructor = + pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class); + ClassLoader loader = (ClassLoader) constructor.newInstance( + DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); + + // Invoke the test method to trigger the runtime crash. + Method m = loader.loadClass("Test").getDeclaredMethod("callInstance"); + int result = ((Integer) m.invoke(null)).intValue(); + if (result != 42) { + throw new Error("Expected 42, got " + result); + } + } +} diff --git a/test/989-method-trace-throw/expected-stdout.txt b/test/989-method-trace-throw/expected-stdout.txt index 0911bc35e8..6bd7357699 100644 --- a/test/989-method-trace-throw/expected-stdout.txt +++ b/test/989-method-trace-throw/expected-stdout.txt @@ -10,6 +10,11 @@ Received expected error for test[class art.Test989$NormalTracer, class art.Test9 Normal: Entering public static native void art.Test989.throwANative() Normal: Leaving public static native void art.Test989.throwANative() returned <exception> Received expected error for test[class art.Test989$NormalTracer, class art.Test989$throwANativeClass] - art.Test989$ErrorA: Throwing Error A +Normal: Entering public static void art.Test989.throwNativeException() +Normal: Entering public static native void art.Test989.doThrowNative() +Normal: Leaving public static native void art.Test989.doThrowNative() returned <exception> +Normal: Leaving public static void art.Test989.throwNativeException() returned <exception> +Received expected error for test[class art.Test989$NormalTracer, class art.Test989$throwNativeExceptionClass] - java.lang.Error: Error Normal: Entering public static java.lang.Object art.Test989.returnValue() Normal: Leaving public static java.lang.Object art.Test989.returnValue() returned TestObject(0) returnValue returned: TestObject(0) @@ -57,6 +62,9 @@ Received expected error for test[class art.Test989$ThrowEnterTracer, class art.T ThrowEnter: Entering public static native void art.Test989.throwANative() ThrowEnter: Leaving public static native void art.Test989.throwANative() returned <exception> Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$throwANativeClass] - art.Test989$ErrorB: Throwing error while entering public static native void art.Test989.throwANative() +ThrowEnter: Entering public static void art.Test989.throwNativeException() +ThrowEnter: Leaving public static void art.Test989.throwNativeException() returned <exception> +Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$throwNativeExceptionClass] - art.Test989$ErrorB: Throwing error while entering public static void art.Test989.throwNativeException() ThrowEnter: Entering public static java.lang.Object art.Test989.returnValue() ThrowEnter: Leaving public static java.lang.Object art.Test989.returnValue() returned <exception> Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$returnValueClass] - art.Test989$ErrorB: Throwing error while entering public static java.lang.Object art.Test989.returnValue() @@ -96,6 +104,11 @@ Received expected error for test[class art.Test989$ThrowExitTracer, class art.Te ThrowExit: Entering public static native void art.Test989.throwANative() ThrowExit: Leaving public static native void art.Test989.throwANative() returned <exception> Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$throwANativeClass] - art.Test989$ErrorB: Throwing error while exit public static native void art.Test989.throwANative() returned <exception> +ThrowExit: Entering public static void art.Test989.throwNativeException() +ThrowExit: Entering public static native void art.Test989.doThrowNative() +ThrowExit: Leaving public static native void art.Test989.doThrowNative() returned <exception> +ThrowExit: Leaving public static void art.Test989.throwNativeException() returned <exception> +Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$throwNativeExceptionClass] - art.Test989$ErrorB: Throwing error while exit public static void art.Test989.throwNativeException() returned <exception> ThrowExit: Entering public static java.lang.Object art.Test989.returnValue() ThrowExit: Leaving public static java.lang.Object art.Test989.returnValue() returned TestObject(7) Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$returnValueClass] - art.Test989$ErrorB: Throwing error while exit public static java.lang.Object art.Test989.returnValue() returned TestObject(7) @@ -137,6 +150,9 @@ Received expected error for test[class art.Test989$ThrowBothTracer, class art.Te ThrowBoth: Entering public static native void art.Test989.throwANative() ThrowBoth: Leaving public static native void art.Test989.throwANative() returned <exception> Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$throwANativeClass] - art.Test989$ErrorC: Throwing error while exit public static native void art.Test989.throwANative() returned <exception> +ThrowBoth: Entering public static void art.Test989.throwNativeException() +ThrowBoth: Leaving public static void art.Test989.throwNativeException() returned <exception> +Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$throwNativeExceptionClass] - art.Test989$ErrorC: Throwing error while exit public static void art.Test989.throwNativeException() returned <exception> ThrowBoth: Entering public static java.lang.Object art.Test989.returnValue() ThrowBoth: Leaving public static java.lang.Object art.Test989.returnValue() returned <exception> Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$returnValueClass] - art.Test989$ErrorC: Throwing error while exit public static java.lang.Object art.Test989.returnValue() returned <exception> @@ -168,6 +184,7 @@ Received no exception as expected for test[class art.Test989$ForceGCTracer, clas Received no exception as expected for test[class art.Test989$ForceGCTracer, class art.Test989$doNothingNativeClass]. Received expected error for test[class art.Test989$ForceGCTracer, class art.Test989$throwAClass] - art.Test989$ErrorA: Throwing Error A Received expected error for test[class art.Test989$ForceGCTracer, class art.Test989$throwANativeClass] - art.Test989$ErrorA: Throwing Error A +Received expected error for test[class art.Test989$ForceGCTracer, class art.Test989$throwNativeExceptionClass] - java.lang.Error: Error returnValue returned: TestObject(14) Received no exception as expected for test[class art.Test989$ForceGCTracer, class art.Test989$returnValueClass]. returnValueNative returned: TestObject(15) @@ -185,4 +202,209 @@ returnDouble returned: 3.14159628 Received no exception as expected for test[class art.Test989$ForceGCTracer, class art.Test989$returnDoubleClass]. returnDoubleNative returned: 3.14159628 Received no exception as expected for test[class art.Test989$ForceGCTracer, class art.Test989$returnDoubleNativeClass]. +Finished - without non-standard exits! +Normal: Entering public static void art.Test989.doNothing() +Normal: Leaving public static void art.Test989.doNothing() returned null +Received no exception as expected for test[class art.Test989$NormalTracer, class art.Test989$doNothingClass]. +Normal: Entering public static native void art.Test989.doNothingNative() +Normal: Leaving public static native void art.Test989.doNothingNative() returned null +Received no exception as expected for test[class art.Test989$NormalTracer, class art.Test989$doNothingNativeClass]. +Normal: Entering public static void art.Test989.throwA() +Normal: Leaving public static void art.Test989.throwA() returned <exception> +Received expected error for test[class art.Test989$NormalTracer, class art.Test989$throwAClass] - art.Test989$ErrorA: Throwing Error A +Normal: Entering public static native void art.Test989.throwANative() +Normal: Leaving public static native void art.Test989.throwANative() returned <exception> +Received expected error for test[class art.Test989$NormalTracer, class art.Test989$throwANativeClass] - art.Test989$ErrorA: Throwing Error A +Normal: Entering public static void art.Test989.throwNativeException() +Normal: Entering public static native void art.Test989.doThrowNative() +Normal: Leaving public static native void art.Test989.doThrowNative() returned <exception> +Normal: Leaving public static void art.Test989.throwNativeException() returned <exception> +Received expected error for test[class art.Test989$NormalTracer, class art.Test989$throwNativeExceptionClass] - java.lang.Error: Error +Normal: Entering public static java.lang.Object art.Test989.returnValue() +Normal: Leaving public static java.lang.Object art.Test989.returnValue() returned TestObject(19) +returnValue returned: TestObject(19) +Received no exception as expected for test[class art.Test989$NormalTracer, class art.Test989$returnValueClass]. +Normal: Entering public static native java.lang.Object art.Test989.returnValueNative() +Normal: Leaving public static native java.lang.Object art.Test989.returnValueNative() returned TestObject(20) +returnValueNative returned: TestObject(20) +Received no exception as expected for test[class art.Test989$NormalTracer, class art.Test989$returnValueNativeClass]. +Normal: Entering public static void art.Test989.acceptValue(java.lang.Object) +Recieved TestObject(21) +Normal: Leaving public static void art.Test989.acceptValue(java.lang.Object) returned null +Received no exception as expected for test[class art.Test989$NormalTracer, class art.Test989$acceptValueClass]. +Normal: Entering public static native void art.Test989.acceptValueNative(java.lang.Object) +Recieved TestObject(22) +Normal: Leaving public static native void art.Test989.acceptValueNative(java.lang.Object) returned null +Received no exception as expected for test[class art.Test989$NormalTracer, class art.Test989$acceptValueNativeClass]. +Normal: Entering public static void art.Test989.tryCatchExit() +Normal: Leaving public static void art.Test989.tryCatchExit() returned null +Received no exception as expected for test[class art.Test989$NormalTracer, class art.Test989$tryCatchExitClass]. +Normal: Entering public static float art.Test989.returnFloat() +Normal: Leaving public static float art.Test989.returnFloat() returned 1.618 +returnFloat returned: 1.618 +Received no exception as expected for test[class art.Test989$NormalTracer, class art.Test989$returnFloatClass]. +Normal: Entering public static native float art.Test989.returnFloatNative() +Normal: Leaving public static native float art.Test989.returnFloatNative() returned 1.618 +returnFloatNative returned: 1.618 +Received no exception as expected for test[class art.Test989$NormalTracer, class art.Test989$returnFloatNativeClass]. +Normal: Entering public static double art.Test989.returnDouble() +Normal: Leaving public static double art.Test989.returnDouble() returned 3.14159628 +returnDouble returned: 3.14159628 +Received no exception as expected for test[class art.Test989$NormalTracer, class art.Test989$returnDoubleClass]. +Normal: Entering public static native double art.Test989.returnDoubleNative() +Normal: Leaving public static native double art.Test989.returnDoubleNative() returned 3.14159628 +returnDoubleNative returned: 3.14159628 +Received no exception as expected for test[class art.Test989$NormalTracer, class art.Test989$returnDoubleNativeClass]. +ThrowEnter: Entering public static void art.Test989.doNothing() +ThrowEnter: Leaving public static void art.Test989.doNothing() returned <exception> +Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$doNothingClass] - art.Test989$ErrorB: Throwing error while entering public static void art.Test989.doNothing() +ThrowEnter: Entering public static native void art.Test989.doNothingNative() +ThrowEnter: Leaving public static native void art.Test989.doNothingNative() returned <exception> +Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$doNothingNativeClass] - art.Test989$ErrorB: Throwing error while entering public static native void art.Test989.doNothingNative() +ThrowEnter: Entering public static void art.Test989.throwA() +ThrowEnter: Leaving public static void art.Test989.throwA() returned <exception> +Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$throwAClass] - art.Test989$ErrorB: Throwing error while entering public static void art.Test989.throwA() +ThrowEnter: Entering public static native void art.Test989.throwANative() +ThrowEnter: Leaving public static native void art.Test989.throwANative() returned <exception> +Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$throwANativeClass] - art.Test989$ErrorB: Throwing error while entering public static native void art.Test989.throwANative() +ThrowEnter: Entering public static void art.Test989.throwNativeException() +ThrowEnter: Leaving public static void art.Test989.throwNativeException() returned <exception> +Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$throwNativeExceptionClass] - art.Test989$ErrorB: Throwing error while entering public static void art.Test989.throwNativeException() +ThrowEnter: Entering public static java.lang.Object art.Test989.returnValue() +ThrowEnter: Leaving public static java.lang.Object art.Test989.returnValue() returned <exception> +Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$returnValueClass] - art.Test989$ErrorB: Throwing error while entering public static java.lang.Object art.Test989.returnValue() +ThrowEnter: Entering public static native java.lang.Object art.Test989.returnValueNative() +ThrowEnter: Leaving public static native java.lang.Object art.Test989.returnValueNative() returned <exception> +Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$returnValueNativeClass] - art.Test989$ErrorB: Throwing error while entering public static native java.lang.Object art.Test989.returnValueNative() +ThrowEnter: Entering public static void art.Test989.acceptValue(java.lang.Object) +ThrowEnter: Leaving public static void art.Test989.acceptValue(java.lang.Object) returned <exception> +Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$acceptValueClass] - art.Test989$ErrorB: Throwing error while entering public static void art.Test989.acceptValue(java.lang.Object) +ThrowEnter: Entering public static native void art.Test989.acceptValueNative(java.lang.Object) +ThrowEnter: Leaving public static native void art.Test989.acceptValueNative(java.lang.Object) returned <exception> +Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$acceptValueNativeClass] - art.Test989$ErrorB: Throwing error while entering public static native void art.Test989.acceptValueNative(java.lang.Object) +ThrowEnter: Entering public static void art.Test989.tryCatchExit() +ThrowEnter: Leaving public static void art.Test989.tryCatchExit() returned <exception> +Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$tryCatchExitClass] - art.Test989$ErrorB: Throwing error while entering public static void art.Test989.tryCatchExit() +ThrowEnter: Entering public static float art.Test989.returnFloat() +ThrowEnter: Leaving public static float art.Test989.returnFloat() returned <exception> +Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$returnFloatClass] - art.Test989$ErrorB: Throwing error while entering public static float art.Test989.returnFloat() +ThrowEnter: Entering public static native float art.Test989.returnFloatNative() +ThrowEnter: Leaving public static native float art.Test989.returnFloatNative() returned <exception> +Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$returnFloatNativeClass] - art.Test989$ErrorB: Throwing error while entering public static native float art.Test989.returnFloatNative() +ThrowEnter: Entering public static double art.Test989.returnDouble() +ThrowEnter: Leaving public static double art.Test989.returnDouble() returned <exception> +Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$returnDoubleClass] - art.Test989$ErrorB: Throwing error while entering public static double art.Test989.returnDouble() +ThrowEnter: Entering public static native double art.Test989.returnDoubleNative() +ThrowEnter: Leaving public static native double art.Test989.returnDoubleNative() returned <exception> +Received expected error for test[class art.Test989$ThrowEnterTracer, class art.Test989$returnDoubleNativeClass] - art.Test989$ErrorB: Throwing error while entering public static native double art.Test989.returnDoubleNative() +ThrowExit: Entering public static void art.Test989.doNothing() +ThrowExit: Leaving public static void art.Test989.doNothing() returned null +Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$doNothingClass] - art.Test989$ErrorB: Throwing error while exit public static void art.Test989.doNothing() returned null +ThrowExit: Entering public static native void art.Test989.doNothingNative() +ThrowExit: Leaving public static native void art.Test989.doNothingNative() returned null +Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$doNothingNativeClass] - art.Test989$ErrorB: Throwing error while exit public static native void art.Test989.doNothingNative() returned null +ThrowExit: Entering public static void art.Test989.throwA() +ThrowExit: Leaving public static void art.Test989.throwA() returned <exception> +Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$throwAClass] - art.Test989$ErrorB: Throwing error while exit public static void art.Test989.throwA() returned <exception> +ThrowExit: Entering public static native void art.Test989.throwANative() +ThrowExit: Leaving public static native void art.Test989.throwANative() returned <exception> +Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$throwANativeClass] - art.Test989$ErrorB: Throwing error while exit public static native void art.Test989.throwANative() returned <exception> +ThrowExit: Entering public static void art.Test989.throwNativeException() +ThrowExit: Entering public static native void art.Test989.doThrowNative() +ThrowExit: Leaving public static native void art.Test989.doThrowNative() returned <exception> +ThrowExit: Leaving public static void art.Test989.throwNativeException() returned <exception> +Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$throwNativeExceptionClass] - art.Test989$ErrorB: Throwing error while exit public static void art.Test989.throwNativeException() returned <exception> +ThrowExit: Entering public static java.lang.Object art.Test989.returnValue() +ThrowExit: Leaving public static java.lang.Object art.Test989.returnValue() returned TestObject(26) +Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$returnValueClass] - art.Test989$ErrorB: Throwing error while exit public static java.lang.Object art.Test989.returnValue() returned TestObject(26) +ThrowExit: Entering public static native java.lang.Object art.Test989.returnValueNative() +ThrowExit: Leaving public static native java.lang.Object art.Test989.returnValueNative() returned TestObject(27) +Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$returnValueNativeClass] - art.Test989$ErrorB: Throwing error while exit public static native java.lang.Object art.Test989.returnValueNative() returned TestObject(27) +ThrowExit: Entering public static void art.Test989.acceptValue(java.lang.Object) +Recieved TestObject(28) +ThrowExit: Leaving public static void art.Test989.acceptValue(java.lang.Object) returned null +Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$acceptValueClass] - art.Test989$ErrorB: Throwing error while exit public static void art.Test989.acceptValue(java.lang.Object) returned null +ThrowExit: Entering public static native void art.Test989.acceptValueNative(java.lang.Object) +Recieved TestObject(29) +ThrowExit: Leaving public static native void art.Test989.acceptValueNative(java.lang.Object) returned null +Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$acceptValueNativeClass] - art.Test989$ErrorB: Throwing error while exit public static native void art.Test989.acceptValueNative(java.lang.Object) returned null +ThrowExit: Entering public static void art.Test989.tryCatchExit() +ThrowExit: Leaving public static void art.Test989.tryCatchExit() returned null +Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$tryCatchExitClass] - art.Test989$ErrorB: Throwing error while exit public static void art.Test989.tryCatchExit() returned null +ThrowExit: Entering public static float art.Test989.returnFloat() +ThrowExit: Leaving public static float art.Test989.returnFloat() returned 1.618 +Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$returnFloatClass] - art.Test989$ErrorB: Throwing error while exit public static float art.Test989.returnFloat() returned 1.618 +ThrowExit: Entering public static native float art.Test989.returnFloatNative() +ThrowExit: Leaving public static native float art.Test989.returnFloatNative() returned 1.618 +Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$returnFloatNativeClass] - art.Test989$ErrorB: Throwing error while exit public static native float art.Test989.returnFloatNative() returned 1.618 +ThrowExit: Entering public static double art.Test989.returnDouble() +ThrowExit: Leaving public static double art.Test989.returnDouble() returned 3.14159628 +Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$returnDoubleClass] - art.Test989$ErrorB: Throwing error while exit public static double art.Test989.returnDouble() returned 3.14159628 +ThrowExit: Entering public static native double art.Test989.returnDoubleNative() +ThrowExit: Leaving public static native double art.Test989.returnDoubleNative() returned 3.14159628 +Received expected error for test[class art.Test989$ThrowExitTracer, class art.Test989$returnDoubleNativeClass] - art.Test989$ErrorB: Throwing error while exit public static native double art.Test989.returnDoubleNative() returned 3.14159628 +ThrowBoth: Entering public static void art.Test989.doNothing() +ThrowBoth: Leaving public static void art.Test989.doNothing() returned <exception> +Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$doNothingClass] - art.Test989$ErrorC: Throwing error while exit public static void art.Test989.doNothing() returned <exception> +ThrowBoth: Entering public static native void art.Test989.doNothingNative() +ThrowBoth: Leaving public static native void art.Test989.doNothingNative() returned <exception> +Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$doNothingNativeClass] - art.Test989$ErrorC: Throwing error while exit public static native void art.Test989.doNothingNative() returned <exception> +ThrowBoth: Entering public static void art.Test989.throwA() +ThrowBoth: Leaving public static void art.Test989.throwA() returned <exception> +Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$throwAClass] - art.Test989$ErrorC: Throwing error while exit public static void art.Test989.throwA() returned <exception> +ThrowBoth: Entering public static native void art.Test989.throwANative() +ThrowBoth: Leaving public static native void art.Test989.throwANative() returned <exception> +Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$throwANativeClass] - art.Test989$ErrorC: Throwing error while exit public static native void art.Test989.throwANative() returned <exception> +ThrowBoth: Entering public static void art.Test989.throwNativeException() +ThrowBoth: Leaving public static void art.Test989.throwNativeException() returned <exception> +Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$throwNativeExceptionClass] - art.Test989$ErrorC: Throwing error while exit public static void art.Test989.throwNativeException() returned <exception> +ThrowBoth: Entering public static java.lang.Object art.Test989.returnValue() +ThrowBoth: Leaving public static java.lang.Object art.Test989.returnValue() returned <exception> +Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$returnValueClass] - art.Test989$ErrorC: Throwing error while exit public static java.lang.Object art.Test989.returnValue() returned <exception> +ThrowBoth: Entering public static native java.lang.Object art.Test989.returnValueNative() +ThrowBoth: Leaving public static native java.lang.Object art.Test989.returnValueNative() returned <exception> +Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$returnValueNativeClass] - art.Test989$ErrorC: Throwing error while exit public static native java.lang.Object art.Test989.returnValueNative() returned <exception> +ThrowBoth: Entering public static void art.Test989.acceptValue(java.lang.Object) +ThrowBoth: Leaving public static void art.Test989.acceptValue(java.lang.Object) returned <exception> +Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$acceptValueClass] - art.Test989$ErrorC: Throwing error while exit public static void art.Test989.acceptValue(java.lang.Object) returned <exception> +ThrowBoth: Entering public static native void art.Test989.acceptValueNative(java.lang.Object) +ThrowBoth: Leaving public static native void art.Test989.acceptValueNative(java.lang.Object) returned <exception> +Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$acceptValueNativeClass] - art.Test989$ErrorC: Throwing error while exit public static native void art.Test989.acceptValueNative(java.lang.Object) returned <exception> +ThrowBoth: Entering public static void art.Test989.tryCatchExit() +ThrowBoth: Leaving public static void art.Test989.tryCatchExit() returned <exception> +Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$tryCatchExitClass] - art.Test989$ErrorC: Throwing error while exit public static void art.Test989.tryCatchExit() returned <exception> +ThrowBoth: Entering public static float art.Test989.returnFloat() +ThrowBoth: Leaving public static float art.Test989.returnFloat() returned <exception> +Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$returnFloatClass] - art.Test989$ErrorC: Throwing error while exit public static float art.Test989.returnFloat() returned <exception> +ThrowBoth: Entering public static native float art.Test989.returnFloatNative() +ThrowBoth: Leaving public static native float art.Test989.returnFloatNative() returned <exception> +Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$returnFloatNativeClass] - art.Test989$ErrorC: Throwing error while exit public static native float art.Test989.returnFloatNative() returned <exception> +ThrowBoth: Entering public static double art.Test989.returnDouble() +ThrowBoth: Leaving public static double art.Test989.returnDouble() returned <exception> +Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$returnDoubleClass] - art.Test989$ErrorC: Throwing error while exit public static double art.Test989.returnDouble() returned <exception> +ThrowBoth: Entering public static native double art.Test989.returnDoubleNative() +ThrowBoth: Leaving public static native double art.Test989.returnDoubleNative() returned <exception> +Received expected error for test[class art.Test989$ThrowBothTracer, class art.Test989$returnDoubleNativeClass] - art.Test989$ErrorC: Throwing error while exit public static native double art.Test989.returnDoubleNative() returned <exception> +Received no exception as expected for test[class art.Test989$ForceGCTracer, class art.Test989$doNothingClass]. +Received no exception as expected for test[class art.Test989$ForceGCTracer, class art.Test989$doNothingNativeClass]. +Received expected error for test[class art.Test989$ForceGCTracer, class art.Test989$throwAClass] - art.Test989$ErrorA: Throwing Error A +Received expected error for test[class art.Test989$ForceGCTracer, class art.Test989$throwANativeClass] - art.Test989$ErrorA: Throwing Error A +Received expected error for test[class art.Test989$ForceGCTracer, class art.Test989$throwNativeExceptionClass] - java.lang.Error: Error +returnValue returned: TestObject(33) +Received no exception as expected for test[class art.Test989$ForceGCTracer, class art.Test989$returnValueClass]. +returnValueNative returned: TestObject(34) +Received no exception as expected for test[class art.Test989$ForceGCTracer, class art.Test989$returnValueNativeClass]. +Recieved TestObject(35) +Received no exception as expected for test[class art.Test989$ForceGCTracer, class art.Test989$acceptValueClass]. +Recieved TestObject(36) +Received no exception as expected for test[class art.Test989$ForceGCTracer, class art.Test989$acceptValueNativeClass]. +Received no exception as expected for test[class art.Test989$ForceGCTracer, class art.Test989$tryCatchExitClass]. +returnFloat returned: 1.618 +Received no exception as expected for test[class art.Test989$ForceGCTracer, class art.Test989$returnFloatClass]. +returnFloatNative returned: 1.618 +Received no exception as expected for test[class art.Test989$ForceGCTracer, class art.Test989$returnFloatNativeClass]. +returnDouble returned: 3.14159628 +Received no exception as expected for test[class art.Test989$ForceGCTracer, class art.Test989$returnDoubleClass]. +returnDoubleNative returned: 3.14159628 +Received no exception as expected for test[class art.Test989$ForceGCTracer, class art.Test989$returnDoubleNativeClass]. Finished! diff --git a/test/989-method-trace-throw/method_trace.cc b/test/989-method-trace-throw/method_trace.cc index edfff907a0..1f62aa7607 100644 --- a/test/989-method-trace-throw/method_trace.cc +++ b/test/989-method-trace-throw/method_trace.cc @@ -63,6 +63,12 @@ extern "C" JNIEXPORT void JNICALL Java_art_Test989_throwANative(JNIEnv* env, env->CallStaticVoidMethod(klass, targetMethod); } +extern "C" JNIEXPORT void JNICALL Java_art_Test989_doThrowNative(JNIEnv* env, + [[maybe_unused]] jclass klass) { + jclass exception_cls = env->FindClass("java/lang/Error"); + env->ThrowNew(exception_cls, "Error"); +} + extern "C" JNIEXPORT void JNICALL Java_art_Test989_acceptValueNative(JNIEnv* env, jclass klass, jobject arg) { diff --git a/test/989-method-trace-throw/src/art/Test989.java b/test/989-method-trace-throw/src/art/Test989.java index f18d5ef3d6..fec09e0934 100644 --- a/test/989-method-trace-throw/src/art/Test989.java +++ b/test/989-method-trace-throw/src/art/Test989.java @@ -37,6 +37,7 @@ public class Test989 { testMethods.add(Test989.class.getDeclaredMethod("doNothingNative")); testMethods.add(Test989.class.getDeclaredMethod("throwA")); testMethods.add(Test989.class.getDeclaredMethod("throwANative")); + testMethods.add(Test989.class.getDeclaredMethod("throwNativeException")); testMethods.add(Test989.class.getDeclaredMethod("returnFloat")); testMethods.add(Test989.class.getDeclaredMethod("returnFloatNative")); testMethods.add(Test989.class.getDeclaredMethod("returnDouble")); @@ -46,6 +47,7 @@ public class Test989 { testMethods.add(Test989.class.getDeclaredMethod("acceptValue", Object.class)); testMethods.add(Test989.class.getDeclaredMethod("acceptValueNative", Object.class)); testMethods.add(Test989.class.getDeclaredMethod("tryCatchExit")); + testMethods.add(Test989.class.getDeclaredMethod("doThrowNative")); } catch (Exception e) { throw new Error("Bad static!", e); } @@ -244,35 +246,50 @@ public class Test989 { } public static void run() throws Exception { - MyRunnable[] testCases = new MyRunnable[] { - new doNothingClass(), - new doNothingNativeClass(), - new throwAClass(), - new throwANativeClass(), - new returnValueClass(), - new returnValueNativeClass(), - new acceptValueClass(), - new acceptValueNativeClass(), - new tryCatchExitClass(), - new returnFloatClass(), - new returnFloatNativeClass(), - new returnDoubleClass(), - new returnDoubleNativeClass(), - }; - MethodTracer[] tracers = new MethodTracer[] { - new NormalTracer(), - new ThrowEnterTracer(), - new ThrowExitTracer(), - new ThrowBothTracer(), - new ForceGCTracer(), - }; - - setupTracing(); - for (MethodTracer t : tracers) { - for (MyRunnable r : testCases) { - doTest(t, r); + MyRunnable[] testCases = new MyRunnable[] { + new doNothingClass(), + new doNothingNativeClass(), + new throwAClass(), + new throwANativeClass(), + new throwNativeExceptionClass(), + new returnValueClass(), + new returnValueNativeClass(), + new acceptValueClass(), + new acceptValueNativeClass(), + new tryCatchExitClass(), + new returnFloatClass(), + new returnFloatNativeClass(), + new returnDoubleClass(), + new returnDoubleNativeClass(), + }; + MethodTracer[] tracers = new MethodTracer[] { + new NormalTracer(), + new ThrowEnterTracer(), + new ThrowExitTracer(), + new ThrowBothTracer(), + new ForceGCTracer(), + }; + + setupTracing(); + for (MethodTracer t : tracers) { + for (MyRunnable r : testCases) { + doTest(t, r); + } + } + + maybeDisableTracing(); + System.out.println("Finished - without non-standard exits!"); + Trace.disableTracing(Thread.currentThread()); + + // Enabling frame pop events force a different path and deoptimize more often. So redo the + // tests by enabling frame pop events. + Trace.enableFramePopEvents(); + setupTracing(); + for (MethodTracer t : tracers) { + for (MyRunnable r : testCases) { + doTest(t, r); + } } - } maybeDisableTracing(); System.out.println("Finished!"); @@ -299,6 +316,17 @@ public class Test989 { } } + private static final class throwNativeExceptionClass implements MyRunnable { + public void run() { + throwNativeException(); + } + + @Override + public Class<?> expectedThrow() { + return Error.class; + } + } + private static final class tryCatchExitClass implements MyRunnable { public void run() { tryCatchExit(); @@ -419,6 +447,10 @@ public class Test989 { throw new ErrorA("Throwing Error A"); } + public static void throwNativeException() { + doThrowNative(); + } + static final class TestObject { private int idx; public TestObject(int v) { @@ -464,4 +496,5 @@ public class Test989 { public static native void throwANative(); public static native float returnFloatNative(); public static native double returnDoubleNative(); + public static native void doThrowNative(); } diff --git a/test/Android.bp b/test/Android.bp index 29f05b2d9f..611f7ec9b0 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -26,15 +26,8 @@ package { default_team: "trendy_team_art_performance", } -soong_config_module_type_import { - from: "art/build/SoongConfig.bp", - module_types: [ - "art_module_cc_defaults", - ], -} - // Properties common to `art_test_defaults` and `art_standalone_test_defaults`. -art_module_cc_defaults { +cc_defaults { name: "art_test_common_defaults", defaults: ["art_defaults"], @@ -68,7 +61,7 @@ art_module_cc_defaults { }, } -art_module_cc_defaults { +cc_defaults { name: "art_test_defaults", defaults: [ "art_test_common_defaults", @@ -117,6 +110,12 @@ art_cc_defaults { name: "art_test_internal_library_defaults", defaults: ["art_test_defaults"], target: { + android: { + test_for: [ + "com.android.art", + "com.android.art.debug", + ], + }, android_arm: { relative_install_path: "com.android.art/lib", }, @@ -259,12 +258,10 @@ art_cc_defaults { // These really are gtests, but the gtest library comes from `libart(d)-gtest.so`. gtest: false, + header_libs: [ + "libnativeloader-headers", + ], shared_libs: [ - // `libnativeloader` must be shared, otherwise host gtests can't load libraries from - // "art_common/out/host", which is present in `libnativeloader` RUNPATH. - // TODO(b/247108425): modify gtests RUNPATH so that `libnativeloader` can be static linked. - "libnativeloader", - // `libart(d)` (`art/runtime/jni/java_vm_ext.cc`) and `libnativehelper` // (`libnativehelper/JniInvocation.c`) define symbols with the same name // (e.g. `JNI_GetDefaultJavaVMInitArgs`). @@ -286,6 +283,12 @@ art_cc_defaults { "heapprofd_client_api", ], }, + host: { + cflags: [ + "-fsanitize-address-use-after-return=never", + "-Wno-unused-command-line-argument", + ], + }, linux: { ldflags: [ // Allow jni_compiler_test to find Java_MyClassNatives_bar @@ -302,10 +305,17 @@ art_cc_defaults { "-Wno-missing-noreturn", ], }, - host: { - cflags: [ - "-fsanitize-address-use-after-return=never", - "-Wno-unused-command-line-argument", + // Library search path needed for running host tests in CI (from testcases directory). + linux_glibc_x86: { + ldflags: [ + "-Wl,-rpath,$ORIGIN/../../art_common/out/host/linux-x86/lib", + "-Wl,--enable-new-dtags", + ], + }, + linux_glibc_x86_64: { + ldflags: [ + "-Wl,-rpath,$ORIGIN/../../art_common/out/host/linux-x86/lib64", + "-Wl,--enable-new-dtags", ], }, }, @@ -371,10 +381,6 @@ art_cc_defaults { "libart-gtest", ], version_script: ":art-standalone-gtest-version", - test_for: [ - "com.android.art", - "com.android.art.debug", - ], } art_cc_defaults { @@ -1320,11 +1326,6 @@ java_library { art_cc_test { name: "ArtGtestsTargetInstallApex", - defaults: [ - // Enable only in source builds, where com.android.art.testing is - // available. - "art_module_source_build_defaults", - ], data: [ // We need the ART testing apex, which contains all gtest binaries. // Note that due to build system and linker constraints the gtests must be in the apex. @@ -1357,11 +1358,6 @@ art_cc_test { art_cc_test { name: "ArtGtestsTargetChroot", - defaults: [ - // Enable only in source builds, where com.android.art.testing is - // available. - "art_module_source_build_defaults", - ], data: [ // We need the ART testing apex, which contains all gtest binaries. // Note that due to build system and linker constraints the gtests must be in the apex. diff --git a/test/Android.run-test.bp b/test/Android.run-test.bp index d7305e6213..78b8c98b72 100644 --- a/test/Android.run-test.bp +++ b/test/Android.run-test.bp @@ -24,7 +24,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard00", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard00-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard00.zip", @@ -44,7 +43,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard01", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard01-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard01.zip", @@ -64,7 +62,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard02", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard02-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard02.zip", @@ -84,7 +81,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard03", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard03-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard03.zip", @@ -104,7 +100,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard04", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard04-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard04.zip", @@ -124,7 +119,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard05", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard05-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard05.zip", @@ -144,7 +138,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard06", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard06-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard06.zip", @@ -164,7 +157,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard07", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard07-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard07.zip", @@ -184,7 +176,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard08", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard08-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard08.zip", @@ -204,7 +195,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard09", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard09-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard09.zip", @@ -224,7 +214,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard10", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard10-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard10.zip", @@ -244,7 +233,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard11", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard11-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard11.zip", @@ -264,7 +252,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard12", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard12-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard12.zip", @@ -284,7 +271,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard13", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard13-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard13.zip", @@ -304,7 +290,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard14", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard14-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard14.zip", @@ -324,7 +309,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard15", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard15-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard15.zip", @@ -344,7 +328,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard16", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard16-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard16.zip", @@ -364,7 +347,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard17", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard17-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard17.zip", @@ -384,7 +366,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard18", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard18-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard18.zip", @@ -404,7 +385,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard19", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard19-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard19.zip", @@ -424,7 +404,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard20", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard20-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard20.zip", @@ -444,7 +423,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard21", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard21-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard21.zip", @@ -464,7 +442,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard22", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard22-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard22.zip", @@ -484,7 +461,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard23", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard23-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard23.zip", @@ -504,7 +480,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard24", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard24-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard24.zip", @@ -524,7 +499,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard25", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard25-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard25.zip", @@ -544,7 +518,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard26", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard26-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard26.zip", @@ -564,7 +537,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard27", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard27-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard27.zip", @@ -584,7 +556,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard28", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard28-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard28.zip", @@ -604,7 +575,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard29", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard29-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard29.zip", @@ -624,7 +594,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard30", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard30-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard30.zip", @@ -644,7 +613,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard31", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard31-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard31.zip", @@ -664,7 +632,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard32", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard32-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard32.zip", @@ -684,7 +651,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard33", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard33-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard33.zip", @@ -704,7 +670,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard34", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard34-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard34.zip", @@ -724,7 +689,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard35", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard35-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard35.zip", @@ -744,7 +708,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard36", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard36-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard36.zip", @@ -764,7 +727,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard37", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard37-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard37.zip", @@ -784,7 +746,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard38", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard38-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard38.zip", @@ -804,7 +765,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard39", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard39-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard39.zip", @@ -824,7 +784,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard40", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard40-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard40.zip", @@ -844,7 +803,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard41", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard41-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard41.zip", @@ -864,7 +822,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard42", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard42-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard42.zip", @@ -884,7 +841,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard43", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard43-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard43.zip", @@ -904,7 +860,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard44", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard44-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard44.zip", @@ -924,7 +879,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard45", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard45-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard45.zip", @@ -944,7 +898,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard46", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard46-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard46.zip", @@ -964,7 +917,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard47", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard47-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard47.zip", @@ -984,7 +936,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard48", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard48-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard48.zip", @@ -1004,7 +955,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard49", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard49-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard49.zip", @@ -1024,7 +974,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard50", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard50-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard50.zip", @@ -1044,7 +993,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard51", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard51-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard51.zip", @@ -1064,7 +1012,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard52", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard52-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard52.zip", @@ -1084,7 +1031,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard53", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard53-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard53.zip", @@ -1104,7 +1050,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard54", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard54-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard54.zip", @@ -1124,7 +1069,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard55", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard55-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard55.zip", @@ -1144,7 +1088,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard56", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard56-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard56.zip", @@ -1164,7 +1107,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard57", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard57-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard57.zip", @@ -1184,7 +1126,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard58", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard58-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard58.zip", @@ -1204,7 +1145,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard59", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard59-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard59.zip", @@ -1224,7 +1164,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard60", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard60-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard60.zip", @@ -1244,7 +1183,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard61", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard61-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard61.zip", @@ -1264,7 +1202,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard62", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard62-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard62.zip", @@ -1284,7 +1221,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard63", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard63-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard63.zip", @@ -1304,7 +1240,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard64", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard64-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard64.zip", @@ -1324,7 +1259,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard65", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard65-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard65.zip", @@ -1344,7 +1278,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard66", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard66-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard66.zip", @@ -1364,7 +1297,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard67", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard67-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard67.zip", @@ -1384,7 +1316,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard68", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard68-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard68.zip", @@ -1404,7 +1335,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard69", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard69-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard69.zip", @@ -1424,7 +1354,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard70", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard70-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard70.zip", @@ -1444,7 +1373,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard71", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard71-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard71.zip", @@ -1464,7 +1392,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard72", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard72-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard72.zip", @@ -1484,7 +1411,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard73", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard73-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard73.zip", @@ -1504,7 +1430,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard74", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard74-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard74.zip", @@ -1524,7 +1449,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard75", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard75-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard75.zip", @@ -1544,7 +1468,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard76", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard76-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard76.zip", @@ -1564,7 +1487,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard77", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard77-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard77.zip", @@ -1584,7 +1506,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard78", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard78-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard78.zip", @@ -1604,7 +1525,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard79", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard79-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard79.zip", @@ -1624,7 +1544,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard80", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard80-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard80.zip", @@ -1644,7 +1563,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard81", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard81-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard81.zip", @@ -1664,7 +1582,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard82", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard82-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard82.zip", @@ -1684,7 +1601,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard83", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard83-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard83.zip", @@ -1704,7 +1620,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard84", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard84-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard84.zip", @@ -1724,7 +1639,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard85", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard85-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard85.zip", @@ -1744,7 +1658,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard86", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard86-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard86.zip", @@ -1764,7 +1677,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard87", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard87-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard87.zip", @@ -1784,7 +1696,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard88", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard88-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard88.zip", @@ -1804,7 +1715,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard89", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard89-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard89.zip", @@ -1824,7 +1734,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard90", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard90-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard90.zip", @@ -1844,7 +1753,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard91", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard91-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard91.zip", @@ -1864,7 +1772,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard92", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard92-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard92.zip", @@ -1884,7 +1791,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard93", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard93-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard93.zip", @@ -1904,7 +1810,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard94", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard94-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard94.zip", @@ -1924,7 +1829,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard95", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard95-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard95.zip", @@ -1944,7 +1848,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard96", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard96-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard96.zip", @@ -1964,7 +1867,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard97", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard97-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard97.zip", @@ -1984,7 +1886,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard98", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard98-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard98.zip", @@ -2004,7 +1905,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shard99", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shard99-tmp", sub_dir: "art", filename: "art-run-test-host-data-shard99.zip", @@ -2025,7 +1925,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-shardHiddenApi", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-shardHiddenApi-tmp", sub_dir: "art", filename: "art-run-test-host-data-shardHiddenApi.zip", @@ -2033,11 +1932,6 @@ prebuilt_etc_host { genrule_defaults { name: "art-run-test-host-data-defaults", - defaults: [ - // Enable only in source builds, where com.android.art.testing is - // available. - "art_module_source_build_genrule_defaults", - ], tool_files: [ "run_test_build.py", ":art-run-test-bootclasspath", @@ -2071,7 +1965,6 @@ genrule_defaults { java_genrule { name: "art-run-test-host-data-merged-tmp", - defaults: ["art_module_source_build_genrule_defaults"], out: ["art-run-test-host-data-merged.zip"], srcs: [ ":art-run-test-host-data-shard00-tmp", @@ -2183,7 +2076,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-host-data-merged", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-merged-tmp", required: [ "art-run-test-host-data-shard00", @@ -2295,11 +2187,6 @@ prebuilt_etc_host { // Phony target used to build all shards java_genrule { name: "art-run-test-host-data-tmp", - defaults: [ - // Enable only in source builds, where com.android.art.testing is - // available. - "art_module_source_build_genrule_defaults", - ], out: ["art-run-test-host-data.txt"], srcs: [ ":art-run-test-host-data-shard00-tmp", @@ -2410,7 +2297,6 @@ java_genrule { // Phony target used to install all shards prebuilt_etc_host { name: "art-run-test-host-data", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-host-data-tmp", required: [ "art-run-test-host-data-shard00", @@ -2533,7 +2419,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard00", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard00-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard00.zip", @@ -2553,7 +2438,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard01", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard01-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard01.zip", @@ -2573,7 +2457,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard02", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard02-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard02.zip", @@ -2593,7 +2476,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard03", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard03-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard03.zip", @@ -2613,7 +2495,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard04", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard04-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard04.zip", @@ -2633,7 +2514,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard05", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard05-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard05.zip", @@ -2653,7 +2533,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard06", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard06-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard06.zip", @@ -2673,7 +2552,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard07", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard07-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard07.zip", @@ -2693,7 +2571,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard08", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard08-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard08.zip", @@ -2713,7 +2590,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard09", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard09-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard09.zip", @@ -2733,7 +2609,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard10", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard10-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard10.zip", @@ -2753,7 +2628,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard11", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard11-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard11.zip", @@ -2773,7 +2647,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard12", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard12-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard12.zip", @@ -2793,7 +2666,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard13", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard13-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard13.zip", @@ -2813,7 +2685,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard14", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard14-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard14.zip", @@ -2833,7 +2704,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard15", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard15-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard15.zip", @@ -2853,7 +2723,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard16", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard16-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard16.zip", @@ -2873,7 +2742,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard17", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard17-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard17.zip", @@ -2893,7 +2761,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard18", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard18-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard18.zip", @@ -2913,7 +2780,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard19", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard19-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard19.zip", @@ -2933,7 +2799,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard20", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard20-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard20.zip", @@ -2953,7 +2818,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard21", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard21-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard21.zip", @@ -2973,7 +2837,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard22", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard22-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard22.zip", @@ -2993,7 +2856,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard23", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard23-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard23.zip", @@ -3013,7 +2875,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard24", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard24-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard24.zip", @@ -3033,7 +2894,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard25", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard25-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard25.zip", @@ -3053,7 +2913,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard26", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard26-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard26.zip", @@ -3073,7 +2932,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard27", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard27-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard27.zip", @@ -3093,7 +2951,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard28", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard28-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard28.zip", @@ -3113,7 +2970,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard29", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard29-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard29.zip", @@ -3133,7 +2989,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard30", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard30-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard30.zip", @@ -3153,7 +3008,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard31", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard31-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard31.zip", @@ -3173,7 +3027,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard32", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard32-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard32.zip", @@ -3193,7 +3046,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard33", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard33-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard33.zip", @@ -3213,7 +3065,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard34", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard34-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard34.zip", @@ -3233,7 +3084,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard35", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard35-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard35.zip", @@ -3253,7 +3103,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard36", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard36-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard36.zip", @@ -3273,7 +3122,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard37", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard37-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard37.zip", @@ -3293,7 +3141,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard38", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard38-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard38.zip", @@ -3313,7 +3160,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard39", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard39-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard39.zip", @@ -3333,7 +3179,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard40", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard40-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard40.zip", @@ -3353,7 +3198,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard41", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard41-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard41.zip", @@ -3373,7 +3217,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard42", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard42-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard42.zip", @@ -3393,7 +3236,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard43", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard43-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard43.zip", @@ -3413,7 +3255,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard44", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard44-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard44.zip", @@ -3433,7 +3274,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard45", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard45-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard45.zip", @@ -3453,7 +3293,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard46", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard46-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard46.zip", @@ -3473,7 +3312,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard47", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard47-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard47.zip", @@ -3493,7 +3331,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard48", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard48-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard48.zip", @@ -3513,7 +3350,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard49", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard49-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard49.zip", @@ -3533,7 +3369,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard50", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard50-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard50.zip", @@ -3553,7 +3388,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard51", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard51-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard51.zip", @@ -3573,7 +3407,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard52", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard52-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard52.zip", @@ -3593,7 +3426,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard53", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard53-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard53.zip", @@ -3613,7 +3445,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard54", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard54-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard54.zip", @@ -3633,7 +3464,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard55", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard55-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard55.zip", @@ -3653,7 +3483,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard56", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard56-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard56.zip", @@ -3673,7 +3502,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard57", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard57-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard57.zip", @@ -3693,7 +3521,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard58", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard58-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard58.zip", @@ -3713,7 +3540,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard59", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard59-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard59.zip", @@ -3733,7 +3559,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard60", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard60-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard60.zip", @@ -3753,7 +3578,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard61", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard61-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard61.zip", @@ -3773,7 +3597,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard62", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard62-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard62.zip", @@ -3793,7 +3616,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard63", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard63-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard63.zip", @@ -3813,7 +3635,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard64", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard64-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard64.zip", @@ -3833,7 +3654,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard65", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard65-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard65.zip", @@ -3853,7 +3673,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard66", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard66-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard66.zip", @@ -3873,7 +3692,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard67", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard67-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard67.zip", @@ -3893,7 +3711,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard68", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard68-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard68.zip", @@ -3913,7 +3730,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard69", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard69-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard69.zip", @@ -3933,7 +3749,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard70", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard70-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard70.zip", @@ -3953,7 +3768,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard71", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard71-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard71.zip", @@ -3973,7 +3787,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard72", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard72-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard72.zip", @@ -3993,7 +3806,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard73", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard73-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard73.zip", @@ -4013,7 +3825,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard74", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard74-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard74.zip", @@ -4033,7 +3844,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard75", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard75-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard75.zip", @@ -4053,7 +3863,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard76", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard76-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard76.zip", @@ -4073,7 +3882,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard77", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard77-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard77.zip", @@ -4093,7 +3901,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard78", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard78-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard78.zip", @@ -4113,7 +3920,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard79", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard79-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard79.zip", @@ -4133,7 +3939,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard80", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard80-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard80.zip", @@ -4153,7 +3958,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard81", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard81-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard81.zip", @@ -4173,7 +3977,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard82", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard82-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard82.zip", @@ -4193,7 +3996,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard83", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard83-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard83.zip", @@ -4213,7 +4015,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard84", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard84-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard84.zip", @@ -4233,7 +4034,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard85", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard85-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard85.zip", @@ -4253,7 +4053,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard86", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard86-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard86.zip", @@ -4273,7 +4072,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard87", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard87-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard87.zip", @@ -4293,7 +4091,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard88", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard88-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard88.zip", @@ -4313,7 +4110,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard89", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard89-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard89.zip", @@ -4333,7 +4129,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard90", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard90-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard90.zip", @@ -4353,7 +4148,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard91", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard91-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard91.zip", @@ -4373,7 +4167,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard92", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard92-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard92.zip", @@ -4393,7 +4186,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard93", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard93-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard93.zip", @@ -4413,7 +4205,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard94", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard94-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard94.zip", @@ -4433,7 +4224,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard95", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard95-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard95.zip", @@ -4453,7 +4243,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard96", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard96-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard96.zip", @@ -4473,7 +4262,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard97", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard97-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard97.zip", @@ -4493,7 +4281,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard98", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard98-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard98.zip", @@ -4513,7 +4300,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shard99", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shard99-tmp", sub_dir: "art", filename: "art-run-test-target-data-shard99.zip", @@ -4534,7 +4320,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-shardHiddenApi", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-shardHiddenApi-tmp", sub_dir: "art", filename: "art-run-test-target-data-shardHiddenApi.zip", @@ -4542,11 +4327,6 @@ prebuilt_etc_host { genrule_defaults { name: "art-run-test-target-data-defaults", - defaults: [ - // Enable only in source builds, where com.android.art.testing is - // available. - "art_module_source_build_genrule_defaults", - ], tool_files: [ "run_test_build.py", ":art-run-test-bootclasspath", @@ -4580,7 +4360,6 @@ genrule_defaults { java_genrule { name: "art-run-test-target-data-merged-tmp", - defaults: ["art_module_source_build_genrule_defaults"], out: ["art-run-test-target-data-merged.zip"], srcs: [ ":art-run-test-target-data-shard00-tmp", @@ -4692,7 +4471,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-target-data-merged", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-merged-tmp", required: [ "art-run-test-target-data-shard00", @@ -4804,11 +4582,6 @@ prebuilt_etc_host { // Phony target used to build all shards java_genrule { name: "art-run-test-target-data-tmp", - defaults: [ - // Enable only in source builds, where com.android.art.testing is - // available. - "art_module_source_build_genrule_defaults", - ], out: ["art-run-test-target-data.txt"], srcs: [ ":art-run-test-target-data-shard00-tmp", @@ -4919,7 +4692,6 @@ java_genrule { // Phony target used to install all shards prebuilt_etc_host { name: "art-run-test-target-data", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-target-data-tmp", required: [ "art-run-test-target-data-shard00", @@ -5042,7 +4814,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard00", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard00-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard00.zip", @@ -5062,7 +4833,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard01", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard01-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard01.zip", @@ -5082,7 +4852,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard02", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard02-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard02.zip", @@ -5102,7 +4871,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard03", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard03-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard03.zip", @@ -5122,7 +4890,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard04", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard04-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard04.zip", @@ -5142,7 +4909,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard05", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard05-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard05.zip", @@ -5162,7 +4928,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard06", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard06-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard06.zip", @@ -5182,7 +4947,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard07", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard07-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard07.zip", @@ -5202,7 +4966,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard08", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard08-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard08.zip", @@ -5222,7 +4985,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard09", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard09-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard09.zip", @@ -5242,7 +5004,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard10", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard10-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard10.zip", @@ -5262,7 +5023,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard11", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard11-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard11.zip", @@ -5282,7 +5042,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard12", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard12-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard12.zip", @@ -5302,7 +5061,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard13", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard13-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard13.zip", @@ -5322,7 +5080,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard14", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard14-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard14.zip", @@ -5342,7 +5099,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard15", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard15-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard15.zip", @@ -5362,7 +5118,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard16", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard16-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard16.zip", @@ -5382,7 +5137,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard17", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard17-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard17.zip", @@ -5402,7 +5156,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard18", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard18-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard18.zip", @@ -5422,7 +5175,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard19", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard19-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard19.zip", @@ -5442,7 +5194,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard20", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard20-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard20.zip", @@ -5462,7 +5213,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard21", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard21-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard21.zip", @@ -5482,7 +5232,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard22", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard22-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard22.zip", @@ -5502,7 +5251,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard23", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard23-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard23.zip", @@ -5522,7 +5270,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard24", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard24-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard24.zip", @@ -5542,7 +5289,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard25", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard25-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard25.zip", @@ -5562,7 +5308,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard26", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard26-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard26.zip", @@ -5582,7 +5327,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard27", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard27-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard27.zip", @@ -5602,7 +5346,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard28", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard28-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard28.zip", @@ -5622,7 +5365,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard29", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard29-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard29.zip", @@ -5642,7 +5384,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard30", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard30-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard30.zip", @@ -5662,7 +5403,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard31", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard31-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard31.zip", @@ -5682,7 +5422,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard32", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard32-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard32.zip", @@ -5702,7 +5441,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard33", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard33-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard33.zip", @@ -5722,7 +5460,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard34", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard34-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard34.zip", @@ -5742,7 +5479,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard35", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard35-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard35.zip", @@ -5762,7 +5498,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard36", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard36-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard36.zip", @@ -5782,7 +5517,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard37", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard37-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard37.zip", @@ -5802,7 +5536,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard38", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard38-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard38.zip", @@ -5822,7 +5555,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard39", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard39-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard39.zip", @@ -5842,7 +5574,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard40", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard40-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard40.zip", @@ -5862,7 +5593,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard41", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard41-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard41.zip", @@ -5882,7 +5612,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard42", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard42-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard42.zip", @@ -5902,7 +5631,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard43", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard43-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard43.zip", @@ -5922,7 +5650,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard44", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard44-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard44.zip", @@ -5942,7 +5669,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard45", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard45-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard45.zip", @@ -5962,7 +5688,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard46", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard46-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard46.zip", @@ -5982,7 +5707,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard47", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard47-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard47.zip", @@ -6002,7 +5726,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard48", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard48-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard48.zip", @@ -6022,7 +5745,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard49", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard49-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard49.zip", @@ -6042,7 +5764,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard50", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard50-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard50.zip", @@ -6062,7 +5783,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard51", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard51-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard51.zip", @@ -6082,7 +5802,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard52", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard52-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard52.zip", @@ -6102,7 +5821,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard53", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard53-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard53.zip", @@ -6122,7 +5840,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard54", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard54-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard54.zip", @@ -6142,7 +5859,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard55", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard55-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard55.zip", @@ -6162,7 +5878,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard56", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard56-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard56.zip", @@ -6182,7 +5897,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard57", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard57-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard57.zip", @@ -6202,7 +5916,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard58", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard58-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard58.zip", @@ -6222,7 +5935,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard59", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard59-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard59.zip", @@ -6242,7 +5954,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard60", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard60-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard60.zip", @@ -6262,7 +5973,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard61", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard61-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard61.zip", @@ -6282,7 +5992,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard62", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard62-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard62.zip", @@ -6302,7 +6011,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard63", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard63-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard63.zip", @@ -6322,7 +6030,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard64", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard64-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard64.zip", @@ -6342,7 +6049,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard65", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard65-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard65.zip", @@ -6362,7 +6068,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard66", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard66-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard66.zip", @@ -6382,7 +6087,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard67", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard67-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard67.zip", @@ -6402,7 +6106,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard68", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard68-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard68.zip", @@ -6422,7 +6125,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard69", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard69-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard69.zip", @@ -6442,7 +6144,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard70", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard70-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard70.zip", @@ -6462,7 +6163,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard71", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard71-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard71.zip", @@ -6482,7 +6182,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard72", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard72-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard72.zip", @@ -6502,7 +6201,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard73", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard73-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard73.zip", @@ -6522,7 +6220,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard74", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard74-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard74.zip", @@ -6542,7 +6239,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard75", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard75-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard75.zip", @@ -6562,7 +6258,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard76", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard76-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard76.zip", @@ -6582,7 +6277,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard77", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard77-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard77.zip", @@ -6602,7 +6296,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard78", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard78-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard78.zip", @@ -6622,7 +6315,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard79", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard79-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard79.zip", @@ -6642,7 +6334,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard80", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard80-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard80.zip", @@ -6662,7 +6353,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard81", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard81-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard81.zip", @@ -6682,7 +6372,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard82", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard82-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard82.zip", @@ -6702,7 +6391,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard83", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard83-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard83.zip", @@ -6722,7 +6410,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard84", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard84-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard84.zip", @@ -6742,7 +6429,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard85", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard85-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard85.zip", @@ -6762,7 +6448,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard86", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard86-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard86.zip", @@ -6782,7 +6467,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard87", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard87-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard87.zip", @@ -6802,7 +6486,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard88", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard88-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard88.zip", @@ -6822,7 +6505,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard89", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard89-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard89.zip", @@ -6842,7 +6524,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard90", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard90-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard90.zip", @@ -6862,7 +6543,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard91", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard91-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard91.zip", @@ -6882,7 +6562,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard92", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard92-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard92.zip", @@ -6902,7 +6581,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard93", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard93-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard93.zip", @@ -6922,7 +6600,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard94", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard94-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard94.zip", @@ -6942,7 +6619,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard95", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard95-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard95.zip", @@ -6962,7 +6638,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard96", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard96-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard96.zip", @@ -6982,7 +6657,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard97", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard97-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard97.zip", @@ -7002,7 +6676,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard98", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard98-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard98.zip", @@ -7022,7 +6695,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shard99", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shard99-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shard99.zip", @@ -7043,7 +6715,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-shardHiddenApi", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-shardHiddenApi-tmp", sub_dir: "art", filename: "art-run-test-jvm-data-shardHiddenApi.zip", @@ -7051,11 +6722,6 @@ prebuilt_etc_host { genrule_defaults { name: "art-run-test-jvm-data-defaults", - defaults: [ - // Enable only in source builds, where com.android.art.testing is - // available. - "art_module_source_build_genrule_defaults", - ], tool_files: [ "run_test_build.py", ":art-run-test-bootclasspath", @@ -7089,7 +6755,6 @@ genrule_defaults { java_genrule { name: "art-run-test-jvm-data-merged-tmp", - defaults: ["art_module_source_build_genrule_defaults"], out: ["art-run-test-jvm-data-merged.zip"], srcs: [ ":art-run-test-jvm-data-shard00-tmp", @@ -7201,7 +6866,6 @@ java_genrule { // Install in the output directory to make it accessible for tests. prebuilt_etc_host { name: "art-run-test-jvm-data-merged", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-merged-tmp", required: [ "art-run-test-jvm-data-shard00", @@ -7313,11 +6977,6 @@ prebuilt_etc_host { // Phony target used to build all shards java_genrule { name: "art-run-test-jvm-data-tmp", - defaults: [ - // Enable only in source builds, where com.android.art.testing is - // available. - "art_module_source_build_genrule_defaults", - ], out: ["art-run-test-jvm-data.txt"], srcs: [ ":art-run-test-jvm-data-shard00-tmp", @@ -7428,7 +7087,6 @@ java_genrule { // Phony target used to install all shards prebuilt_etc_host { name: "art-run-test-jvm-data", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":art-run-test-jvm-data-tmp", required: [ "art-run-test-jvm-data-shard00", diff --git a/test/Android.run-test.bp.py b/test/Android.run-test.bp.py index 74745b04ac..2f1dfb8e2a 100755 --- a/test/Android.run-test.bp.py +++ b/test/Android.run-test.bp.py @@ -61,7 +61,6 @@ def main(): // Install in the output directory to make it accessible for tests. prebuilt_etc_host {{ name: "{name}", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":{name}-tmp", sub_dir: "art", filename: "{name}.zip", @@ -89,7 +88,6 @@ def main(): // Install in the output directory to make it accessible for tests. prebuilt_etc_host {{ name: "{name}", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":{name}-tmp", sub_dir: "art", filename: "{name}.zip", @@ -99,11 +97,6 @@ def main(): f.write(textwrap.dedent(f""" genrule_defaults {{ name: "art-run-test-{mode}-data-defaults", - defaults: [ - // Enable only in source builds, where com.android.art.testing is - // available. - "art_module_source_build_genrule_defaults", - ], tool_files: [ "run_test_build.py", ":art-run-test-bootclasspath", @@ -142,7 +135,6 @@ def main(): f.write(textwrap.dedent(f""" java_genrule {{ name: "{name}-tmp", - defaults: ["art_module_source_build_genrule_defaults"], out: ["{name}.zip"], srcs: [ {srcs} @@ -154,7 +146,6 @@ def main(): // Install in the output directory to make it accessible for tests. prebuilt_etc_host {{ name: "{name}", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":{name}-tmp", required: [ {deps} @@ -171,11 +162,6 @@ def main(): // Phony target used to build all shards java_genrule {{ name: "{name}-tmp", - defaults: [ - // Enable only in source builds, where com.android.art.testing is - // available. - "art_module_source_build_genrule_defaults", - ], out: ["{name}.txt"], srcs: [ {srcs} @@ -186,7 +172,6 @@ def main(): // Phony target used to install all shards prebuilt_etc_host {{ name: "{name}", - defaults: ["art_module_source_build_prebuilt_defaults"], src: ":{name}-tmp", required: [ {deps} diff --git a/test/jvmti-common/Trace.java b/test/jvmti-common/Trace.java index 8999bb1368..3df1737c91 100644 --- a/test/jvmti-common/Trace.java +++ b/test/jvmti-common/Trace.java @@ -28,6 +28,7 @@ public class Trace { Method singleStep, Thread thr); public static native void disableTracing(Thread thr); + public static native void nativeEnableFramePopEvents(); public static void enableFieldTracing(Class<?> methodClass, Method fieldAccess, @@ -43,6 +44,10 @@ public class Trace { enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr); } + public static void enableFramePopEvents() { + nativeEnableFramePopEvents(); + } + public static void enableSingleStepTracing(Class<?> methodClass, Method singleStep, Thread thr) { diff --git a/test/knownfailures.json b/test/knownfailures.json index 89b5196d84..4e5a19d0eb 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -1178,6 +1178,7 @@ "846-multidex-data-image", "847-filled-new-aray", "848-pattern-match", + "854-image-inlining", "999-redefine-hiddenapi", "1000-non-moving-space-stress", "1001-app-image-regions", diff --git a/test/odsign/Android.bp b/test/odsign/Android.bp index 59101706d8..ca1da53323 100644 --- a/test/odsign/Android.bp +++ b/test/odsign/Android.bp @@ -19,7 +19,6 @@ package { java_defaults { name: "odsign_e2e_tests_defaults", - defaults: ["art_module_source_build_java_defaults"], srcs: ["test-src/**/*.java"], libs: ["tradefed"], static_libs: [ @@ -77,7 +76,6 @@ cc_library_shared { android_test_helper_app { name: "odsign_e2e_test_app", - defaults: ["art_module_source_build_java_defaults"], manifest: "AndroidManifest.xml", srcs: ["src/**/*.java"], jni_libs: [ diff --git a/test/ti-agent/trace_helper.cc b/test/ti-agent/trace_helper.cc index 58958cb268..e34b5bdce9 100644 --- a/test/ti-agent/trace_helper.cc +++ b/test/ti-agent/trace_helper.cc @@ -466,6 +466,15 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldAccess( env->DeleteLocalRef(klass); } +extern "C" JNIEXPORT void JNICALL Java_art_Trace_nativeEnableFramePopEvents(JNIEnv* env) { + jvmtiCapabilities caps; + memset(&caps, 0, sizeof(caps)); + caps.can_pop_frame = 1; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) { + return; + } +} + extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing2( JNIEnv* env, [[maybe_unused]] jclass trace, diff --git a/test/update-rollback/Android.bp b/test/update-rollback/Android.bp index ad14988f2f..8bba8cfd24 100644 --- a/test/update-rollback/Android.bp +++ b/test/update-rollback/Android.bp @@ -18,7 +18,6 @@ package { java_test_host { name: "art-apex-update-rollback", - defaults: ["art_module_source_build_java_defaults"], srcs: ["src/**/*.java"], libs: ["tradefed"], static_libs: ["cts-install-lib-host"], diff --git a/test/utils/regen-test-files b/test/utils/regen-test-files index 6f30c50a9a..cedf354882 100755 --- a/test/utils/regen-test-files +++ b/test/utils/regen-test-files @@ -253,6 +253,7 @@ art_gtest_user_module_names = [ "art_standalone_odrefresh_tests", "art_standalone_runtime_tests", "art_standalone_sigchain_tests", + "libnativebridge-tests", "libnativeloader_test", ] @@ -272,6 +273,7 @@ art_gtest_module_names = sorted(art_gtest_user_module_names + art_gtest_eng_only # removing them from this set (in order to promote them to # presubmits). art_gtest_postsubmit_only_module_names = [ + "libnativebridge-tests", ] # ART gtests supported in MTS that do not need root access to the device. diff --git a/tools/Android.bp b/tools/Android.bp index 6be8c0d14f..492749d8ba 100644 --- a/tools/Android.bp +++ b/tools/Android.bp @@ -24,13 +24,6 @@ package { default_team: "trendy_team_art_performance", } -soong_config_module_type_import { - from: "art/build/SoongConfig.bp", - module_types: [ - "art_module_cc_genrule", - ], -} - python_binary_host { name: "generate_operator_out", srcs: [ @@ -38,13 +31,6 @@ python_binary_host { ], } -soong_config_module_type_import { - from: "art/build/SoongConfig.bp", - module_types: [ - "art_module_sh_binary", - ], -} - cc_binary { name: "art_boot", defaults: ["art_defaults"], @@ -57,7 +43,7 @@ cc_binary { } // Copy the art shell script to the host and target's bin directory -art_module_sh_binary { +sh_binary { name: "art-script", host_supported: true, @@ -106,7 +92,7 @@ sh_binary { }, } -art_module_cc_genrule { +cc_genrule { name: "check_cfi", tool_files: [ diff --git a/tools/boot-image-profile-generate.sh b/tools/boot-image-profile-generate.sh index 20d0421139..2d3e87d5a3 100755 --- a/tools/boot-image-profile-generate.sh +++ b/tools/boot-image-profile-generate.sh @@ -79,7 +79,6 @@ cd "$ANDROID_BUILD_TOP" echo "Unziping boot.zip" BOOT_UNZIP_DIR="$WORK_DIR"/boot-dex -ART_JARS="$BOOT_UNZIP_DIR"/dex_artjars_input BOOT_JARS="$BOOT_UNZIP_DIR"/dex_bootjars_input SYSTEM_SERVER_JAR="$BOOT_UNZIP_DIR"/system/framework/services.jar @@ -87,10 +86,6 @@ unzip -o "$BOOT_ZIP" -d "$BOOT_UNZIP_DIR" echo "Processing boot image jar files" jar_args=() -for entry in "$ART_JARS"/* -do - jar_args+=("--apk=$entry") -done for entry in "$BOOT_JARS"/* do jar_args+=("--apk=$entry") diff --git a/tools/checker/README b/tools/checker/README index e01f0d0750..a87a72be77 100644 --- a/tools/checker/README +++ b/tools/checker/README @@ -96,12 +96,12 @@ CHECK-IF and CHECK-ELIF take a Python expression as input that will be evaluated A possible use case of branching is to check whether the generated code exploits the instruction architecture features enabled at compile time. For that purpose, you can call the custom made -function isaHasFeature("feature_name"). +function hasIsaFeature("feature_name"). Example: /// CHECK-START-ARM64: int other.TestByte.testDotProdComplex(byte[], byte[]) disassembly (after) /// CHECK: VecDotProd - /// CHECK-IF: isaHasFeature("dotprod") + /// CHECK-IF: hasIsaFeature("dotprod") /// CHECK: sdot /// CHECK-ELSE: /// CHECK-NOT: sdot diff --git a/tools/cpp-define-generator/Android.bp b/tools/cpp-define-generator/Android.bp index e374ed8857..babcfa43fe 100644 --- a/tools/cpp-define-generator/Android.bp +++ b/tools/cpp-define-generator/Android.bp @@ -44,15 +44,8 @@ cc_object { ], } -soong_config_module_type_import { - from: "art/build/SoongConfig.bp", - module_types: [ - "art_module_cc_genrule", - ], -} - // This extracts the compile-time constants from asm_defines.s and creates the header. -art_module_cc_genrule { +cc_genrule { name: "cpp-define-generator-asm-support", host_supported: true, device_supported: true, diff --git a/tools/fuzzer/Android.bp b/tools/fuzzer/Android.bp index ae073a1afa..dff2847e8a 100644 --- a/tools/fuzzer/Android.bp +++ b/tools/fuzzer/Android.bp @@ -27,11 +27,6 @@ cc_defaults { name: "libart_verify_dex_fuzzer-defaults", srcs: ["libart_verify_dex_fuzzer.cc"], - defaults: [ - // To allow the ART module to build correctly. - "art_module_source_build_defaults", - ], - // Build and run on x86 too. host_supported: true, @@ -91,10 +86,6 @@ cc_fuzz { // TODO(b/171429704): Remove this genrule and use the zip directly. genrule { name: "art_runtest_corpus", - defaults: [ - // To allow the ART module to build correctly. - "art_module_source_build_defaults", - ], tool_files: ["create_corpus.py"], cmd: "$(location) $(genDir) $(in)", srcs: [ @@ -1280,7 +1271,7 @@ genrule { // Zip the corpus folder. To get the folder, we grab the first file // from `in` and use its directory. cmd: "FILES=($(in)) &&" + - "$(location soong_zip) -j -L 0 -o $(out) -D $$(dirname $${FILES[0]})", + "$(location soong_zip) -j -L 0 -o $(out) -D $$(dirname $${FILES[0]})", srcs: ["corpus/*"], out: ["fuzzer_corpus.zip"], tools: ["soong_zip"], |