summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-05-23 15:41:01 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-05-23 15:41:01 +0000
commit0fe83a81a66492cb8c895bd19a4cb36def38212d (patch)
treea0459ea58fc12779b5ff4c362e138e7595394921
parent86c80efbb0c0c347f4feb0a0d35cc2df0ddf16c5 (diff)
parentd11c004a5df78a67e6e9021492a8e096de336db3 (diff)
downloadart-build-tools-release.tar.gz
Snap for 11880863 from d11c004a5df78a67e6e9021492a8e096de336db3 to build-tools-releasebuild-tools-release
Change-Id: I158c0b997bd9cc8e123afcccd47514c82810ca61
-rw-r--r--PREUPLOAD.cfg4
-rw-r--r--TEST_MAPPING3
-rw-r--r--artd/Android.bp1
-rw-r--r--artd/artd.cc52
-rw-r--r--artd/artd.h3
-rw-r--r--artd/artd_test.cc271
-rw-r--r--artd/binder/com/android/server/art/IArtd.aidl3
-rw-r--r--artd/file_utils.cc5
-rw-r--r--artd/path_utils.cc16
-rw-r--r--artd/path_utils.h2
-rw-r--r--build/Android.bp73
-rw-r--r--build/SoongConfig.bp89
-rw-r--r--build/apex/Android.bp15
-rw-r--r--build/apex/art.rc7
-rw-r--r--build/sdk/Android.bp14
-rw-r--r--compiler/driver/compiler_options.cc4
-rw-r--r--compiler/driver/compiler_options.h2
-rw-r--r--compiler/driver/compiler_options_map.h2
-rw-r--r--compiler/linker/linker_patch.h13
-rw-r--r--compiler/optimizing/code_generator.h18
-rw-r--r--compiler/optimizing/code_generator_arm64.cc39
-rw-r--r--compiler/optimizing/code_generator_arm64.h10
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.cc32
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.h3
-rw-r--r--compiler/optimizing/code_generator_riscv64.cc94
-rw-r--r--compiler/optimizing/code_generator_riscv64.h6
-rw-r--r--compiler/optimizing/code_generator_x86.cc32
-rw-r--r--compiler/optimizing/code_generator_x86.h3
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc29
-rw-r--r--compiler/optimizing/code_generator_x86_64.h3
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc41
-rw-r--r--compiler/optimizing/intrinsics_arm_vixl.cc40
-rw-r--r--compiler/optimizing/intrinsics_riscv64.cc224
-rw-r--r--compiler/optimizing/intrinsics_x86.cc43
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc41
-rw-r--r--compiler/optimizing/nodes.h7
-rw-r--r--compiler/optimizing/sharpening.cc2
-rw-r--r--dex2oat/dex2oat.cc4
-rw-r--r--dex2oat/driver/compiler_driver.cc64
-rw-r--r--dex2oat/linker/arm64/relative_patcher_arm64.cc2
-rw-r--r--dex2oat/linker/elf_writer.h1
-rw-r--r--dex2oat/linker/elf_writer_quick.cc3
-rw-r--r--dex2oat/linker/image_test.h1
-rw-r--r--dex2oat/linker/image_writer.cc12
-rw-r--r--dex2oat/linker/image_writer.h9
-rw-r--r--dex2oat/linker/oat_writer.cc54
-rw-r--r--dex2oat/linker/oat_writer.h21
-rw-r--r--dex2oat/linker/oat_writer_test.cc1
-rw-r--r--dex2oat/verifier_deps_test.cc14
-rw-r--r--dexopt_chroot_setup/Android.bp1
-rw-r--r--dexopt_chroot_setup/dexopt_chroot_setup.cc49
-rw-r--r--dexopt_chroot_setup/dexopt_chroot_setup_test.cc2
-rw-r--r--libartbase/base/arena_allocator.cc2
-rw-r--r--libartbase/base/memory_tool.h2
-rw-r--r--libartbase/base/unix_file/fd_file_test.cc4
-rw-r--r--libartservice/service/Android.bp6
-rw-r--r--libartservice/service/java/com/android/server/art/ArtFileManager.java6
-rw-r--r--libartservice/service/java/com/android/server/art/ArtJni.java1
-rw-r--r--libartservice/service/java/com/android/server/art/ArtManagerLocal.java45
-rw-r--r--libartservice/service/java/com/android/server/art/ArtShellCommand.java79
-rw-r--r--libartservice/service/java/com/android/server/art/ArtdRefCache.java1
-rw-r--r--libartservice/service/java/com/android/server/art/AsLog.java89
-rw-r--r--libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java20
-rw-r--r--libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java18
-rw-r--r--libartservice/service/java/com/android/server/art/DexMetadataHelper.java5
-rw-r--r--libartservice/service/java/com/android/server/art/DexUseManagerLocal.java19
-rw-r--r--libartservice/service/java/com/android/server/art/DexoptHelper.java2
-rw-r--r--libartservice/service/java/com/android/server/art/Dexopter.java99
-rw-r--r--libartservice/service/java/com/android/server/art/DumpHelper.java7
-rw-r--r--libartservice/service/java/com/android/server/art/PreRebootDexoptJob.java171
-rw-r--r--libartservice/service/java/com/android/server/art/PrimaryDexopter.java9
-rw-r--r--libartservice/service/java/com/android/server/art/SecondaryDexopter.java6
-rw-r--r--libartservice/service/java/com/android/server/art/Utils.java17
-rw-r--r--libartservice/service/java/com/android/server/art/model/ArtFlags.java10
-rw-r--r--libartservice/service/java/com/android/server/art/prereboot/PreRebootDriver.java15
-rw-r--r--libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java18
-rw-r--r--libartservice/service/javatests/com/android/server/art/DumpHelperTest.java51
-rw-r--r--libartservice/service/javatests/com/android/server/art/PreRebootDexoptJobTest.java263
-rw-r--r--libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java44
-rw-r--r--libarttools/art_exec.cc2
-rw-r--r--libdexfile/Android.bp7
-rw-r--r--libdexfile/dex/dex_file-inl.h84
-rw-r--r--libdexfile/dex/dex_file.cc12
-rw-r--r--libdexfile/dex/dex_file.h33
-rw-r--r--libdexfile/dex/dex_file_loader.h2
-rw-r--r--libdexfile/dex/fuzzer_corpus_test.cc2
-rw-r--r--libdexfile/dex/method_reference.h11
-rw-r--r--libdexfile/dex/modifiers.h2
-rw-r--r--libdexfile/dex/proto_reference.h4
-rw-r--r--libdexfile/dex/signature-inl.h4
-rw-r--r--libdexfile/dex/type_lookup_table.cc2
-rw-r--r--libelffile/elf/elf_builder.h15
-rw-r--r--libnativebridge/tests/Android.bp40
-rw-r--r--libnativebridge/tests/CodeCacheCreate_test.cpp10
-rw-r--r--libnativebridge/tests/CodeCacheExists_test.cpp10
-rw-r--r--libnativebridge/tests/CodeCacheStatFail_test.cpp40
-rw-r--r--libnativebridge/tests/CompleteFlow_test.cpp4
-rw-r--r--libnativebridge/tests/NativeBridge2Signal_test.cpp4
-rw-r--r--libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp4
-rw-r--r--libnativebridge/tests/NativeBridge3GetError_test.cpp4
-rw-r--r--libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp4
-rw-r--r--libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp4
-rw-r--r--libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp4
-rw-r--r--libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp4
-rw-r--r--libnativebridge/tests/NativeBridge6PreZygoteFork_test.cpp2
-rw-r--r--libnativebridge/tests/NativeBridge7CriticalNative_test.cpp2
-rw-r--r--libnativebridge/tests/NativeBridgeTest.cpp26
-rw-r--r--libnativebridge/tests/NativeBridgeTest.h24
-rwxr-xr-xlibnativebridge/tests/preupload_check_test_tag.sh21
-rw-r--r--libnativeloader/Android.bp14
-rw-r--r--libnativeloader/native_loader.cpp4
-rw-r--r--libnativeloader/public_libraries.cpp16
-rw-r--r--libnativeloader/test/Android.bp3
-rw-r--r--libprofile/profile/profile_compilation_info.cc13
-rw-r--r--libprofile/profile/profile_compilation_info.h14
-rw-r--r--libprofile/profile/profile_compilation_info_test.cc2
-rw-r--r--oatdump/oatdump.cc1
-rw-r--r--openjdkjvmti/ti_redefine.cc9
-rw-r--r--profman/profman.cc7
-rw-r--r--runtime/Android.bp10
-rw-r--r--runtime/arch/arm/quick_entrypoints_arm.S2
-rw-r--r--runtime/art_field-inl.h53
-rw-r--r--runtime/art_field.cc1
-rw-r--r--runtime/art_field.h5
-rw-r--r--runtime/art_method-inl.h23
-rw-r--r--runtime/art_method.cc31
-rw-r--r--runtime/art_method.h2
-rw-r--r--runtime/class_linker.cc170
-rw-r--r--runtime/class_linker.h78
-rw-r--r--runtime/class_table-inl.h18
-rw-r--r--runtime/class_table.cc28
-rw-r--r--runtime/class_table.h4
-rw-r--r--runtime/common_dex_operations.h4
-rw-r--r--runtime/common_runtime_test.cc20
-rw-r--r--runtime/common_runtime_test.h5
-rw-r--r--runtime/common_throws.h3
-rw-r--r--runtime/common_transaction_test.cc50
-rw-r--r--runtime/common_transaction_test.h39
-rw-r--r--runtime/gc/collector/concurrent_copying.cc2
-rw-r--r--runtime/gc/collector/mark_compact-inl.h28
-rw-r--r--runtime/gc/collector/mark_compact.cc539
-rw-r--r--runtime/gc/collector/mark_compact.h40
-rw-r--r--runtime/gc/heap.cc2
-rw-r--r--runtime/gc/space/image_space.cc12
-rw-r--r--runtime/hidden_api.cc9
-rw-r--r--runtime/hidden_api_test.cc76
-rw-r--r--runtime/intern_table.cc7
-rw-r--r--runtime/interpreter/interpreter.cc2
-rw-r--r--runtime/interpreter/interpreter_common.cc91
-rw-r--r--runtime/interpreter/interpreter_common.h263
-rw-r--r--runtime/interpreter/interpreter_switch_impl-inl.h329
-rw-r--r--runtime/interpreter/interpreter_switch_impl0.cc135
-rw-r--r--runtime/interpreter/interpreter_switch_impl1.cc173
-rw-r--r--runtime/interpreter/unstarted_runtime.cc113
-rw-r--r--runtime/interpreter/unstarted_runtime.h2
-rw-r--r--runtime/interpreter/unstarted_runtime_test.cc359
-rw-r--r--runtime/interpreter/unstarted_runtime_test.h125
-rw-r--r--runtime/interpreter/unstarted_runtime_transaction_test.cc236
-rw-r--r--runtime/jit/jit.cc8
-rw-r--r--runtime/jit/jit_code_cache.cc239
-rw-r--r--runtime/jit/jit_code_cache.h1
-rw-r--r--runtime/jit/profile_saver.cc4
-rw-r--r--runtime/jni/java_vm_ext.cc2
-rw-r--r--runtime/mirror/array-inl.h3
-rw-r--r--runtime/mirror/class-inl.h7
-rw-r--r--runtime/mirror/class.cc80
-rw-r--r--runtime/mirror/class.h6
-rw-r--r--runtime/mirror/dex_cache-inl.h4
-rw-r--r--runtime/mirror/dex_cache.h2
-rw-r--r--runtime/mirror/object-inl.h102
-rw-r--r--runtime/mirror/object-readbarrier-inl.h11
-rw-r--r--runtime/native/java_lang_reflect_Executable.cc17
-rw-r--r--runtime/native_gc_triggering.md87
-rw-r--r--runtime/oat/aot_class_linker.cc333
-rw-r--r--runtime/oat/aot_class_linker.h105
-rw-r--r--runtime/oat/elf_file.cc18
-rw-r--r--runtime/oat/elf_file.h2
-rw-r--r--runtime/oat/elf_file_impl.h3
-rw-r--r--runtime/oat/oat.h4
-rw-r--r--runtime/oat/oat_file.cc53
-rw-r--r--runtime/oat/oat_file.h25
-rw-r--r--runtime/oat/oat_file_manager.cc2
-rw-r--r--runtime/proxy_test.cc2
-rw-r--r--runtime/quick_exception_handler.cc33
-rw-r--r--runtime/reflection.cc3
-rw-r--r--runtime/runtime.cc242
-rw-r--r--runtime/runtime.h89
-rw-r--r--runtime/runtime_options.def3
-rw-r--r--runtime/runtime_options.h4
-rw-r--r--runtime/runtime_test.cc34
-rw-r--r--runtime/sdk_checker.cc23
-rw-r--r--runtime/sdk_checker.h2
-rw-r--r--runtime/thread.cc2
-rw-r--r--runtime/thread.h2
-rw-r--r--runtime/thread_list.cc18
-rw-r--r--runtime/thread_pool.cc18
-rw-r--r--runtime/transaction.cc20
-rw-r--r--runtime/transaction.h2
-rw-r--r--runtime/transaction_test.cc60
-rw-r--r--runtime/verifier/method_verifier.cc56
-rw-r--r--runtime/verifier/reg_type-inl.h4
-rw-r--r--runtime/verifier/reg_type_cache.cc42
-rw-r--r--runtime/verifier/reg_type_cache.h13
-rw-r--r--runtime/verifier/reg_type_test.cc71
-rw-r--r--runtime/verifier/verifier_deps.cc65
-rw-r--r--runtime/verifier/verifier_deps.h32
-rw-r--r--test/2256-checker-vector-replacement/src/Main.java32
-rw-r--r--test/536-checker-intrinsic-optimization/src/Main.java44
-rw-r--r--test/663-checker-select-generator/src/Main.java15
-rw-r--r--test/667-jit-jni-stub/jit_jni_stub_test.cc8
-rw-r--r--test/732-checker-app-image/expected-stdout.txt3
-rw-r--r--test/732-checker-app-image/profile5
-rw-r--r--test/732-checker-app-image/run.py11
-rw-r--r--test/732-checker-app-image/src-ex/Secondary.java51
-rw-r--r--test/732-checker-app-image/src/Main.java39
-rw-r--r--test/732-checker-app-image/src/PrimaryInterface.java17
-rw-r--r--test/854-image-inlining/expected-stderr.txt0
-rw-r--r--test/854-image-inlining/expected-stdout.txt0
-rw-r--r--test/854-image-inlining/info.txt4
-rw-r--r--test/854-image-inlining/profile7
-rw-r--r--test/854-image-inlining/run.py20
-rw-r--r--test/854-image-inlining/src-ex/Test.java43
-rw-r--r--test/854-image-inlining/src/Itf.java18
-rw-r--r--test/854-image-inlining/src/Main.java40
-rw-r--r--test/989-method-trace-throw/expected-stdout.txt222
-rw-r--r--test/989-method-trace-throw/method_trace.cc6
-rw-r--r--test/989-method-trace-throw/src/art/Test989.java89
-rw-r--r--test/Android.bp60
-rw-r--r--test/Android.run-test.bp342
-rwxr-xr-xtest/Android.run-test.bp.py15
-rw-r--r--test/jvmti-common/Trace.java5
-rw-r--r--test/knownfailures.json1
-rw-r--r--test/odsign/Android.bp2
-rw-r--r--test/ti-agent/trace_helper.cc9
-rw-r--r--test/update-rollback/Android.bp1
-rwxr-xr-xtest/utils/regen-test-files2
-rw-r--r--tools/Android.bp18
-rwxr-xr-xtools/boot-image-profile-generate.sh5
-rw-r--r--tools/checker/README4
-rw-r--r--tools/cpp-define-generator/Android.bp9
-rw-r--r--tools/fuzzer/Android.bp11
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_ = &reg_types_.FromDescriptor(class_loader_.Get(), descriptor, false);
+ declaring_class_ = &reg_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 = &reg_types_.FromDescriptor(class_loader_.Get(), descriptor, false);
+ return_type = &reg_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 = &reg_types_.FromDescriptor(class_loader_.Get(),
- return_type_descriptor,
- false);
+ return_type = &reg_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 = &reg_types_.FromDescriptor(class_loader_.Get(), descriptor, false);
+ result = &reg_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 = &reg_types_.FromDescriptor(
- class_loader_.Get(),
- dex_file_->GetTypeDescriptor(class_idx),
- false);
+ res_method_class =
+ &reg_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 = &reg_types_.FromDescriptor(class_loader_.Get(),
- field_class_descriptor,
- false);
+ const RegType* field_class_type =
+ &reg_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 = &reg_types_.FromDescriptor(class_loader_.Get(), descriptor, false);
+ field_type = &reg_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_ = &reg_types_.FromDescriptor(class_loader_.Get(), descriptor, false);
+ return_type_ = &reg_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"],